diff --git a/.gitignore b/.gitignore
index cc2524aa3..69b5fb54d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,9 @@ __pycache__/
.Python
build/
develop-eggs/
-dist/
+**/dist/*
+!**/dist/assets
+!**/dist/index.html
downloads/
eggs/
.eggs/
@@ -158,9 +160,11 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+.idea
.vscode
.DS_Store
figs
frontend
g_push.sh
+raw/
\ No newline at end of file
diff --git a/README.md b/README.md
index 6febecdcb..df21c24e6 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@
- 🛠 **Tools (Plugins) Utilization**: AgentVerse supports the multi-agent environments with tools. Currently, AgentVerse supports tools provided in [BMTools](https://github.com/OpenBMB/BMTools).
-- 🤖 **Supports a Wide Range of LLMs**: Our framework is built on top of [LangChain](https://github.com/hwchase17/langchain), which supports a wide range of different large language models. You can also easily customize your own LLM by referring to the [LangChain documentation](https://python.langchain.com/en/latest/modules/models/llms/examples/custom_llm.html).
+- 🤖 **Supports a Wide Range of LLMs**: You can easily customize your own LLM by implementing a new LLM by inheriting and extending our BaseLLM class (tutorial coming soon).
### 🗓 Coming Soon
@@ -119,7 +119,7 @@ By abstracting the environment into these five components, we have created a hig
### Agent
-Another fundamental component is the agent. The agent in our framework is built on LangChain. Thanks for the wide variety of LLMs supported in LangChain, our framework can takes in different LLM as the backend of the agent.
+Another fundamental component is the agent. Currently we provide two types of agents: **ConversationAgent** and **ToolAgent**. You can also customize your own agent by inheriting BaseAgent class (tutorial coming soon).
@@ -145,7 +145,7 @@ First, we need to create a task directory and write our configuration file for t
```yaml
# config.yaml
environment:
- env_type: base # Use the basic environment provided in AgentVerse
+ env_type: basic # Use the basic environment provided in AgentVerse
max_turns: 10 # Specify the maximum number of dialogue turns
rule:
order:
@@ -153,11 +153,11 @@ environment:
visibility:
type: all # Each message can be seen by all agents
selector:
- type: base # Basic selector (do not select)
+ type: basic # Basic selector (do not select)
updater:
- type: base # Basic updater (update the message to all agents)
+ type: basic # Basic updater (update the message to all agents)
describer:
- type: base # Basic describer (no description)
+ type: basic # Basic describer (no description)
```
This configuration specifies that we will use the basic environment provided in AgentVerse, with a maximum of 10 dialogue turns. We'll use the sequential order, with all messages visible to all agents. We won't be using any selectors, our updater will update the messages to all the agents and our describer will provide no description.
@@ -170,25 +170,24 @@ Next, we'll configure the agents. In the `config.yaml` file, we'll add the confi
# config.yaml
agents:
-
- agent_type: chat
+ agent_type: conversation
name: Professor Micheal # Name of the agent
role_description: You are Prof. Micheal, ... # Description of the agent
memory:
- memory_type: chat_message_history # Will store all the chat history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
+ memory_type: chat_history # Will store all the chat history
+ prompt_template: *professor_prompt
llm:
- llm_type: text-davinci-003
+ llm_type: text-davinci-003 # Will use OpenAICompletion LLM
+ model: text-davinci-003 # The argument passed to the api call
temperature: 0.7
max_tokens: 250
```
-In this example, we'll use the `chat` agent type. We've given the agent a name and a description, and we'll store the chat history in memory. We've also provided a pr ompt for the agent, divided into three parts: `prefix_prompt`, `format_prompt`, and `suffix_prompt`. These will be concatenated to form the final prompt given to the agent.
+In this example, we'll use the `conversation` agent type. We've given the agent a name and a description, and we'll store the chat history in memory. We've also provided a prompt template with placeholders marked as ${placeholder}. These will be instantiated by the `_fill_prompt_template` method of the agent.
##### 3. Writing an Output Parser
-The next step is to write a simple parser for your agent's response. Because you have specified the output format in `format_prompt`, you need to provide a corresponding parser. In this example, we let the model to output in the following format
+The next step is to write a simple parser for your agent's response. Because you may have specified the output format in your prompt template, you need to provide a corresponding parser. In this example, we inform the model to output in the following format in our prompt template
```
Action: Speak
@@ -201,11 +200,11 @@ With these steps, we've successfully built a simple classroom environment and cu
### Customization Guide for More Complex Environments
-While we provide a basic framework for building environments with our five rule components, more complex environments may require further customization. Here are some steps you can take to customize your environment:
+While we provide a basic framework for building environments with our five rule components, more complex environments may require further customization. A detailed documentation and tutorial is coming soon. Here we briefly introduce some steps you can take to customize your environment:
1. **Customize the five rule components**. Each rule component has an interface, allowing you to customize its behavior to suit your specific needs. It's important to note that these components are not necessarily independent and can interact through the `rule_params` dictionary in the environment. You can create your own rule components and integrate them with the existing ones to build more complex interactions between agents.
-2. **Customize the environment itself**. Our `base` environment provides a default execution order for the five rule components that is suitable for most cases, but you can inherit the `BaseEnvironment` class and write your own `run` method to implement a more sophisticated execution order.
-3. **Customize the agent**. Depending on your specific use case, you may also need to customize the `Agent` class. For example, you may want to use your local LLM as your agents or create agents with specialized knowledge or skills.
+2. **Customize the environment itself**. Our `basic` environment provides a default execution order for the five rule components that is suitable for most cases, but you can inherit the `BaseEnvironment` class and write your own `run` method to implement a more sophisticated execution order.
+3. **Customize the agent**. Depending on your specific use case, you may also need to customize the `Agent` class. For example, you may want to use your local LLM as your agents or create agents with specialized knowledge or skills.
@@ -220,3 +219,4 @@ Here's a brief overview of each example:
3. `nlp_classroom_9players_group`: This example introduces group discussions. The professor can launch a group discussion when necessary, and students can only interact with other students in the same group during the group discussion.
4. `nlp_classroom_3players_withtool`: Students in this classroom can use Bing search API when listening to the class.
5. `math_problem_2players_tools`: A simple example demonstrating how two agents can use the WolframAlpha API to play an arithmetic game.
+6. `prisoner_dilema`: A simple example showing how LLMs handle the prisoner dilema.
diff --git a/agentverse/UI.py b/agentverse/UI.py
deleted file mode 100644
index 64af5ae19..000000000
--- a/agentverse/UI.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import time
-
-from module.generate import Generate
-import gradio as gr
-
-
-class UI:
- """
- the UI of frontend
- """
-
- def __init__(self):
- """
- init a UI.
- default number of students is 0
- """
- self.generate = Generate(0)
- self.autoplay = False
- self.image_now = None
- self.text_now = None
-
- def stop_auto(self):
- self.autoplay = False
-
- def auto_play(self):
- self.autoplay = True
- while self.autoplay:
- outputs = self.generate.gen_output()
- self.image_now, self.text_now = outputs
- yield outputs
- time.sleep(5)
-
- def gen_output(self):
- yield self.image_now, self.text_now, gr.Button.update(interactive=False)
- outputs = self.generate.gen_output()
- self.image_now, self.text_now = outputs
- yield self.image_now, self.text_now, gr.Button.update(interactive=True)
-
- def reset(self):
- self.image_now, self.text_now = self.generate.reset()
- return self.image_now, self.text_now
-
- def launch(self):
- """
- start a frontend
- """
- with gr.Blocks() as demo:
- with gr.Row():
- with gr.Column():
- image_output = gr.Image()
- with gr.Row():
- reset_btn = gr.Button("Reset")
- stop_auto_btn = gr.Button("Stop Auto Play")
- auto_btn = gr.Button("Start Auto Play")
- next_btn = gr.Button("Next", variant="primary")
- text_output = gr.HTML(self.generate.reset()[1])
- # stu_num = gr.Number(label="Student Number", precision=0)
- next_btn.click(fn=self.gen_output, inputs=None, outputs=[image_output, text_output, next_btn],
- show_progress=False)
- reset_btn.click(fn=self.reset, inputs=None, outputs=[image_output, text_output],
- show_progress=False)
- auto_btn.click(
- fn=self.auto_play,
- inputs=None, outputs=[image_output, text_output], show_progress=False)
- stop_auto_btn.click(fn=self.stop_auto, inputs=None, outputs=None, show_progress=False)
- demo.queue(concurrency_count=5, max_size=20).launch()
diff --git a/agentverse/__init__.py b/agentverse/__init__.py
index 10de7cad0..89277bd64 100644
--- a/agentverse/__init__.py
+++ b/agentverse/__init__.py
@@ -1,5 +1,6 @@
from .tasks import *
-from .agents import Agent
+
+# from .agents import Agent
from .environments import env_registry
from .environments.rules import Rule
from .environments.rules.order import order_registry
@@ -8,4 +9,11 @@
from .environments.rules.updater import updater_registry
from .environments.rules.visibility import visibility_registry
from .agentverse import AgentVerse
-from .initialization import prepare_task_config, load_agent, load_environment, load_tools, load_llm, load_memory
\ No newline at end of file
+from .initialization import (
+ prepare_task_config,
+ load_agent,
+ load_environment,
+ load_tools,
+ load_llm,
+ load_memory,
+)
diff --git a/agentverse/agents/__init__.py b/agentverse/agents/__init__.py
index d2361b7a3..5f3213e98 100644
--- a/agentverse/agents/__init__.py
+++ b/agentverse/agents/__init__.py
@@ -1 +1,9 @@
-from .agent import Agent
+# from .agent import Agent
+from agentverse.registry import Registry
+
+agent_registry = Registry(name="AgentRegistry")
+
+from .base import BaseAgent
+from .conversation_agent import ConversationAgent
+from .tool_agent import ToolAgent
+from .prisoner_dilema_agent import PoliceAgent, PrisonerAgent
diff --git a/agentverse/agents/agent.py b/agentverse/agents/agent.py
deleted file mode 100644
index c588b3a0c..000000000
--- a/agentverse/agents/agent.py
+++ /dev/null
@@ -1,275 +0,0 @@
-import logging
-import os
-from typing import Any, List, Optional, Sequence, Tuple, Union
-
-from langchain.agents import AgentExecutor, AgentOutputParser
-from langchain.agents.agent import Agent as langchainAgent
-from langchain.agents.agent import AgentOutputParser
-from langchain.agents.conversational_chat.output_parser import ConvoOutputParser
-from langchain.base_language import BaseLanguageModel
-from langchain.callbacks.base import BaseCallbackManager
-from langchain.chains import LLMChain
-from langchain.chat_models.base import BaseChatModel
-from langchain.llms.base import BaseLLM
-from langchain.memory import ChatMessageHistory
-from langchain.prompts import BasePromptTemplate, PromptTemplate
-from langchain.prompts.chat import ChatPromptTemplate, MessagesPlaceholder
-from langchain.schema import (AgentAction, AIMessage,
- BaseMessage, HumanMessage, SystemMessage)
-from langchain.tools.base import BaseTool
-
-from agentverse.memory import SummaryMemory
-from agentverse.message import Message
-from agentverse.parser import OutputParseError
-
-try:
- import openai
-except ImportError:
- is_openai_available = False
- logging.warning("openai package is not installed")
-else:
- openai.api_key = os.environ.get("OPENAI_API_KEY")
- openai.proxy = os.environ.get("http_proxy")
- if openai.proxy is None:
- openai.proxy = os.environ.get("HTTP_PROXY")
- if openai.api_key is None:
- logging.warning("OpenAI API key is not set. Please set the environment variable OPENAI_API_KEY")
- is_openai_available = False
- else:
- is_openai_available = True
-
-
-class Agent(langchainAgent):
- """Action unit in the environment.
-
- Args:
- name: Name of the agent
- role_description: Description of the role of the agent
- memory: Memory of the agent. This is used to store the chat history.
- max_retry: Maximum number of retries when encountering an error during generation
- tool_memory: Memory of the tools. This is used to store the observations from executing tools by this agent.
- tools: List of tools that the agent can use
- receiver: List of receivers of the messages generated by this agent
- """
- name: str
- role_description: str
- memory: ChatMessageHistory
- max_retry: int = 3
- tool_memory: Optional[SummaryMemory] = None
- tools: List[BaseTool] = []
- receiver: List[str] = ["all"]
- input_variables: List[str] = ["chat_history", "agent_scratchpad"]
-
- @classmethod
- def _get_default_output_parser(cls, **kwargs: Any) -> AgentOutputParser:
- return ConvoOutputParser()
-
- @property
- def _agent_type(self) -> str:
- raise NotImplementedError
-
- @property
- def observation_prefix(self) -> str:
- """Prefix to append the observation with."""
- return "Observation: "
-
- @property
- def llm_prefix(self) -> str:
- """Prefix to append the llm call with."""
- return ""
-
- def get_receiver(self) -> List[str]:
- return self.receiver
-
- def set_receiver(self, receiver: List[str]) -> None:
- self.receiver = receiver
-
- def step(self, env_description: str="") -> Message:
- """Generate the next message"""
- executor = AgentExecutor.from_agent_and_tools(
- agent=self,
- tools=self.tools,
- verbose=True,
- return_intermediate_steps=True,
- output_parser=self.output_parser
- )
- input_arguments = {"agent_name": self.name}
- if isinstance(self.llm_chain.llm, BaseChatModel):
- chat_history = self.memory.messages
- elif isinstance(self.llm_chain.llm, BaseLLM):
- messages = self.memory.messages
- chat_history = "\n".join([message.content for message in messages])
- chat_history += f"\n{self.name}: "
- else:
- raise ValueError(f"Unsupported LLM type: {self.llm_chain.llm}")
-
- input_arguments['chat_history'] = chat_history
- input_arguments['env_description'] = env_description
-
- response = None
- for i in range(self.max_retry):
- try:
- response = executor(input_arguments)
- break
- except OutputParseError as e:
- print(e)
- print("Retrying...")
- continue
- if response is None:
- raise ValueError(f"{self.name} failed to generate valid response.")
-
- message = Message(
- content=response['output'],
- role="assistant",
- sender=self.name,
- receiver=self.get_receiver(),
- tool_response=response['intermediate_steps']
- )
- return message
-
- async def astep(self, env_description: str="") -> Message:
- """Asynchronous version of step"""
- executor = AgentExecutor.from_agent_and_tools(
- agent=self,
- tools=self.tools,
- verbose=True,
- return_intermediate_steps=True,
- output_parser=self.output_parser
- )
- input_arguments = {"agent_name": self.name}
- if isinstance(self.llm_chain.llm, BaseChatModel):
- chat_history = self.memory.messages
- elif isinstance(self.llm_chain.llm, BaseLLM):
- messages = self.memory.messages
- chat_history = "\n".join([message.content for message in messages])
- chat_history += f"\n{self.name}: "
- else:
- raise ValueError(f"Unsupported LLM type: {self.llm_chain.llm}")
- if self.tool_memory is not None:
- input_arguments['tool_memory'] = self.tool_memory.buffer
-
- input_arguments['chat_history'] = chat_history
- input_arguments['env_description'] = env_description
- if self.tool_memory is not None:
- input_arguments['tool_memory'] = self.tool_memory.buffer
- else:
- input_arguments['tool_memory'] = ""
-
- response = None
- for i in range(self.max_retry):
- try:
- response = await executor.acall(input_arguments)
- break
- except OutputParseError as e:
- print(e)
- print("Retrying...")
- continue
- if response is None:
- logging.error(f"{self.name} failed to generate valid response.")
-
- message = Message(
- content="" if response is None else response['output'],
- role="assistant",
- sender=self.name,
- receiver=self.get_receiver(),
- tool_response=[] if response is None else response['intermediate_steps']
- )
- return message
-
- @classmethod
- def create_prompt(
- cls,
- llm: BaseLanguageModel,
- tools: List[BaseTool],
- prefix_prompt: str = "",
- format_prompt: str = "",
- suffix_prompt: str = "",
- input_variables: Optional[List[str]] = None,
- ) -> BasePromptTemplate:
- """Create the prompt for the agent.
-
- Args:
- llm: Language model to use. Following the interface of langchain.llms
- tools: List of tools to use. Following the interface of langchain.tools
- prefix_prompt: Introduce the scene
- format_prompt: Specifying the format of the output and the rule of the environment
- suffix_prompt: Telling the agent to response
- input_variables: List of placeholder variable names in your prompt
- """
- if input_variables is None:
- if len(tools) > 0:
- input_variables = ["chat_history", "agent_scratchpad"]
- else:
- input_variables = ["chat_history"]
- if isinstance(llm, BaseChatModel):
- messages = [
- SystemMessage(content=prefix_prompt),
- SystemMessage(content=format_prompt),
- SystemMessage(content=suffix_prompt),
- MessagesPlaceholder(variable_name="chat_history")
- ]
- if len(tools) > 0:
- messages.append(MessagesPlaceholder(variable_name="agent_scratchpad"))
- return ChatPromptTemplate(input_variables=input_variables, messages=messages)
- elif isinstance(llm, BaseLLM):
- template = f"{prefix_prompt}\n\n{format_prompt}\n\n{suffix_prompt}"
- return PromptTemplate(input_variables=input_variables, template=template)
- else:
- raise ValueError(f"Unsupported LLM type: {llm}")
-
- def _construct_scratchpad(
- self, intermediate_steps: List[Tuple[AgentAction, str]]
- ) -> Union[List[BaseMessage], str]:
- """Construct the scratchpad that lets the agent continue its thought process."""
- thoughts = [] if isinstance(self.llm_chain.llm, BaseChatModel) else ""
- for action, observation in intermediate_steps:
- if isinstance(self.llm_chain.llm, BaseChatModel):
- thoughts.append(AIMessage(content=action.log.strip()))
- human_message = HumanMessage(
- content="Tool response:\n{observation}".format(observation=observation)
- )
- thoughts.append(human_message)
- elif isinstance(self.llm_chain.llm, BaseLLM):
- thoughts += action.log.strip()
- thoughts += "\n" + "Observation: " + observation
- return thoughts
-
- @classmethod
- def from_llm_and_tools(
- cls,
- llm: BaseLanguageModel,
- tools: Sequence[BaseTool],
- callback_manager: Optional[BaseCallbackManager] = None,
- output_parser: Optional[AgentOutputParser] = None,
- prefix_prompt: str = "",
- format_prompt: str = "",
- suffix_prompt: str = "",
- input_variables: Optional[List[str]] = None,
- **kwargs
- ) -> langchainAgent:
- """Construct an agent from an LLM and tools."""
- cls._validate_tools(tools)
- prompt = cls.create_prompt(
- llm=llm,
- tools=tools,
- prefix_prompt=prefix_prompt,
- format_prompt=format_prompt,
- suffix_prompt=suffix_prompt,
- input_variables=input_variables,
- )
- llm_chain = LLMChain(
- llm=llm,
- prompt=prompt,
- callback_manager=callback_manager,
- )
- tool_names = [tool.name for tool in tools]
- return cls(
- llm_chain=llm_chain,
- tools=tools,
- allowed_tools=tool_names,
- output_parser=output_parser,
- **kwargs,
- )
-
- def reset(self):
- self.memory.clear()
\ No newline at end of file
diff --git a/agentverse/agents/base.py b/agentverse/agents/base.py
new file mode 100644
index 000000000..13db010b7
--- /dev/null
+++ b/agentverse/agents/base.py
@@ -0,0 +1,78 @@
+import logging
+from abc import abstractmethod
+from typing import List, NamedTuple, Set, Union
+
+from pydantic import BaseModel, Field
+
+from agentverse.llms import BaseLLM
+from agentverse.memory import BaseMemory, ChatHistoryMemory
+from agentverse.message import Message
+from agentverse.parser import OutputParser
+
+
+class BaseAgent(BaseModel):
+ name: str
+ llm: BaseLLM
+ output_parser: OutputParser
+ prompt_template: str
+ role_description: str = Field(default="")
+ memory: BaseMemory = Field(default_factory=ChatHistoryMemory)
+ max_retry: int = Field(default=3)
+ receiver: Set[str] = Field(default=set({"all"}))
+ async_mode: bool = Field(default=True)
+
+ @abstractmethod
+ def step(self, env_description: str = "") -> Message:
+ """Get one step response"""
+ pass
+
+ @abstractmethod
+ def astep(self, env_description: str = "") -> Message:
+ """Asynchronous version of step"""
+ pass
+
+ @abstractmethod
+ def reset(self) -> None:
+ """Reset the agent"""
+ pass
+
+ @abstractmethod
+ def add_message_to_memory(self, messages: List[Message]) -> None:
+ """Add a message to the memory"""
+ pass
+
+ def get_receiver(self) -> Set[str]:
+ return self.receiver
+
+ def set_receiver(self, receiver: Union[Set[str], str]) -> None:
+ if isinstance(receiver, str):
+ self.receiver = set({receiver})
+ elif isinstance(receiver, set):
+ self.receiver = receiver
+ else:
+ raise ValueError(
+ "input argument `receiver` must be a string or a set of string"
+ )
+
+ def add_receiver(self, receiver: Union[Set[str], str]) -> None:
+ if isinstance(receiver, str):
+ self.receiver.add(receiver)
+ elif isinstance(receiver, set):
+ self.receiver = self.receiver.union(receiver)
+ else:
+ raise ValueError(
+ "input argument `receiver` must be a string or a set of string"
+ )
+
+ def remove_receiver(self, receiver: Union[Set[str], str]) -> None:
+ if isinstance(receiver, str):
+ try:
+ self.receiver.remove(receiver)
+ except KeyError as e:
+ logging.warning(f"Receiver {receiver} not found.")
+ elif isinstance(receiver, set):
+ self.receiver = self.receiver.difference(receiver)
+ else:
+ raise ValueError(
+ "input argument `receiver` must be a string or a set of string"
+ )
diff --git a/agentverse/agents/conversation_agent.py b/agentverse/agents/conversation_agent.py
new file mode 100644
index 000000000..fecd87d9c
--- /dev/null
+++ b/agentverse/agents/conversation_agent.py
@@ -0,0 +1,96 @@
+from __future__ import annotations
+
+import logging
+import bdb
+from string import Template
+from typing import TYPE_CHECKING, List
+
+from agentverse.message import Message
+
+from . import agent_registry
+from .base import BaseAgent
+
+
+@agent_registry.register("conversation")
+class ConversationAgent(BaseAgent):
+ def step(self, env_description: str = "") -> Message:
+ prompt = self._fill_prompt_template(env_description)
+
+ parsed_response = None
+ for i in range(self.max_retry):
+ try:
+ response = self.llm.generate_response(prompt)
+ parsed_response = self.output_parser.parse(response)
+ break
+ except KeyboardInterrupt:
+ raise
+ except Exception as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ async def astep(self, env_description: str = "") -> Message:
+ """Asynchronous version of step"""
+ prompt = self._fill_prompt_template(env_description)
+
+ parsed_response = None
+ for i in range(self.max_retry):
+ try:
+ response = await self.llm.agenerate_response(prompt)
+ parsed_response = self.output_parser.parse(response)
+ break
+ except (KeyboardInterrupt, bdb.BdbQuit):
+ raise
+ except Exception as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ def _fill_prompt_template(self, env_description: str = "") -> str:
+ """Fill the placeholders in the prompt template
+
+ In the conversation agent, three placeholders are supported:
+ - ${agent_name}: the name of the agent
+ - ${env_description}: the description of the environment
+ - ${role_description}: the description of the role of the agent
+ - ${chat_history}: the chat history of the agent
+ """
+ input_arguments = {
+ "agent_name": self.name,
+ "env_description": env_description,
+ "role_description": self.role_description,
+ "chat_history": self.memory.to_string(add_sender_prefix=True),
+ }
+ return Template(self.prompt_template).safe_substitute(input_arguments)
+
+ def add_message_to_memory(self, messages: List[Message]) -> None:
+ self.memory.add_message(messages)
+
+ def reset(self) -> None:
+ """Reset the agent"""
+ self.memory.reset()
+ # TODO: reset receiver
diff --git a/agentverse/agents/prisoner_dilema_agent.py b/agentverse/agents/prisoner_dilema_agent.py
new file mode 100644
index 000000000..deaaa5a1a
--- /dev/null
+++ b/agentverse/agents/prisoner_dilema_agent.py
@@ -0,0 +1,165 @@
+from __future__ import annotations
+
+import logging
+from string import Template
+from typing import TYPE_CHECKING, List
+
+from agentverse.message import Message
+
+from . import agent_registry
+from .base import BaseAgent
+
+if TYPE_CHECKING:
+ from agentverse.environments.base import BaseEnvironment
+
+
+class PrisonerDilemaAgent(BaseAgent):
+ def step(
+ self,
+ environment: BaseEnvironment,
+ env_description: str = "",
+ ) -> Message:
+ prompt = self._fill_prompt_template(env_description)
+
+ parsed_response = None
+ for i in range(self.max_retry):
+ try:
+ response = self.llm.generate_response(prompt)
+ parsed_response = self.output_parser.parse(self, environment, response)
+ break
+ except Exception as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ async def astep(
+ self, environment: BaseEnvironment, env_description: str = ""
+ ) -> Message:
+ """Asynchronous version of step"""
+ prompt = self._fill_prompt_template(env_description)
+
+ parsed_response = None
+ for i in range(self.max_retry):
+ try:
+ response = await self.llm.agenerate_response(prompt)
+ parsed_response = self.output_parser.parse(self, environment, response)
+ break
+ except Exception as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ def _fill_prompt_template(self, env_description: str = "") -> str:
+ """Fill the placeholders in the prompt template
+
+ In the conversation agent, three placeholders are supported:
+ - ${agent_name}: the name of the agent
+ - ${env_description}: the description of the environment
+ - ${role_description}: the description of the role of the agent
+ - ${chat_history}: the chat history of the agent
+ """
+ input_arguments = {
+ "agent_name": self.name,
+ "env_description": env_description,
+ "role_description": self.role_description,
+ "chat_history": self.memory.to_string(add_sender_prefix=True),
+ }
+ return Template(self.prompt_template).safe_substitute(input_arguments)
+
+ def add_message_to_memory(self, messages: List[Message]) -> None:
+ self.memory.add_message(messages)
+
+ def reset(self) -> None:
+ """Reset the agent"""
+ self.memory.reset()
+ # TODO: reset receiver
+
+
+@agent_registry.register("police")
+class PoliceAgent(PrisonerDilemaAgent):
+ interrogating_form: str
+
+ def _fill_prompt_template(self, env_description: str = "") -> str:
+ """Fill the placeholders in the prompt template
+
+ In the conversation agent, three placeholders are supported:
+ - ${agent_name}: the name of the agent
+ - ${env_description}: the description of the environment
+ - ${role_description}: the description of the role of the agent
+ - ${chat_history}: the chat history of the agent
+ """
+ input_arguments = {
+ "agent_name": self.name,
+ "env_description": env_description,
+ "role_description": self.role_description,
+ "chat_history": self.memory.to_string(add_sender_prefix=True),
+ }
+
+ role_argument = {
+ "interrogating_form": self.interrogating_form,
+ }
+
+ role_description = Template(self.role_description).safe_substitute(
+ role_argument
+ )
+ input_arguments["role_description"] = role_description
+
+ return Template(self.prompt_template).safe_substitute(input_arguments)
+
+
+@agent_registry.register("prisoner")
+class PrisonerAgent(PrisonerDilemaAgent):
+ personality: str
+ relationship_with_another: str
+
+ def _fill_prompt_template(self, env_description: str = "") -> str:
+ """Fill the placeholders in the prompt template
+
+ In the conversation agent, three placeholders are supported:
+ - ${agent_name}: the name of the agent
+ - ${env_description}: the description of the environment
+ - ${role_description}: the description of the role of the agent
+ - ${chat_history}: the chat history of the agent
+ """
+ input_arguments = {
+ "agent_name": self.name,
+ "env_description": env_description,
+ "role_description": self.role_description,
+ "chat_history": self.memory.to_string(add_sender_prefix=True),
+ }
+
+ role_argument = {
+ "personality": self.personality,
+ "relationship_with_another": self.relationship_with_another,
+ }
+
+ role_description = Template(self.role_description).safe_substitute(
+ role_argument
+ )
+ input_arguments["role_description"] = role_description
+
+ return Template(self.prompt_template).safe_substitute(input_arguments)
diff --git a/agentverse/agents/tool_agent.py b/agentverse/agents/tool_agent.py
new file mode 100644
index 000000000..a95b8ce2d
--- /dev/null
+++ b/agentverse/agents/tool_agent.py
@@ -0,0 +1,174 @@
+import logging
+from string import Template
+from typing import List, NamedTuple, Optional, Union
+
+from langchain.tools import BaseTool
+from pydantic import Field
+
+from agentverse.memory import BaseMemory, ChatHistoryMemory
+from agentverse.message import Message
+from agentverse.utils import AgentAction, AgentFinish
+
+from . import agent_registry
+from .base import BaseAgent
+
+
+class ToolNotExistError(BaseException):
+ """Exception raised when parsing output from a command fails."""
+
+ def __init__(self, tool_name=""):
+ self.tool_name = tool_name
+
+ def __str__(self):
+ return f"Tool {self.tool_name} does not exist."
+
+
+@agent_registry.register("tool")
+class ToolAgent(BaseAgent):
+ tools: List[BaseTool] = Field(default=[])
+ tool_memory: BaseMemory = Field(default_factory=ChatHistoryMemory)
+ verbose: bool = Field(default=False)
+
+ def step(self, env_description: str = "") -> Message:
+ parsed_response = None
+ tool_observation = [self.tool_memory.to_string()]
+ while True:
+ prompt = self._fill_prompt_template(env_description, tool_observation)
+
+ for i in range(self.max_retry):
+ try:
+ response = self.llm.generate_response(prompt)
+ parsed_response = self.output_parser.parse(response)
+ if isinstance(parsed_response, AgentAction):
+ observation = self._call_tool(parsed_response)
+ tool_observation.append(
+ parsed_response.log.strip()
+ + f"\nObservation: {observation.strip()}"
+ )
+ break
+ except BaseException as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+ if parsed_response is None or isinstance(parsed_response, AgentFinish):
+ break
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ self._update_tool_memory(tool_observation)
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ async def astep(self, env_description: str = "") -> Message:
+ """Asynchronous version of step"""
+ parsed_response = None
+ # Initialize the tool_observation with tool_memory
+ tool_observation = [self.tool_memory.to_string()]
+ while True:
+ prompt = self._fill_prompt_template(env_description, tool_observation)
+
+ for i in range(self.max_retry):
+ try:
+ response = await self.llm.agenerate_response(prompt)
+ parsed_response = self.output_parser.parse(response)
+ if isinstance(parsed_response, AgentAction):
+ # If the response is an action, call the tool
+ # and append the observation to tool_observation
+ observation = await self._acall_tool(parsed_response)
+ tool_observation.append(
+ parsed_response.log.strip()
+ + f"\nObservation: {observation.strip()}"
+ )
+ break
+ except BaseException as e:
+ logging.error(e)
+ logging.warning("Retrying...")
+ continue
+ if parsed_response is None or isinstance(parsed_response, AgentFinish):
+ break
+
+ if parsed_response is None:
+ logging.error(f"{self.name} failed to generate valid response.")
+
+ self._update_tool_memory(tool_observation)
+
+ message = Message(
+ content=""
+ if parsed_response is None
+ else parsed_response.return_values["output"],
+ sender=self.name,
+ receiver=self.get_receiver(),
+ )
+ return message
+
+ def _call_tool(self, response: NamedTuple) -> str:
+ """Call a tool and return the output"""
+ name_to_tool = {tool.name: tool for tool in self.tools}
+ if response.tool not in name_to_tool:
+ raise ToolNotExistError(response.tool)
+ tool = name_to_tool[response.tool]
+ observation = tool.run(response.tool_input, verbose=self.verbose)
+ return observation
+
+ async def _acall_tool(self, response: NamedTuple) -> str:
+ """Call a tool and return the output"""
+ name_to_tool = {tool.name: tool for tool in self.tools}
+ if response.tool not in name_to_tool:
+ raise ToolNotExistError(response.tool)
+ tool = name_to_tool[response.tool]
+ observation = await tool.arun(response.tool_input, verbose=self.verbose)
+ return observation
+
+ def _update_tool_memory(self, tool_observation: List[str]):
+ """Update the memory of the tool"""
+ if len(tool_observation) == 1:
+ # If no tool is called this turn, do nothing
+ return
+ messages = [
+ Message(content=observation) for observation in tool_observation[1:]
+ ]
+ self.tool_memory.add_message(messages)
+
+ def _fill_prompt_template(
+ self, env_description: str = "", tool_observation: List[str] = []
+ ) -> str:
+ """Fill the placeholders in the prompt template
+
+ In the tool agent, these placeholders are supported:
+ - ${agent_name}: the name of the agent
+ - ${env_description}: the description of the environment
+ - ${role_description}: the description of the role of the agent
+ - ${chat_history}: the chat history of the agent
+ - ${tools}: the list of tools and their usage
+ - ${tool_names}: the list of tool names
+ - ${tool_observations}: the observation of the tool in this turn
+ """
+ tools = "\n".join([f"> {tool.name}: {tool.description}" for tool in self.tools])
+ tools = tools.replace("{{", "{").replace("}}", "}")
+ tool_names = ", ".join([tool.name for tool in self.tools])
+ input_arguments = {
+ "agent_name": self.name,
+ "env_description": env_description,
+ "role_description": self.role_description,
+ "chat_history": self.memory.to_string(add_sender_prefix=True),
+ "tools": tools,
+ "tool_names": tool_names,
+ "tool_observation": "\n".join(tool_observation),
+ }
+ return Template(self.prompt_template).safe_substitute(input_arguments)
+
+ def add_message_to_memory(self, messages: List[Message]) -> None:
+ self.memory.add_message(messages)
+
+ def reset(self) -> None:
+ """Reset the agent"""
+ self.memory.reset()
+ # TODO: reset receiver
diff --git a/agentverse/agentverse.py b/agentverse/agentverse.py
index 99a7b0133..6307314dc 100644
--- a/agentverse/agentverse.py
+++ b/agentverse/agentverse.py
@@ -2,19 +2,23 @@
import logging
from typing import List
-from agentverse.agents import Agent
+# from agentverse.agents import Agent
+from agentverse.agents.conversation_agent import BaseAgent
from agentverse.environments import BaseEnvironment
-from agentverse.initialization import (load_agent, load_environment,
- prepare_task_config)
+from agentverse.initialization import load_agent, load_environment, prepare_task_config
-logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s', datefmt='%m/%d/%Y %H:%M:%S', level=logging.INFO)
+logging.basicConfig(
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
+ datefmt="%m/%d/%Y %H:%M:%S",
+ level=logging.INFO,
+)
openai_logger = logging.getLogger("openai")
openai_logger.setLevel(logging.WARNING)
-class AgentVerse():
- def __init__(self, agents: List[Agent], environment: BaseEnvironment):
+class AgentVerse:
+ def __init__(self, agents: List[BaseAgent], environment: BaseEnvironment):
self.agents = agents
self.environment = environment
@@ -29,13 +33,13 @@ def from_task(cls, task: str):
# Build the agents
agents = []
- for agent_configs in task_config['agents']:
+ for agent_configs in task_config["agents"]:
agent = load_agent(agent_configs)
agents.append(agent)
# Build the environment
- env_config = task_config['environment']
- env_config['agents'] = agents
+ env_config = task_config["environment"]
+ env_config["agents"] = agents
environment = load_environment(env_config)
return cls(agents, environment)
@@ -51,7 +55,7 @@ def reset(self):
for agent in self.agents:
agent.reset()
- def next(self):
+ def next(self, *args, **kwargs):
"""Run the environment for one step and return the return message."""
- return_message = asyncio.run(self.environment.step())
+ return_message = asyncio.run(self.environment.step(*args, **kwargs))
return return_message
diff --git a/agentverse/demo.py b/agentverse/demo.py
index 22bdc7d12..0a0045dbf 100644
--- a/agentverse/demo.py
+++ b/agentverse/demo.py
@@ -23,48 +23,78 @@ def cover_img(background, img, place: Tuple[int, int]):
background[place[0] + i, place[1] + j] = img[i, j, :3]
-def get_avatar(idx):
- img = cv2.imread(f"./imgs/{idx}.png")
- base64_str = cv2.imencode(".png", img)[1].tostring()
- return "data:image/png;base64," + base64.b64encode(base64_str).decode("utf-8")
-
-
class UI:
"""
the UI of frontend
"""
+
def __init__(self, task: str):
"""
init a UI.
default number of students is 0
"""
+ self.messages = []
+ self.task = task
self.backend = AgentVerse.from_task(task)
- self.agent_id = {self.backend.agents[idx].name:idx for idx in range(len(self.backend.agents))}
+ self.agent_id = {
+ self.backend.agents[idx].name: idx
+ for idx in range(len(self.backend.agents))
+ }
self.stu_num = len(self.agent_id) - 1
self.autoplay = False
self.image_now = None
self.text_now = None
+ def get_avatar(self, idx):
+ if self.task == "prisoner_dilema":
+ img = cv2.imread(f"./imgs/prison/{idx}.png")
+ else:
+ img = cv2.imread(f"./imgs/{idx}.png")
+ base64_str = cv2.imencode(".png", img)[1].tostring()
+ return "data:image/png;base64," + base64.b64encode(base64_str).decode("utf-8")
+
def stop_autoplay(self):
self.autoplay = False
+ return (
+ gr.Button.update(interactive=False),
+ gr.Button.update(interactive=False),
+ gr.Button.update(interactive=False),
+ )
def start_autoplay(self):
self.autoplay = True
+ yield self.image_now, self.text_now, gr.Button.update(
+ interactive=False
+ ), gr.Button.update(interactive=True), gr.Button.update(interactive=False)
while self.autoplay:
outputs = self.gen_output()
self.image_now, self.text_now = outputs
- yield outputs
+ yield *outputs, gr.Button.update(
+ interactive=not self.autoplay
+ ), gr.Button.update(interactive=self.autoplay), gr.Button.update(
+ interactive=not self.autoplay
+ )
def delay_gen_output(self):
- yield self.image_now, self.text_now, gr.Button.update(interactive=False)
+ yield self.image_now, self.text_now, gr.Button.update(
+ interactive=False
+ ), gr.Button.update(interactive=False)
outputs = self.gen_output()
self.image_now, self.text_now = outputs
- yield self.image_now, self.text_now, gr.Button.update(interactive=True)
+ yield self.image_now, self.text_now, gr.Button.update(
+ interactive=True
+ ), gr.Button.update(interactive=True)
def delay_reset(self):
+ self.autoplay = False
self.image_now, self.text_now = self.reset()
- return self.image_now, self.text_now
-
+ return (
+ self.image_now,
+ self.text_now,
+ gr.Button.update(interactive=True),
+ gr.Button.update(interactive=False),
+ gr.Button.update(interactive=True),
+ )
def reset(self, stu_num=0):
"""
@@ -78,82 +108,117 @@ def reset(self, stu_num=0):
"""
# [To-Do] Need to add a function to assign agent numbers into the backend.
"""
- #self.backend.reset(stu_num)
- #self.stu_num = stu_num
+ # self.backend.reset(stu_num)
+ # self.stu_num = stu_num
"""
# [To-Do] Pass the parameters to reset
"""
self.backend.reset()
- background = cv2.imread("./imgs/background.png")
- back_h, back_w, _ = background.shape
- stu_cnt = 0
- for h_begin, w_begin in itertools.product(range(800, back_h, 300), range(135, back_w - 200, 200)):
- stu_cnt += 1
- img = cv2.imread(
- f"./imgs/{(stu_cnt - 1) % 11 + 1 if stu_cnt <= self.stu_num else 'empty'}.png",
- cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (h_begin -30 if img.shape[0] > 190 else h_begin, w_begin))
+ if self.task == "prisoner_dilema":
+ background = cv2.imread("./imgs/prison/case_1.png")
+ else:
+ background = cv2.imread("./imgs/background.png")
+ back_h, back_w, _ = background.shape
+ stu_cnt = 0
+ for h_begin, w_begin in itertools.product(
+ range(800, back_h, 300), range(135, back_w - 200, 200)
+ ):
+ stu_cnt += 1
+ img = cv2.imread(
+ f"./imgs/{(stu_cnt - 1) % 11 + 1 if stu_cnt <= self.stu_num else 'empty'}.png",
+ cv2.IMREAD_UNCHANGED,
+ )
+ cover_img(
+ background,
+ img,
+ (h_begin - 30 if img.shape[0] > 190 else h_begin, w_begin),
+ )
+ self.messages = []
return [cv2.cvtColor(background, cv2.COLOR_BGR2RGB), ""]
-
def gen_img(self, data: List[Dict]):
"""
generate new image with sender rank
- :param sender:
+ :param data:
:return: the new image
"""
- # The foloowing code need to be more general. This one is too task-specific.
- #if len(data) != self.stu_num:
- if len(data) != self.stu_num+1:
+ # The following code need to be more general. This one is too task-specific.
+ # if len(data) != self.stu_num:
+ if len(data) != self.stu_num + 1:
raise gr.Error("data length is not equal to the total number of students.")
- background = cv2.imread("./imgs/background.png")
- back_h, back_w, _ = background.shape
- stu_cnt = 0
- if data[stu_cnt]["message"] not in ["", "[RaiseHand]"]:
+ if self.task == "prisoner_dilema":
img = cv2.imread("./imgs/speaking.png", cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (370, 1250))
- for h_begin, w_begin in itertools.product(range(800, back_h, 300), range(135, back_w - 200, 200)):
- stu_cnt += 1
- if stu_cnt <= self.stu_num:
- img = cv2.imread(
- f"./imgs/{(stu_cnt - 1) % 11 + 1}.png",
- cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (h_begin -30 if img.shape[0] > 190 else h_begin, w_begin))
- if "[RaiseHand]" in data[stu_cnt]["message"]:
- #elif data[stu_cnt]["message"] == "[RaiseHand]":
- img = cv2.imread("./imgs/hand.png", cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (h_begin - 90, w_begin + 10))
- elif data[stu_cnt]["message"] not in ["", "[RaiseHand]"]:
- img = cv2.imread("./imgs/speaking.png", cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (h_begin - 90, w_begin + 10))
-
+ if (
+ len(self.messages) < 2
+ or self.messages[-1][0] == 1
+ or self.messages[-2][0] == 2
+ ):
+ background = cv2.imread("./imgs/prison/case_1.png")
+ if data[0]["message"] != "":
+ cover_img(background, img, (400, 480))
else:
- img = cv2.imread("./imgs/empty.png", cv2.IMREAD_UNCHANGED)
- cover_img(background, img, (h_begin, w_begin))
+ background = cv2.imread("./imgs/prison/case_2.png")
+ if data[0]["message"] != "":
+ cover_img(background, img, (400, 880))
+ if data[1]["message"] != "":
+ cover_img(background, img, (550, 480))
+ if data[2]["message"] != "":
+ cover_img(background, img, (550, 880))
+ else:
+ background = cv2.imread("./imgs/background.png")
+ back_h, back_w, _ = background.shape
+ stu_cnt = 0
+ if data[stu_cnt]["message"] not in ["", "[RaiseHand]"]:
+ img = cv2.imread("./imgs/speaking.png", cv2.IMREAD_UNCHANGED)
+ cover_img(background, img, (370, 1250))
+ for h_begin, w_begin in itertools.product(
+ range(800, back_h, 300), range(135, back_w - 200, 200)
+ ):
+ stu_cnt += 1
+ if stu_cnt <= self.stu_num:
+ img = cv2.imread(
+ f"./imgs/{(stu_cnt - 1) % 11 + 1}.png", cv2.IMREAD_UNCHANGED
+ )
+ cover_img(
+ background,
+ img,
+ (h_begin - 30 if img.shape[0] > 190 else h_begin, w_begin),
+ )
+ if "[RaiseHand]" in data[stu_cnt]["message"]:
+ # elif data[stu_cnt]["message"] == "[RaiseHand]":
+ img = cv2.imread("./imgs/hand.png", cv2.IMREAD_UNCHANGED)
+ cover_img(background, img, (h_begin - 90, w_begin + 10))
+ elif data[stu_cnt]["message"] not in ["", "[RaiseHand]"]:
+ img = cv2.imread("./imgs/speaking.png", cv2.IMREAD_UNCHANGED)
+ cover_img(background, img, (h_begin - 90, w_begin + 10))
+
+ else:
+ img = cv2.imread("./imgs/empty.png", cv2.IMREAD_UNCHANGED)
+ cover_img(background, img, (h_begin, w_begin))
return cv2.cvtColor(background, cv2.COLOR_BGR2RGB)
def return_format(self, messages: List[Message]):
- _format = [{"message": "","sender": idx} for idx in range(len(self.agent_id))]
+ _format = [{"message": "", "sender": idx} for idx in range(len(self.agent_id))]
for message in messages:
- _format[self.agent_id[message.sender]]["message"] = "[{}]: {}".format(message.sender, message.content)
+ _format[self.agent_id[message.sender]]["message"] = "[{}]: {}".format(
+ message.sender, message.content
+ )
return _format
-
def gen_output(self):
"""
generate new image and message of next step
:return: [new image, new message]
"""
- #data = self.backend.next_data()
+ # data = self.backend.next_data()
return_message = self.backend.next()
data = self.return_format(return_message)
-
- #data.sort(key=lambda item: item["sender"])
+ # data.sort(key=lambda item: item["sender"])
"""
# [To-Do]; Check the message from the backend: only 1 person can speak
"""
@@ -166,24 +231,24 @@ def gen_output(self):
message = item["message"]
break
"""
- messages = []
for item in data:
if item["message"] not in ["", "[RaiseHand]"]:
- messages.append((item['sender'], item['message']))
- message = ""
- for sender, msg in messages:
+ self.messages.append((item["sender"], item["message"]))
+ for sender, msg in self.messages:
if sender == 0:
- avatar = get_avatar(0)
+ avatar = self.get_avatar(0)
else:
- avatar = get_avatar((sender - 1) % 11 + 1)
- message += f'
'
- message += f'

'
- message += f'
'
- message += f'{msg}'
- message += f'
'
+ avatar = self.get_avatar((sender - 1) % 11 + 1)
+ message = (
+ f''
+ f'

'
+ f'
'
+ f"{msg}"
+ f"
" + message
+ )
+ message = '' + message + "
"
return [self.gen_img(data), message]
-
def launch(self):
"""
start a frontend
@@ -194,29 +259,61 @@ def launch(self):
image_output = gr.Image()
with gr.Row():
reset_btn = gr.Button("Reset")
- #next_btn = gr.Button("Next", variant="primary")
+ # next_btn = gr.Button("Next", variant="primary")
next_btn = gr.Button("Next")
- stop_autoplay_btn = gr.Button("Stop Autoplay")
+ stop_autoplay_btn = gr.Button(
+ "Stop Autoplay", interactive=False
+ )
start_autoplay_btn = gr.Button("Start Autoplay")
- #text_output = gr.Textbox()
+ # text_output = gr.Textbox()
text_output = gr.HTML(self.reset()[1])
# Given a botton to provide student numbers and their inf.
- #stu_num = gr.Number(label="Student Number", precision=0)
- #stu_num = self.stu_num
+ # stu_num = gr.Number(label="Student Number", precision=0)
+ # stu_num = self.stu_num
- #next_btn.click(fn=self.gen_output, inputs=None, outputs=[image_output, text_output], show_progress=False)
- next_btn.click(fn=self.delay_gen_output, inputs=None, outputs=[image_output, text_output, next_btn], show_progress=False)
+ # next_btn.click(fn=self.gen_output, inputs=None, outputs=[image_output, text_output], show_progress=False)
+ next_btn.click(
+ fn=self.delay_gen_output,
+ inputs=None,
+ outputs=[image_output, text_output, next_btn, start_autoplay_btn],
+ show_progress=False,
+ )
# [To-Do] Add botton: re-start (load different people and env)
- #reset_btn.click(fn=self.reset, inputs=stu_num, outputs=[image_output, text_output], show_progress=False)
- #reset_btn.click(fn=self.reset, inputs=None, outputs=[image_output, text_output], show_progress=False)
- reset_btn.click(fn=self.delay_reset, inputs=None, outputs=[image_output, text_output], show_progress=False)
-
- stop_autoplay_btn.click(fn=self.stop_autoplay, inputs=None, outputs=None, show_progress=False)
- start_autoplay_btn.click(fn=self.start_autoplay, inputs=None, outputs=[image_output, text_output], show_progress=False)
+ # reset_btn.click(fn=self.reset, inputs=stu_num, outputs=[image_output, text_output], show_progress=False)
+ # reset_btn.click(fn=self.reset, inputs=None, outputs=[image_output, text_output], show_progress=False)
+ reset_btn.click(
+ fn=self.delay_reset,
+ inputs=None,
+ outputs=[
+ image_output,
+ text_output,
+ next_btn,
+ stop_autoplay_btn,
+ start_autoplay_btn,
+ ],
+ show_progress=False,
+ )
+
+ stop_autoplay_btn.click(
+ fn=self.stop_autoplay,
+ inputs=None,
+ outputs=[next_btn, stop_autoplay_btn, start_autoplay_btn],
+ show_progress=False,
+ )
+ start_autoplay_btn.click(
+ fn=self.start_autoplay,
+ inputs=None,
+ outputs=[
+ image_output,
+ text_output,
+ next_btn,
+ stop_autoplay_btn,
+ start_autoplay_btn,
+ ],
+ show_progress=False,
+ )
demo.queue(concurrency_count=5, max_size=20).launch()
- #demo.launch()
-
-
+ # demo.launch()
diff --git a/agentverse/environments/__init__.py b/agentverse/environments/__init__.py
index 4dc494ac7..5e7dd3b57 100644
--- a/agentverse/environments/__init__.py
+++ b/agentverse/environments/__init__.py
@@ -1,5 +1,9 @@
from typing import Dict
from agentverse.registry import Registry
+
env_registry = Registry(name="EnvironmentRegistry")
from .base import BaseEnvironment
+from .basic import BasicEnvironment
+from .pokemon import PokemonEnvironment
+from .prisoner_dilema import PrisonerDilemaEnvironment
diff --git a/agentverse/environments/base.py b/agentverse/environments/base.py
index 334eaf147..e537a51b3 100644
--- a/agentverse/environments/base.py
+++ b/agentverse/environments/base.py
@@ -1,20 +1,21 @@
-import asyncio
-import logging
-from typing import Any, Dict, List
+from __future__ import annotations
+
+from abc import abstractmethod
+from typing import TYPE_CHECKING, Any, Dict, List
from pydantic import BaseModel
-from agentverse.agents.agent import Agent
-from agentverse.environments.rules.base import Rule
-from agentverse.message import Message
+# from agentverse.agents.agent import Agent
-from . import env_registry as EnvironmentRegistry
+if TYPE_CHECKING:
+ from agentverse.agents.base import BaseAgent
+ from agentverse.environments.rules.base import Rule
+ from agentverse.message import Message
-@EnvironmentRegistry.register("base")
class BaseEnvironment(BaseModel):
"""
- A basic environment implementing the logic of conversation.
+ Base class for environment.
Args:
agents: List of agents
@@ -24,61 +25,23 @@ class BaseEnvironment(BaseModel):
last_messages: Messages from last turn
rule_params: Variables set by the rule
"""
- agents: List[Agent]
+
+ agents: List[BaseAgent]
rule: Rule
max_turns: int = 10
cnt_turn: int = 0
last_messages: List[Message] = []
rule_params: Dict = {}
- def __init__(self, rule, **kwargs):
- rule_config = rule
- order_config = rule_config.get('order', {'type': 'sequential'})
- visibility_config = rule_config.get('visibility', {'type': 'all'})
- selector_config = rule_config.get('selector', {'type': 'base'})
- updater_config = rule_config.get('updater', {'type': 'base'})
- describer_config = rule_config.get('describer', {'type': 'base'})
- rule = Rule(order_config, visibility_config, selector_config, updater_config, describer_config)
- super().__init__(rule=rule, **kwargs)
-
+ @abstractmethod
async def step(self) -> List[Message]:
"""Run one step of the environment"""
+ pass
- # Get the next agent index
- agent_ids = self.rule.get_next_agent_idx(self)
-
- # Generate current environment description
- env_descriptions = self.rule.get_env_description(self)
-
- # Generate the next message
- messages = await asyncio.gather(*[self.agents[i].astep(env_descriptions[i]) for i in agent_ids])
-
- # Some rules will select certain messages from all the messages
- selected_messages = self.rule.select_message(self, messages)
- self.last_messages = selected_messages
- self.print_messages(selected_messages)
-
- # Update the memory of the agents
- self.rule.update_memory(self)
-
- # Update the set of visible agents for each agent
- self.rule.update_visible_agents(self)
-
- self.cnt_turn += 1
-
- return selected_messages
-
- def print_messages(self, messages: List[Message]) -> None:
- for message in messages:
- if message is not None:
- logging.info(f"{message.sender}: {message.content}")
-
+ @abstractmethod
def reset(self) -> None:
"""Reset the environment"""
- self.cnt_turn = 0
- self.rule.reset()
- for agent in self.agents:
- agent.reset()
+ pass
def is_done(self) -> bool:
"""Check if the environment is done"""
diff --git a/agentverse/environments/basic.py b/agentverse/environments/basic.py
new file mode 100644
index 000000000..55e2bb6fb
--- /dev/null
+++ b/agentverse/environments/basic.py
@@ -0,0 +1,94 @@
+import asyncio
+import logging
+from typing import Any, Dict, List
+
+# from agentverse.agents.agent import Agent
+from agentverse.agents.conversation_agent import BaseAgent
+from agentverse.environments.rules.base import Rule
+from agentverse.message import Message
+
+from . import env_registry as EnvironmentRegistry
+from .base import BaseEnvironment
+
+
+@EnvironmentRegistry.register("basic")
+class BasicEnvironment(BaseEnvironment):
+ """
+ A basic environment implementing the logic of conversation.
+
+ Args:
+ agents: List of agents
+ rule: Rule for the environment
+ max_turns: Maximum number of turns
+ cnt_turn: Current turn number
+ last_messages: Messages from last turn
+ rule_params: Variables set by the rule
+ """
+
+ agents: List[BaseAgent]
+ rule: Rule
+ max_turns: int = 10
+ cnt_turn: int = 0
+ last_messages: List[Message] = []
+ rule_params: Dict = {}
+
+ def __init__(self, rule, **kwargs):
+ rule_config = rule
+ order_config = rule_config.get("order", {"type": "sequential"})
+ visibility_config = rule_config.get("visibility", {"type": "all"})
+ selector_config = rule_config.get("selector", {"type": "basic"})
+ updater_config = rule_config.get("updater", {"type": "basic"})
+ describer_config = rule_config.get("describer", {"type": "basic"})
+ rule = Rule(
+ order_config,
+ visibility_config,
+ selector_config,
+ updater_config,
+ describer_config,
+ )
+ super().__init__(rule=rule, **kwargs)
+
+ async def step(self) -> List[Message]:
+ """Run one step of the environment"""
+
+ # Get the next agent index
+ agent_ids = self.rule.get_next_agent_idx(self)
+
+ # Generate current environment description
+ env_descriptions = self.rule.get_env_description(self)
+
+ # Generate the next message
+ messages = await asyncio.gather(
+ *[self.agents[i].astep(env_descriptions[i]) for i in agent_ids]
+ )
+
+ # Some rules will select certain messages from all the messages
+ selected_messages = self.rule.select_message(self, messages)
+ self.last_messages = selected_messages
+ self.print_messages(selected_messages)
+
+ # Update the memory of the agents
+ self.rule.update_memory(self)
+
+ # Update the set of visible agents for each agent
+ self.rule.update_visible_agents(self)
+
+ self.cnt_turn += 1
+
+ return selected_messages
+
+ def print_messages(self, messages: List[Message]) -> None:
+ for message in messages:
+ if message is not None:
+ logging.info(f"{message.sender}: {message.content}")
+
+ def reset(self) -> None:
+ """Reset the environment"""
+ self.cnt_turn = 0
+ self.rule.reset()
+ for agent in self.agents:
+ agent.reset()
+
+ def is_done(self) -> bool:
+ """Check if the environment is done"""
+ return self.cnt_turn >= self.max_turns
diff --git a/agentverse/environments/pokemon.py b/agentverse/environments/pokemon.py
new file mode 100644
index 000000000..6e9af5fd5
--- /dev/null
+++ b/agentverse/environments/pokemon.py
@@ -0,0 +1,108 @@
+import asyncio
+import time
+import logging
+from typing import Any, Dict, List, Optional
+
+# from agentverse.agents.agent import Agent
+from agentverse.agents.conversation_agent import BaseAgent
+from agentverse.environments.rules.base import Rule
+from agentverse.message import Message
+
+from . import env_registry as EnvironmentRegistry
+from .base import BaseEnvironment
+
+
+@EnvironmentRegistry.register("pokemon")
+class PokemonEnvironment(BaseEnvironment):
+ """
+ An environment for Pokémon demo.
+
+ Args:
+ agents: List of agents
+ rule: Rule for the environment
+ max_turns: Maximum number of turns
+ cnt_turn: Current turn number
+ last_messages: Messages from last turn
+ rule_params: Variables set by the rule
+ """
+
+ agents: List[BaseAgent]
+ rule: Rule
+ max_turns: int = 10
+ cnt_turn: int = 0
+ last_messages: List[Message] = []
+ rule_params: Dict = {}
+
+ def __init__(self, rule, **kwargs):
+ rule_config = rule
+ order_config = rule_config.get("order", {"type": "sequential"})
+ visibility_config = rule_config.get("visibility", {"type": "all"})
+ selector_config = rule_config.get("selector", {"type": "basic"})
+ updater_config = rule_config.get("updater", {"type": "basic"})
+ describer_config = rule_config.get("describer", {"type": "basic"})
+ rule = Rule(
+ order_config,
+ visibility_config,
+ selector_config,
+ updater_config,
+ describer_config,
+ )
+ super().__init__(rule=rule, **kwargs)
+
+ async def step(
+ self, player_content: str, receiver: str, receiver_id: Optional[int] = None
+ ) -> List[Message]:
+ """Run one step of the environment"""
+
+ # Get the next agent index
+ # time.sleep(8)
+ # return [Message(content="Test", sender="May", receiver=["May"])]
+ if receiver_id is None:
+ for agent in self.agents:
+ if agent.name == receiver:
+ receiver_id = agent.agent_id
+ break
+ agent_ids = [receiver_id]
+ agent_name = receiver
+ player_message = Message(
+ sender="Brenden", content=player_content, receiver=[agent_name]
+ )
+
+ # Update the set of visible agents for each agent
+ self.rule.update_visible_agents(self)
+
+ # Generate current environment description
+ env_descriptions = self.rule.get_env_description(self, player_content)
+
+ # Generate the next message
+ messages = await asyncio.gather(
+ *[self.agents[i].astep(env_descriptions[i]) for i in agent_ids]
+ )
+
+ # Some rules will select certain messages from all the messages
+ selected_messages = self.rule.select_message(self, messages)
+
+ # Update the memory of the agents
+ self.last_messages = [player_message, *selected_messages]
+ self.rule.update_memory(self)
+ self.print_messages(selected_messages)
+
+ self.cnt_turn += 1
+
+ return selected_messages
+
+ def print_messages(self, messages: List[Message]) -> None:
+ for message in messages:
+ if message is not None:
+ logging.info(f"{message.sender}: {message.content}")
+
+ def reset(self) -> None:
+ """Reset the environment"""
+ self.cnt_turn = 0
+ self.rule.reset()
+ for agent in self.agents:
+ agent.reset()
+
+ def is_done(self) -> bool:
+ """Check if the environment is done"""
+ return self.cnt_turn >= self.max_turns
diff --git a/agentverse/environments/prisoner_dilema.py b/agentverse/environments/prisoner_dilema.py
new file mode 100644
index 000000000..49e352dcf
--- /dev/null
+++ b/agentverse/environments/prisoner_dilema.py
@@ -0,0 +1,47 @@
+import asyncio
+import logging
+from typing import Any, Dict, List
+
+# from agentverse.agents.agent import Agent
+from agentverse.agents.conversation_agent import BaseAgent
+from agentverse.environments.rules.base import Rule
+from agentverse.message import Message
+
+from . import env_registry as EnvironmentRegistry
+from .basic import BasicEnvironment
+
+
+@EnvironmentRegistry.register("prisoner_dilema")
+class PrisonerDilemaEnvironment(BasicEnvironment):
+ """
+ An environment for prisoner dilema.
+ """
+
+ async def step(self) -> List[Message]:
+ """Run one step of the environment"""
+
+ # Get the next agent index
+ agent_ids = self.rule.get_next_agent_idx(self)
+
+ # Generate current environment description
+ env_descriptions = self.rule.get_env_description(self)
+
+ # Generate the next message
+ messages = await asyncio.gather(
+ *[self.agents[i].astep(self, env_descriptions[i]) for i in agent_ids]
+ )
+
+ # Some rules will select certain messages from all the messages
+ selected_messages = self.rule.select_message(self, messages)
+ self.last_messages = selected_messages
+ self.print_messages(selected_messages)
+
+ # Update the memory of the agents
+ self.rule.update_memory(self)
+
+ # Update the set of visible agents for each agent
+ self.rule.update_visible_agents(self)
+
+ self.cnt_turn += 1
+
+ return selected_messages
diff --git a/agentverse/environments/rules/base.py b/agentverse/environments/rules/base.py
index 7802b366d..f53ddc8f7 100644
--- a/agentverse/environments/rules/base.py
+++ b/agentverse/environments/rules/base.py
@@ -5,14 +5,11 @@
from pydantic import BaseModel
-from agentverse.environments.rules.describer import (BaseDescriber,
- describer_registry)
+from agentverse.environments.rules.describer import BaseDescriber, describer_registry
from agentverse.environments.rules.order import BaseOrder, order_registry
-from agentverse.environments.rules.selector import (BaseSelector,
- selector_registry)
+from agentverse.environments.rules.selector import BaseSelector, selector_registry
from agentverse.environments.rules.updater import BaseUpdater, updater_registry
-from agentverse.environments.rules.visibility import (BaseVisibility,
- visibility_registry)
+from agentverse.environments.rules.visibility import BaseVisibility, visibility_registry
if TYPE_CHECKING:
from agentverse.environments.base import BaseEnvironment
@@ -22,46 +19,68 @@
class Rule(BaseModel):
"""
- Rule for the environment. It controls the speaking order of the agents
+ Rule for the environment. It controls the speaking order of the agents
and maintain the set of visible agents for each agent.
"""
+
order: BaseOrder
visibility: BaseVisibility
selector: BaseSelector
updater: BaseUpdater
describer: BaseDescriber
- def __init__(self, order_config, visibility_config, selector_config, updater_config, describer_config):
+ def __init__(
+ self,
+ order_config,
+ visibility_config,
+ selector_config,
+ updater_config,
+ describer_config,
+ ):
order = order_registry.build(**order_config)
visibility = visibility_registry.build(**visibility_config)
selector = selector_registry.build(**selector_config)
updater = updater_registry.build(**updater_config)
describer = describer_registry.build(**describer_config)
- super().__init__(order=order, visibility=visibility, selector=selector, updater=updater, describer=describer)
+ super().__init__(
+ order=order,
+ visibility=visibility,
+ selector=selector,
+ updater=updater,
+ describer=describer,
+ )
- def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
+ def get_next_agent_idx(
+ self, environment: BaseEnvironment, *args, **kwargs
+ ) -> List[int]:
"""Return the index of the next agent to speak"""
- return self.order.get_next_agent_idx(environment)
+ return self.order.get_next_agent_idx(environment, *args, **kwargs)
- def update_visible_agents(self, environment: BaseEnvironment) -> None:
+ def update_visible_agents(
+ self, environment: BaseEnvironment, *args, **kwargs
+ ) -> None:
"""Update the set of visible agents for the agent"""
- self.visibility.update_visible_agents(environment)
+ self.visibility.update_visible_agents(environment, *args, **kwargs)
- def select_message(self, environment: BaseEnvironment, messages: List[Message]) -> List[Message]:
+ def select_message(
+ self, environment: BaseEnvironment, messages: List[Message], *args, **kwargs
+ ) -> List[Message]:
"""Select a set of valid messages from all the generated messages"""
- return self.selector.select_message(environment, messages)
+ return self.selector.select_message(environment, messages, *args, **kwargs)
- def update_memory(self, environment: BaseEnvironment) -> None:
+ def update_memory(self, environment: BaseEnvironment, *args, **kwargs) -> None:
"""For each message, add it to the memory of the agent who is able to see that message"""
- self.updater.update_memory(environment)
+ self.updater.update_memory(environment, *args, **kwargs)
- def get_env_description(self, environment: BaseEnvironment) -> List[str]:
+ def get_env_description(
+ self, environment: BaseEnvironment, *args, **kwargs
+ ) -> List[str]:
"""Return the description of the environment for each agent"""
- return self.describer.get_env_description(environment)
+ return self.describer.get_env_description(environment, *args, **kwargs)
def reset(self) -> None:
self.order.reset()
self.visibility.reset()
self.selector.reset()
self.updater.reset()
- self.describer.reset()
\ No newline at end of file
+ self.describer.reset()
diff --git a/agentverse/environments/rules/describer/__init__.py b/agentverse/environments/rules/describer/__init__.py
index a275126f3..803d13920 100644
--- a/agentverse/environments/rules/describer/__init__.py
+++ b/agentverse/environments/rules/describer/__init__.py
@@ -1,5 +1,9 @@
from agentverse.registry import Registry
+
describer_registry = Registry(name="DescriberRegistry")
from .base import BaseDescriber
+from .basic import BasicDescriber
from .classroom import ClassroomDescriber
+from .pokemon import PokemonDescriber
+from .prisoner import PrisonerDescriber
diff --git a/agentverse/environments/rules/describer/base.py b/agentverse/environments/rules/describer/base.py
index 5fbaa1d97..83b7b9763 100644
--- a/agentverse/environments/rules/describer/base.py
+++ b/agentverse/environments/rules/describer/base.py
@@ -5,16 +5,19 @@
from pydantic import BaseModel
from . import describer_registry as DescriberRegistry
+from abc import abstractmethod
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
-@DescriberRegistry.register("base")
class BaseDescriber(BaseModel):
- def get_env_description(self, environment: BaseEnvironment) -> List[str]:
+ @abstractmethod
+ def get_env_description(
+ self, environment: BaseEnvironment, *args, **kwargs
+ ) -> List[str]:
"""Return the environment description for each agent"""
- return ["" for _ in range(len(environment.agents))]
+ pass
def reset(self) -> None:
pass
diff --git a/agentverse/environments/rules/describer/basic.py b/agentverse/environments/rules/describer/basic.py
new file mode 100644
index 000000000..20f6bd4f6
--- /dev/null
+++ b/agentverse/environments/rules/describer/basic.py
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, List
+
+from . import describer_registry as DescriberRegistry
+from .base import BaseDescriber
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@DescriberRegistry.register("basic")
+class BasicDescriber(BaseDescriber):
+ def get_env_description(self, environment: BaseEnvironment) -> List[str]:
+ """Return the environment description for each agent"""
+ return ["" for _ in range(len(environment.agents))]
diff --git a/agentverse/environments/rules/describer/classroom.py b/agentverse/environments/rules/describer/classroom.py
index 1ffbe5fe9..91fd22a75 100644
--- a/agentverse/environments/rules/describer/classroom.py
+++ b/agentverse/environments/rules/describer/classroom.py
@@ -1,16 +1,17 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, List
+from string import Template
from . import describer_registry as DescriberRegistry
-from .base import BaseDescriber
+from .basic import BasicDescriber
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
@DescriberRegistry.register("classroom")
-class ClassroomDescriber(BaseDescriber):
+class ClassroomDescriber(BasicDescriber):
start_prompt: str
end_prompt: str
@@ -18,7 +19,7 @@ def get_env_description(self, environment: BaseEnvironment) -> List[str]:
if not environment.rule_params.get("is_grouped", False):
if environment.rule_params.get("is_grouped_ended", False):
# If the group discussion is just ended
- environment.rule_params['is_grouped_ended'] = False
+ environment.rule_params["is_grouped_ended"] = False
return [self.end_prompt for _ in range(len(environment.agents))]
else:
return super().get_env_description(environment)
@@ -28,7 +29,11 @@ def get_env_description(self, environment: BaseEnvironment) -> List[str]:
# Professor will not participate in group discussion
description.append("")
else:
- description.append(self.start_prompt.format(receiver_name=", ".join(agent.receiver)))
+ description.append(
+ Template(self.start_prompt).safe_substitute(
+ {"receiver_name": ", ".join(agent.receiver)}
+ )
+ )
return description
def reset(self) -> None:
diff --git a/agentverse/environments/rules/describer/pokemon.py b/agentverse/environments/rules/describer/pokemon.py
new file mode 100644
index 000000000..abbfbfbd3
--- /dev/null
+++ b/agentverse/environments/rules/describer/pokemon.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, List, Optional
+
+from . import describer_registry as DescriberRegistry
+from .base import BaseDescriber
+
+if TYPE_CHECKING:
+ from agentverse.environments.pokemon import PokemonEnvironment
+
+
+@DescriberRegistry.register("pokemon")
+class PokemonDescriber(BaseDescriber):
+ def get_env_description(
+ self,
+ environment: PokemonEnvironment,
+ player_content: str,
+ time: Optional[str] = None,
+ ) -> List[str]:
+ description = ""
+ if time is not None:
+ description += f"It is now {time}. Brendan is talking to you.\n"
+ description += f"[Brendan]: {player_content}\n"
+ return [description for _ in range(len(environment.agents))]
diff --git a/agentverse/environments/rules/describer/prisoner.py b/agentverse/environments/rules/describer/prisoner.py
new file mode 100644
index 000000000..a96352049
--- /dev/null
+++ b/agentverse/environments/rules/describer/prisoner.py
@@ -0,0 +1,49 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, List
+
+from . import describer_registry as DescriberRegistry
+from .base import BaseDescriber
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@DescriberRegistry.register("prisoner")
+class PrisonerDescriber(BaseDescriber):
+ switch_func = {
+ "Both Suspects": "Suspect2",
+ "Suspect1": "Suspect2",
+ "Suspect2": "Suspect1",
+ }
+ receiver: str = "Both Suspects"
+
+ def get_env_description(self, environment: BaseEnvironment) -> List[str]:
+ if environment.cnt_turn == 0:
+ environment.agents[0].set_receiver({"all"})
+ environment.agents[1].set_receiver({"Police", "Suspect1"})
+ environment.agents[2].set_receiver({"Police", "Suspect2"})
+
+ # only police have to choose to talk to suspect1 or suspect
+ description = []
+ for i, agent in enumerate(environment.agents):
+ if i == 0:
+ # police -> suspect1 -> police -> suspect2
+ if environment.cnt_turn % 2 == 1:
+ description.append("")
+ continue
+
+ # Police will have to choose talk to which suspect
+ description.append(f"You are now talking to {self.receiver}")
+
+ receiver = "all" if self.receiver == "Both Suspects" else self.receiver
+ self.receiver = self.switch_func[self.receiver]
+ agent.set_receiver({receiver})
+
+ else:
+ description.append("")
+
+ return description
+
+ def reset(self) -> None:
+ pass
diff --git a/agentverse/environments/rules/order/__init__.py b/agentverse/environments/rules/order/__init__.py
index 84d08bd04..4ac22ec7b 100644
--- a/agentverse/environments/rules/order/__init__.py
+++ b/agentverse/environments/rules/order/__init__.py
@@ -6,3 +6,4 @@
from .random import RandomOrder
from .concurrent import ConcurrentOrder
from .classroom import ClassroomOrder
+from .prisoner import PrisonerOrder
diff --git a/agentverse/environments/rules/order/classroom.py b/agentverse/environments/rules/order/classroom.py
index 38f6cb852..dba459bee 100644
--- a/agentverse/environments/rules/order/classroom.py
+++ b/agentverse/environments/rules/order/classroom.py
@@ -20,16 +20,17 @@ class ClassroomOrder(BaseOrder):
3. The professor can call on a student, then the student can speak or ask a question
4. In the group discussion, the students in the group can speak in turn
"""
+
def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
# `is_grouped_ended`: whether the group discussion just ended
# `is_grouped`: whether it is currently in a group discussion
- if environment.rule_params.get('is_grouped_ended', False):
+ if environment.rule_params.get("is_grouped_ended", False):
return [0]
- if environment.rule_params.get('is_grouped', False):
+ if environment.rule_params.get("is_grouped", False):
return self.get_next_agent_idx_grouped(environment)
else:
return self.get_next_agent_idx_ungrouped(environment)
-
+
def get_next_agent_idx_ungrouped(self, environment: BaseEnvironment) -> List[int]:
if len(environment.last_messages) == 0:
# If the class just begins or no one speaks in the last turn, we let only the professor speak
@@ -41,9 +42,12 @@ def get_next_agent_idx_ungrouped(self, environment: BaseEnvironment) -> List[int
if sender.startswith("Professor"):
if content.startswith("[CallOn]"):
# 1. professor calls on someone, then the student should speak
- result = re.search(r'\[CallOn\] Yes, ([sS]tudent )?(\w+)', content)
+ result = re.search(r"\[CallOn\] Yes, ([sS]tudent )?(\w+)", content)
if result is not None:
- name_to_id = {agent.name[len("Student "):]: i for i, agent in enumerate(environment.agents)}
+ name_to_id = {
+ agent.name[len("Student ") :]: i
+ for i, agent in enumerate(environment.agents)
+ }
return [name_to_id[result.group(2)]]
else:
# 2. professor normally speaks, then anyone can act
@@ -54,24 +58,32 @@ def get_next_agent_idx_ungrouped(self, environment: BaseEnvironment) -> List[int
# 5. the group discussion is just over, and there happens to be only a student speaking in the last turn
return [0]
else:
- # If len(last_messages) > 1, then
+ # If len(last_messages) > 1, then
# 1. there must be at least one student raises hand or speaks.
# 2. the group discussion is just over.
return [0]
- assert False, f"Should not reach here, last_messages: {environment.last_messages}"
+ assert (
+ False
+ ), f"Should not reach here, last_messages: {environment.last_messages}"
def get_next_agent_idx_grouped(self, environment: BaseEnvironment) -> List[int]:
# Get the grouping information
- # groups: A list of list of agent ids, the i-th list contains
+ # groups: A list of list of agent ids, the i-th list contains
# the agent ids in the i-th group
# group_speaker_mapping: A mapping from group id to the id of
# the speaker in the group
# `groups` should be set in the corresponding `visibility`,
# and `group_speaker_mapping` should be maintained here.
if "groups" not in environment.rule_params:
- logging.warning("The environment is grouped, but the grouping information is not provided.")
- groups = environment.rule_params.get("groups", [list(range(len(environment.agents)))])
- group_speaker_mapping = environment.rule_params.get("group_speaker_mapping", {i: 0 for i in range(len(groups))})
+ logging.warning(
+ "The environment is grouped, but the grouping information is not provided."
+ )
+ groups = environment.rule_params.get(
+ "groups", [list(range(len(environment.agents)))]
+ )
+ group_speaker_mapping = environment.rule_params.get(
+ "group_speaker_mapping", {i: 0 for i in range(len(groups))}
+ )
# For grouped environment, we let the students speak in turn within each group
next_agent_idx = []
@@ -85,4 +97,4 @@ def get_next_agent_idx_grouped(self, environment: BaseEnvironment) -> List[int]:
group_speaker_mapping[k] = (v + 1) % len(groups[k])
environment.rule_params["group_speaker_mapping"] = group_speaker_mapping
- return next_agent_idx
\ No newline at end of file
+ return next_agent_idx
diff --git a/agentverse/environments/rules/order/concurrent.py b/agentverse/environments/rules/order/concurrent.py
index d9e29e54d..738e32e28 100644
--- a/agentverse/environments/rules/order/concurrent.py
+++ b/agentverse/environments/rules/order/concurrent.py
@@ -8,10 +8,12 @@
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
+
@OrderRegistry.register("concurrent")
class ConcurrentOrder(BaseOrder):
"""
The agents speak concurrently
"""
+
def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
return list(range(len(environment.agents)))
diff --git a/agentverse/environments/rules/order/prisoner.py b/agentverse/environments/rules/order/prisoner.py
new file mode 100644
index 000000000..6859911a8
--- /dev/null
+++ b/agentverse/environments/rules/order/prisoner.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+import logging
+import re
+from typing import TYPE_CHECKING, Any, List, Optional
+
+from . import order_registry as OrderRegistry
+from .base import BaseOrder
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@OrderRegistry.register("prisoner")
+class PrisonerOrder(BaseOrder):
+ """The order for a classroom discussion
+ The agents speak in the following order:
+ 1. The professor speaks first
+ 2. Then the professor can continue to speak, and the students can raise hands
+ 3. The professor can call on a student, then the student can speak or ask a question
+ 4. In the group discussion, the students in the group can speak in turn
+ """
+
+ # try police, prisoner1 prisoner2 first
+
+ last_prisoner_index: int = 1
+ switch_func: dict = {1: 2, 2: 1}
+
+ def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
+ if len(environment.last_messages) == 0:
+ # If the game just begins or , we let only the police speak
+ return [0]
+ elif len(environment.last_messages) == 1:
+ message = environment.last_messages[0]
+ sender = message.sender
+ content = message.content
+ if sender.startswith("Police"):
+ next_prisoner = self.last_prisoner_index
+ self.last_prisoner_index = self.switch_func[self.last_prisoner_index]
+ return [next_prisoner]
+ elif sender.startswith("Suspect"):
+ # 3. when one prisoner made his action, let the police tell another prisoner
+ return [0]
+ else:
+ # If len(last_messages) > 1, then
+ # 1. there must be at least one student raises hand or speaks.
+ # 2. the group discussion is just over.
+ return [0]
diff --git a/agentverse/environments/rules/order/random.py b/agentverse/environments/rules/order/random.py
index e8578bc24..8a348a156 100644
--- a/agentverse/environments/rules/order/random.py
+++ b/agentverse/environments/rules/order/random.py
@@ -9,11 +9,13 @@
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
+
@OrderRegistry.register("random")
class RandomOrder(BaseOrder):
"""
Order for random conversation
The agents speak in a random order
"""
+
def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
return [random.randint(0, len(environment.agents) - 1)]
diff --git a/agentverse/environments/rules/order/sequential.py b/agentverse/environments/rules/order/sequential.py
index 11f0e5705..a739a7c2d 100644
--- a/agentverse/environments/rules/order/sequential.py
+++ b/agentverse/environments/rules/order/sequential.py
@@ -15,6 +15,7 @@ class SequentialOrder(BaseOrder):
Order for sequential conversation
The agents speak in a round-robin fashion
"""
+
next_agent_idx: int = 0
def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
@@ -22,6 +23,6 @@ def get_next_agent_idx(self, environment: BaseEnvironment) -> List[int]:
ret = self.next_agent_idx
self.next_agent_idx = (self.next_agent_idx + 1) % len(environment.agents)
return [ret]
-
+
def reset(self) -> None:
- self.next_agent_idx = 0
\ No newline at end of file
+ self.next_agent_idx = 0
diff --git a/agentverse/environments/rules/selector/__init__.py b/agentverse/environments/rules/selector/__init__.py
index 2b713a5e6..da11bda59 100644
--- a/agentverse/environments/rules/selector/__init__.py
+++ b/agentverse/environments/rules/selector/__init__.py
@@ -1,5 +1,7 @@
from agentverse.registry import Registry
+
selector_registry = Registry(name="SelectorRegistry")
from .base import BaseSelector
-from .classroom import ClassroomSelector
\ No newline at end of file
+from .basic import BasicSelector
+from .classroom import ClassroomSelector
diff --git a/agentverse/environments/rules/selector/base.py b/agentverse/environments/rules/selector/base.py
index bc69ac9c5..cc283870f 100644
--- a/agentverse/environments/rules/selector/base.py
+++ b/agentverse/environments/rules/selector/base.py
@@ -7,6 +7,7 @@
from agentverse.message import Message
from . import selector_registry as SelectorRegistry
+from abc import abstractmethod
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
@@ -17,9 +18,13 @@ class BaseSelector(BaseModel):
"""
Base class for all selecters
"""
- def select_message(self, environment: BaseEnvironment, messages: List[Message]) -> List[Message]:
+
+ @abstractmethod
+ def select_message(
+ self, environment: BaseEnvironment, messages: List[Message]
+ ) -> List[Message]:
"""Selects a set of valid messages from all messages"""
- return messages
+ pass
def reset(self) -> None:
- pass
\ No newline at end of file
+ pass
diff --git a/agentverse/environments/rules/selector/basic.py b/agentverse/environments/rules/selector/basic.py
new file mode 100644
index 000000000..1ebc0b48b
--- /dev/null
+++ b/agentverse/environments/rules/selector/basic.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List
+
+from agentverse.message import Message
+
+from . import selector_registry as SelectorRegistry
+from .base import BaseSelector
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@SelectorRegistry.register("basic")
+class BasicSelector(BaseSelector):
+ """
+ Base class for all selecters
+ """
+
+ def select_message(
+ self, environment: BaseEnvironment, messages: List[Message]
+ ) -> List[Message]:
+ """Selects a set of valid messages from all messages"""
+ return messages
+
+ def reset(self) -> None:
+ pass
diff --git a/agentverse/environments/rules/selector/classroom.py b/agentverse/environments/rules/selector/classroom.py
index 99c140c56..07365b279 100644
--- a/agentverse/environments/rules/selector/classroom.py
+++ b/agentverse/environments/rules/selector/classroom.py
@@ -10,9 +10,12 @@
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
+
@SelectorRegistry.register("classroom")
class ClassroomSelector(BaseSelector):
- def select_message(self, environment: BaseEnvironment, messages: List[Message]) -> List[Message]:
+ def select_message(
+ self, environment: BaseEnvironment, messages: List[Message]
+ ) -> List[Message]:
selected = []
for message in messages:
if message.sender.startswith("Student"):
@@ -30,7 +33,11 @@ def select_message(self, environment: BaseEnvironment, messages: List[Message])
# If some student speak while the professor is speaking, then
# we brutely discard the student's message in this turn
- if len(selected) > 1 and selected[0].sender.startswith("Professor") and selected[0].content != "":
+ if (
+ len(selected) > 1
+ and selected[0].sender.startswith("Professor")
+ and selected[0].content != ""
+ ):
filtered_selected = []
filtered_selected.append(selected[0])
for message in selected[1:]:
diff --git a/agentverse/environments/rules/updater/__init__.py b/agentverse/environments/rules/updater/__init__.py
index d8682db8c..81c30a37f 100644
--- a/agentverse/environments/rules/updater/__init__.py
+++ b/agentverse/environments/rules/updater/__init__.py
@@ -1,5 +1,7 @@
from agentverse.registry import Registry
+
updater_registry = Registry(name="UpdaterRegistry")
from .base import BaseUpdater
-from .classroom import ClassroomUpdater
\ No newline at end of file
+from .basic import BasicUpdater
+from .classroom import ClassroomUpdater
diff --git a/agentverse/environments/rules/updater/base.py b/agentverse/environments/rules/updater/base.py
index ff6925c08..abd212875 100644
--- a/agentverse/environments/rules/updater/base.py
+++ b/agentverse/environments/rules/updater/base.py
@@ -2,11 +2,10 @@
from typing import TYPE_CHECKING, List, Tuple
-from langchain.schema import AgentAction
from pydantic import BaseModel
-from agentverse.agents import Agent
-from agentverse.message import Message
+# from agentverse.agents import Agent
+from abc import abstractmethod
from . import updater_registry as UpdaterRegistry
@@ -17,50 +16,12 @@
@UpdaterRegistry.register("base")
class BaseUpdater(BaseModel):
"""
- The basic version of updater.
- The messages will be seen by all the receiver specified in the message.
+ The base class of updater class.
"""
- def update_memory(self, environment: BaseEnvironment):
- added = False
- for message in environment.last_messages:
- if len(message.tool_response) > 0:
- self.add_tool_response(message.sender, environment.agents, message.tool_response)
- if message.content == "":
- continue
- added |= self.add_message_to_all_agents(environment.agents, message)
- # If no one speaks in this turn. Add an empty message to all agents
- if not added:
- for agent in environment.agents:
- agent.memory.add_user_message("[Silence]")
- def add_tool_response(self, name: str, agents: List[Agent], tool_response: List[Tuple[AgentAction, str]]):
- for agent in agents:
- if agent.name != name:
- continue
- if agent.tool_memory is not None:
- agent.tool_memory.save_context(tool_response)
- break
+ @abstractmethod
+ def update_memory(self, environment: BaseEnvironment):
+ pass
- def add_message_to_all_agents(self, agents: List[Agent], message: Message) -> bool:
- if "all" in message.receiver:
- # If receiver is all, then add the message to all agents
- for agent in agents:
- if agent.name != message.sender:
- agent.memory.add_user_message(f'{message.sender}: ' + message.content)
- else:
- agent.memory.add_ai_message(f'{message.sender}: ' + message.content)
- return True
- else:
- # If receiver is not all, then add the message to the specified agents
- receiver_set = set(message.receiver)
- for agent in agents:
- if agent.name in receiver_set:
- agent.memory.add_user_message(f'{message.sender}: ' + message.content)
- receiver_set.remove(agent.name)
- if len(receiver_set) > 0:
- missing_receiver = ', '.join(list(receiver_set))
- raise ValueError("Receiver {} not found. Message discarded".format(missing_receiver))
- return True
-
def reset(self):
- pass
\ No newline at end of file
+ pass
diff --git a/agentverse/environments/rules/updater/basic.py b/agentverse/environments/rules/updater/basic.py
new file mode 100644
index 000000000..36c59a3ec
--- /dev/null
+++ b/agentverse/environments/rules/updater/basic.py
@@ -0,0 +1,69 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Tuple
+
+from . import updater_registry as UpdaterRegistry
+from .base import BaseUpdater
+from agentverse.message import Message
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+ from agentverse.agents import BaseAgent
+
+
+@UpdaterRegistry.register("basic")
+class BasicUpdater(BaseUpdater):
+ """
+ The basic version of updater.
+ The messages will be seen by all the receiver specified in the message.
+ """
+
+ def update_memory(self, environment: BaseEnvironment):
+ added = False
+ for message in environment.last_messages:
+ if len(message.tool_response) > 0:
+ self.add_tool_response(
+ message.sender, environment.agents, message.tool_response
+ )
+ if message.content == "":
+ continue
+ added |= self.add_message_to_all_agents(environment.agents, message)
+ # If no one speaks in this turn. Add an empty message to all agents
+ if not added:
+ for agent in environment.agents:
+ agent.add_message_to_memory([Message(content="[Silence]")])
+
+ def add_tool_response(
+ self,
+ name: str,
+ agents: List[BaseAgent],
+ tool_response: List[str],
+ ):
+ for agent in agents:
+ if agent.name != name:
+ continue
+ if agent.tool_memory is not None:
+ agent.tool_memory.add_message(tool_response)
+ break
+
+ def add_message_to_all_agents(
+ self, agents: List[BaseAgent], message: Message
+ ) -> bool:
+ if "all" in message.receiver:
+ # If receiver is all, then add the message to all agents
+ for agent in agents:
+ agent.add_message_to_memory([message])
+ return True
+ else:
+ # If receiver is not all, then add the message to the specified agents
+ receiver_set = message.receiver
+ for agent in agents:
+ if agent.name in receiver_set:
+ agent.add_message_to_memory([message])
+ receiver_set.remove(agent.name)
+ if len(receiver_set) > 0:
+ missing_receiver = ", ".join(list(receiver_set))
+ raise ValueError(
+ "Receiver {} not found. Message discarded".format(missing_receiver)
+ )
+ return True
diff --git a/agentverse/environments/rules/updater/classroom.py b/agentverse/environments/rules/updater/classroom.py
index 72d6ee18a..69afc5f05 100644
--- a/agentverse/environments/rules/updater/classroom.py
+++ b/agentverse/environments/rules/updater/classroom.py
@@ -3,26 +3,31 @@
from typing import TYPE_CHECKING, List, Tuple
from . import updater_registry as UpdaterRegistry
-from .base import BaseUpdater
+from .basic import BasicUpdater
+from agentverse.message import Message
if TYPE_CHECKING:
from agentverse.environments import BaseEnvironment
@UpdaterRegistry.register("classroom")
-class ClassroomUpdater(BaseUpdater):
+class ClassroomUpdater(BasicUpdater):
def update_memory(self, environment: BaseEnvironment):
added = False
for message in environment.last_messages:
if len(message.tool_response) > 0:
- self.add_tool_response(message.sender, environment.agents, message.tool_response)
+ self.add_tool_response(
+ message.sender, environment.agents, message.tool_response
+ )
if message.content == "":
continue
added |= self.add_message_to_all_agents(environment.agents, message)
# If no one speaks in this turn. Add an empty message to all agents
if not added:
for agent in environment.agents:
- agent.memory.add_user_message("[Silence]")
+ agent.add_message_to_memory([Message(content="[Silence]")])
if environment.rule_params.get("is_grouped", False):
# When discussing, telling the professor that the group is discussing
- environment.agents[0].memory.add_user_message("[Discussing]")
+ environment.agents[0].add_message_to_memory(
+ [Message(content="[Discussing]")]
+ )
diff --git a/agentverse/environments/rules/visibility/__init__.py b/agentverse/environments/rules/visibility/__init__.py
index 055ac9835..c5fba1455 100644
--- a/agentverse/environments/rules/visibility/__init__.py
+++ b/agentverse/environments/rules/visibility/__init__.py
@@ -1,8 +1,11 @@
from typing import Dict
from agentverse.registry import Registry
+
visibility_registry = Registry(name="VisibilityRegistry")
from .base import BaseVisibility
from .all import AllVisibility
from .classroom import ClassroomVisibility
+from .oneself import OneselfVisibility
+from .prisoner import PrisonerVisibility
diff --git a/agentverse/environments/rules/visibility/all.py b/agentverse/environments/rules/visibility/all.py
index ea03f2d25..a0f6fc98a 100644
--- a/agentverse/environments/rules/visibility/all.py
+++ b/agentverse/environments/rules/visibility/all.py
@@ -12,5 +12,6 @@
@VisibilityRegistry.register("all")
class AllVisibility(BaseVisibility):
"""All the messages can be seen by all the agents"""
+
def update_visible_agents(self, environment: BaseEnvironment):
- pass
\ No newline at end of file
+ pass
diff --git a/agentverse/environments/rules/visibility/base.py b/agentverse/environments/rules/visibility/base.py
index d4a1f8aab..e5da006e5 100644
--- a/agentverse/environments/rules/visibility/base.py
+++ b/agentverse/environments/rules/visibility/base.py
@@ -15,4 +15,4 @@ def update_visible_agents(self, environment: BaseEnvironment):
"""Update the set of visible agents for the agent"""
def reset(self):
- pass
\ No newline at end of file
+ pass
diff --git a/agentverse/environments/rules/visibility/classroom.py b/agentverse/environments/rules/visibility/classroom.py
index 12bd588ca..140e82500 100644
--- a/agentverse/environments/rules/visibility/classroom.py
+++ b/agentverse/environments/rules/visibility/classroom.py
@@ -25,6 +25,7 @@ class ClassroomVisibility(BaseVisibility):
grouping method, options are ["random", "sequential"]. If it is a
list of list of int, then it should be the grouping information.
"""
+
grouping: Union[str, List[List[int]]]
student_per_group: int = 4
num_discussion_turn: int = 5
@@ -32,22 +33,24 @@ class ClassroomVisibility(BaseVisibility):
def update_visible_agents(self, environment: BaseEnvironment):
# We turn on grouping mode when the professor launches a group discussion
- if len(environment.last_messages) == 1 and environment.last_messages[0].content.startswith("[GroupDiscuss]"):
- environment.rule_params['is_grouped'] = True
+ if len(environment.last_messages) == 1 and environment.last_messages[
+ 0
+ ].content.startswith("[GroupDiscuss]"):
+ environment.rule_params["is_grouped"] = True
# We randomly group the students
- environment.rule_params['groups'] = self.group_students(environment)
+ environment.rule_params["groups"] = self.group_students(environment)
# Update the receiver for each agent
self.update_receiver(environment)
else:
# If now in grouping mode, then we check if the group discussion is over
- if environment.rule_params.get('is_grouped', False):
+ if environment.rule_params.get("is_grouped", False):
self.current_turn += 1
if self.current_turn >= self.num_discussion_turn:
self.reset()
- environment.rule_params['is_grouped'] = False
- environment.rule_params['is_grouped_ended'] = True
+ environment.rule_params["is_grouped"] = False
+ environment.rule_params["is_grouped_ended"] = True
self.update_receiver(environment, reset=True)
-
+
def group_students(self, environment: BaseEnvironment) -> List[List[int]]:
if isinstance(self.grouping, str):
student_index = list(range(1, len(environment.agents)))
@@ -55,10 +58,10 @@ def group_students(self, environment: BaseEnvironment) -> List[List[int]]:
if self.grouping == "random":
random.shuffle(student_index)
for i in range(0, len(student_index), self.student_per_group):
- result.append(student_index[i:i+self.student_per_group])
+ result.append(student_index[i : i + self.student_per_group])
elif self.grouping == "sequential":
for i in range(0, len(student_index), self.student_per_group):
- result.append(student_index[i:i+self.student_per_group])
+ result.append(student_index[i : i + self.student_per_group])
else:
raise ValueError(f"Unsupported grouping method {self.grouping}")
return result
@@ -69,13 +72,13 @@ def group_students(self, environment: BaseEnvironment) -> List[List[int]]:
def update_receiver(self, environment: BaseEnvironment, reset=False):
if reset:
for agent in environment.agents:
- agent.set_receiver(["all"])
+ agent.set_receiver(set({"all"}))
else:
groups = environment.rule_params["groups"]
for group in groups:
- group_name = [environment.agents[i].name for i in group]
+ group_name = set({environment.agents[i].name for i in group})
for agent_id in group:
environment.agents[agent_id].set_receiver(group_name)
-
+
def reset(self):
- self.current_turn = 0
\ No newline at end of file
+ self.current_turn = 0
diff --git a/agentverse/environments/rules/visibility/oneself.py b/agentverse/environments/rules/visibility/oneself.py
new file mode 100644
index 000000000..826ade24c
--- /dev/null
+++ b/agentverse/environments/rules/visibility/oneself.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from . import visibility_registry as VisibilityRegistry
+from .base import BaseVisibility
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@VisibilityRegistry.register("oneself")
+class OneselfVisibility(BaseVisibility):
+ """Only the agent itself can see the message"""
+
+ def update_visible_agents(self, environment: BaseEnvironment):
+ for agent in environment.agents:
+ agent.set_receiver(set({agent.name}))
diff --git a/agentverse/environments/rules/visibility/prisoner.py b/agentverse/environments/rules/visibility/prisoner.py
new file mode 100644
index 000000000..c21217312
--- /dev/null
+++ b/agentverse/environments/rules/visibility/prisoner.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+import random
+from typing import TYPE_CHECKING, Any, List, Union
+
+from . import visibility_registry as VisibilityRegistry
+from .base import BaseVisibility
+
+if TYPE_CHECKING:
+ from agentverse.environments import BaseEnvironment
+
+
+@VisibilityRegistry.register("prisoner")
+class PrisonerVisibility(BaseVisibility):
+ """
+ Visibility function for classroom, supports group discussion.
+
+ Args:
+ student_per_group:
+ The number of students per group.
+ num_discussion_turn:
+ The number of turns for group discussion.
+ grouping:
+ The grouping information. If it is a string, then it should be a
+ grouping method, options are ["random", "sequential"]. If it is a
+ list of list of int, then it should be the grouping information.
+ """
+
+ current_turn: int = 0
+
+ def update_visible_agents(self, environment: BaseEnvironment):
+ self.update_receiver(environment, reset=False)
+
+ def update_receiver(self, environment: BaseEnvironment, reset=False):
+ if reset:
+ for agent in environment.agents:
+ agent.set_receiver(["all"])
+ else:
+ # 0:police 1: prisoner1 2: prisoner2
+ # environment.agents[0].set_receiver({"Police", "Suspect1", "Suspect2"})
+ # environment.agents[1].set_receiver({"Police", "Suspect1"})
+ # environment.agents[2].set_receiver({"Police", "Suspect2"})
+
+ # we update receiver in environment
+ pass
+
+ def reset(self):
+ self.current_turn = 0
diff --git a/agentverse/initialization.py b/agentverse/initialization.py
index 7b989177b..eedcc320f 100644
--- a/agentverse/initialization.py
+++ b/agentverse/initialization.py
@@ -4,112 +4,90 @@
import yaml
from bmtools.agent.singletool import import_all_apis, load_single_tools
from langchain.agents import Agent as langchainAgent
-from langchain.chat_models import ChatOpenAI
-from langchain.chat_models.base import BaseChatModel
-from langchain.llms import OpenAI
-from langchain.llms.base import BaseLLM
-from langchain.memory import ChatMessageHistory
+
+# from langchain.chat_models import ChatOpenAI
+# from langchain.chat_models.base import BaseChatModel
+# from langchain.llms import OpenAI
+# from langchain.llms.base import BaseLLM
+from agentverse.llms import OpenAICompletion, OpenAIChat, llm_registry
+
+# from langchain.memory import ChatMessageHistory
from langchain.memory.prompt import _DEFAULT_SUMMARIZER_TEMPLATE
from langchain.prompts import PromptTemplate
-from agentverse.agents import Agent
+# from agentverse.agents import Agent
+from agentverse.agents import agent_registry
from agentverse.environments import BaseEnvironment, env_registry
-from agentverse.memory import SummaryMemory
+from agentverse.memory import memory_registry
+
+# from agentverse.memory.memory import SummaryMemory
from agentverse.parser import output_parser_registry
def load_llm(llm_config: Dict):
- llm_type = llm_config.pop('llm_type', 'text-davinci-003')
- if llm_type == 'gpt-3.5-turbo':
- return ChatOpenAI(**llm_config)
- elif llm_type == 'text-davinci-003':
- return OpenAI(**llm_config)
- else:
- raise NotImplementedError("LLM type {} not implemented".format(llm_type))
+ llm_type = llm_config.pop("llm_type", "text-davinci-003")
+
+ return llm_registry.build(llm_type, **llm_config)
+
def load_memory(memory_config: Dict):
- memory_type = memory_config.pop("memory_type", "chat_message_history")
- if memory_type == "chat_message_history":
- return ChatMessageHistory()
- elif memory_type == 'summary':
- llm = load_llm(memory_config.pop('llm', 'text-davinci-003'))
- prompt = memory_config.pop('prompt', _DEFAULT_SUMMARIZER_TEMPLATE)
- memory_config['prompt'] = PromptTemplate(
- input_variables=["summary", "new_lines"], template=prompt
- )
- return SummaryMemory(llm=llm, **memory_config)
- else:
- raise NotImplementedError("Memory type {} not implemented".format(memory_type))
-
+ memory_type = memory_config.pop("memory_type", "chat_history")
+ return memory_registry.build(memory_type, **memory_config)
+
+
def load_tools(tool_config: List[Dict]):
if len(tool_config) == 0:
return []
all_tools_list = []
for tool in tool_config:
- _, config = load_single_tools(tool['tool_name'], tool['tool_url'])
+ _, config = load_single_tools(tool["tool_name"], tool["tool_url"])
all_tools_list += import_all_apis(config)
return all_tools_list
+
def load_environment(env_config: Dict) -> BaseEnvironment:
- env_type = env_config.pop('env_type', 'base')
+ env_type = env_config.pop("env_type", "basic")
return env_registry.build(env_type, **env_config)
+
def load_agent(agent_config: Dict) -> langchainAgent:
- agent_type = agent_config.pop('agent_type', 'chat')
- if agent_type == "chat":
- agent = Agent.from_llm_and_tools(**agent_config)
- else:
- raise NotImplementedError("Agent type {} not found".format(agent_type))
+ agent_type = agent_config.pop("agent_type", "conversation")
+ agent = agent_registry.build(agent_type, **agent_config)
return agent
+
def prepare_task_config(task):
"""Read the yaml config of the given task in `tasks` directory."""
- all_task_dir = os.path.join(os.path.dirname(__file__), 'tasks')
+ all_task_dir = os.path.join(os.path.dirname(__file__), "tasks")
task_path = os.path.join(all_task_dir, task)
- config_path = os.path.join(task_path, 'config.yaml')
+ config_path = os.path.join(task_path, "config.yaml")
if not os.path.exists(task_path):
all_tasks = []
for task in os.listdir(all_task_dir):
- if os.path.isdir(os.path.join(all_task_dir, task)) \
- and task != "__pycache__":
+ if (
+ os.path.isdir(os.path.join(all_task_dir, task))
+ and task != "__pycache__"
+ ):
all_tasks.append(task)
raise ValueError(f"Task {task} not found. Available tasks: {all_tasks}")
if not os.path.exists(config_path):
- raise ValueError("You should include the config.yaml file in the task directory")
+ raise ValueError(
+ "You should include the config.yaml file in the task directory"
+ )
task_config = yaml.safe_load(open(config_path))
+ # Build the output parser
parser = output_parser_registry.build(task)
- task_config['output_parser'] = parser
-
- for i, agent_configs in enumerate(task_config['agents']):
- agent_configs['memory'] = load_memory(agent_configs['memory'])
- if agent_configs.get('tool_memory', None) is not None:
- agent_configs['tool_memory'] = load_memory(agent_configs['tool_memory'])
- llm = load_llm(agent_configs['llm'])
- agent_configs['llm'] = llm
- agent_configs['tools'] = load_tools(agent_configs.get("tools", []))
-
- # BaseLLM and its subclass will use .format to format the {chat_history} and {agent_scratchpad} during prompting
- # so we have to keep the bracket {{ and }} in the description of the tools (will become { and } after formatting})
- # BaseChatModel and its subclass will not use .format, so we have to replace {{ and }} with { and } in the description of the tools
- if isinstance(llm, BaseLLM):
- tool_strings = "\n".join(
- [f"> {tool.name}: {tool.description}" for tool in agent_configs['tools']]
- )
- elif isinstance(llm, BaseChatModel):
- tool_strings = "\n".join(
- [f"> {tool.name}: {tool.description.replace('{{', '{').replace('}}', '}')}" for tool in agent_configs['tools']]
- )
- else:
- raise NotImplementedError("LLM type {} not supported".format(llm.__class__.__name__))
-
- tool_names = ", ".join([tool.name for tool in agent_configs['tools']])
- # Here we assume that the description for tools only appears in prefix prompt with placeholder {tool}
- # and we assume that format prompt contains the placeholder {tool_names} that tells the model
- # which tools is available
- # TODO: Improve the flexibility
- agent_configs['output_parser'] = task_config['output_parser']
- agent_configs['prefix_prompt'] = agent_configs['prefix_prompt'] + '\n' + agent_configs['role_description']
- agent_configs['format_prompt'] = agent_configs['format_prompt'].format(tool_names=tool_names, tools=tool_strings)
-
- return task_config
\ No newline at end of file
+ task_config["output_parser"] = parser
+
+ for i, agent_configs in enumerate(task_config["agents"]):
+ agent_configs["memory"] = load_memory(agent_configs.get("memory", {}))
+ if agent_configs.get("tool_memory", None) is not None:
+ agent_configs["tool_memory"] = load_memory(agent_configs["tool_memory"])
+ llm = load_llm(agent_configs.get("llm", "text-davinci-003"))
+ agent_configs["llm"] = llm
+ agent_configs["tools"] = load_tools(agent_configs.get("tools", []))
+
+ agent_configs["output_parser"] = task_config["output_parser"]
+
+ return task_config
diff --git a/agentverse/llms/__init__.py b/agentverse/llms/__init__.py
new file mode 100644
index 000000000..182a1e38d
--- /dev/null
+++ b/agentverse/llms/__init__.py
@@ -0,0 +1,6 @@
+from agentverse.registry import Registry
+
+llm_registry = Registry(name="LLMRegistry")
+
+from .base import BaseLLM, BaseChatModel, BaseCompletionModel, LLMResult
+from .openai import OpenAIChat, OpenAICompletion
diff --git a/agentverse/llms/base.py b/agentverse/llms/base.py
new file mode 100644
index 000000000..644246cd3
--- /dev/null
+++ b/agentverse/llms/base.py
@@ -0,0 +1,36 @@
+from abc import abstractmethod
+from typing import Dict
+
+from pydantic import BaseModel, Field
+
+
+class LLMResult(BaseModel):
+ content: str
+ send_tokens: int
+ recv_tokens: int
+ total_tokens: int
+
+
+class BaseModelArgs(BaseModel):
+ pass
+
+
+class BaseLLM(BaseModel):
+ args: BaseModelArgs = Field(default_factory=BaseModelArgs)
+ max_retry: int = Field(default=3)
+
+ @abstractmethod
+ def generate_response(self, **kwargs) -> LLMResult:
+ pass
+
+ @abstractmethod
+ def agenerate_response(self, **kwargs) -> LLMResult:
+ pass
+
+
+class BaseChatModel(BaseLLM):
+ pass
+
+
+class BaseCompletionModel(BaseLLM):
+ pass
diff --git a/agentverse/llms/openai.py b/agentverse/llms/openai.py
new file mode 100644
index 000000000..10495f8bb
--- /dev/null
+++ b/agentverse/llms/openai.py
@@ -0,0 +1,127 @@
+import logging
+import os
+from typing import Dict, List, Optional, Union
+
+from pydantic import BaseModel, Field
+
+from agentverse.llms.base import LLMResult
+
+from . import llm_registry
+from .base import BaseChatModel, BaseCompletionModel, BaseModelArgs
+
+try:
+ import openai
+ from openai.error import OpenAIError
+except ImportError:
+ is_openai_available = False
+ logging.warning("openai package is not installed")
+else:
+ openai.api_key = os.environ.get("OPENAI_API_KEY")
+ openai.proxy = os.environ.get("http_proxy")
+ if openai.proxy is None:
+ openai.proxy = os.environ.get("HTTP_PROXY")
+ if openai.api_key is None:
+ logging.warning(
+ "OpenAI API key is not set. Please set the environment variable OPENAI_API_KEY"
+ )
+ is_openai_available = False
+ else:
+ is_openai_available = True
+
+
+class OpenAIChatArgs(BaseModelArgs):
+ model: str = Field(default="gpt-3.5-turbo")
+ max_tokens: int = Field(default=2048)
+ temperature: float = Field(default=1.0)
+ top_p: int = Field(default=1)
+ n: int = Field(default=1)
+ stop: Optional[Union[str, List]] = Field(default=None)
+ presence_penalty: int = Field(default=0)
+ frequency_penalty: int = Field(default=0)
+
+
+class OpenAICompletionArgs(OpenAIChatArgs):
+ model: str = Field(default="text-davinci-003")
+ suffix: str = Field(default="")
+ best_of: int = Field(default=1)
+
+
+@llm_registry.register("text-davinci-003")
+class OpenAICompletion(BaseCompletionModel):
+ args: OpenAICompletionArgs = Field(default_factory=OpenAICompletionArgs)
+
+ def __init__(self, max_retry: int = 3, **kwargs):
+ args = OpenAICompletionArgs()
+ args = args.dict()
+ for k, v in args.items():
+ args[k] = kwargs.pop(k, v)
+ if len(kwargs) > 0:
+ logging.warning(f"Unused arguments: {kwargs}")
+ super().__init__(args=args, max_retry=max_retry)
+
+ def generate_response(self, prompt: str) -> LLMResult:
+ response = openai.Completion.create(prompt=prompt, **self.args.dict())
+ return LLMResult(
+ content=response["choices"][0]["text"],
+ send_tokens=response["usage"]["prompt_tokens"],
+ recv_tokens=response["usage"]["completion_tokens"],
+ total_tokens=response["usage"]["total_tokens"],
+ )
+
+ async def agenerate_response(self, prompt: str) -> LLMResult:
+ response = await openai.Completion.acreate(prompt=prompt, **self.args.dict())
+ return LLMResult(
+ content=response["choices"][0]["text"],
+ send_tokens=response["usage"]["prompt_tokens"],
+ recv_tokens=response["usage"]["completion_tokens"],
+ total_tokens=response["usage"]["total_tokens"],
+ )
+
+
+@llm_registry.register("gpt-3.5-turbo")
+@llm_registry.register("gpt-4")
+class OpenAIChat(BaseChatModel):
+ args: OpenAIChatArgs = Field(default_factory=OpenAIChatArgs)
+
+ def __init__(self, max_retry: int = 3, **kwargs):
+ args = OpenAIChatArgs()
+ args = args.dict()
+
+ for k, v in args.items():
+ args[k] = kwargs.pop(k, v)
+ if len(kwargs) > 0:
+ logging.warning(f"Unused arguments: {kwargs}")
+ super().__init__(args=args, max_retry=max_retry)
+
+ def _construct_messages(self, prompt: str):
+ return [{"role": "user", "content": prompt}]
+
+ def generate_response(self, prompt: str) -> LLMResult:
+ messages = self._construct_messages(prompt)
+ try:
+ response = openai.ChatCompletion.create(
+ messages=messages, **self.args.dict()
+ )
+ except (OpenAIError, KeyboardInterrupt) as error:
+ raise
+ return LLMResult(
+ content=response["choices"][0]["message"]["content"],
+ send_tokens=response["usage"]["prompt_tokens"],
+ recv_tokens=response["usage"]["completion_tokens"],
+ total_tokens=response["usage"]["total_tokens"],
+ )
+
+ async def agenerate_response(self, prompt: str) -> LLMResult:
+ messages = self._construct_messages(prompt)
+ try:
+ response = await openai.ChatCompletion.acreate(
+ messages=messages, **self.args.dict()
+ )
+ except (OpenAIError, KeyboardInterrupt) as error:
+ raise
+ return LLMResult(
+ content=response["choices"][0]["message"]["content"],
+ send_tokens=response["usage"]["prompt_tokens"],
+ recv_tokens=response["usage"]["completion_tokens"],
+ total_tokens=response["usage"]["total_tokens"],
+ )
diff --git a/agentverse/memory.py b/agentverse/memory.py
deleted file mode 100644
index f4969e853..000000000
--- a/agentverse/memory.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# Modified from langchain.memory.summary.py
-
-from typing import Any, Dict, List, Tuple, Type, Union
-
-from langchain.base_language import BaseLanguageModel
-from langchain.chains.llm import LLMChain
-from langchain.memory.chat_memory import BaseChatMemory
-from langchain.memory.prompt import SUMMARY_PROMPT
-from langchain.prompts.base import BasePromptTemplate
-from langchain.schema import (AgentAction, AIMessage, BaseMessage, ChatMessage,
- SystemMessage, get_buffer_string)
-from pydantic import BaseModel, root_validator
-
-from agentverse.message import Message
-
-
-class SummarizerMixin(BaseModel):
- llm: BaseLanguageModel
- prompt: BasePromptTemplate = SUMMARY_PROMPT
- summary_message_cls: Type[BaseMessage] = AIMessage
-
- def predict_new_summary(
- self, messages: List[ChatMessage], existing_summary: str
- ) -> str:
- lines = []
- for message in messages:
- if message.role == "":
- # no role. it's tool responses
- lines.append(message.content)
- else:
- lines.append(f"{message.role}: {message.content}")
- new_lines = "\n".join(lines)
-
- chain = LLMChain(llm=self.llm, prompt=self.prompt)
- return chain.predict(summary=existing_summary, new_lines=new_lines)
-
-
-class SummaryMemory(BaseChatMemory, SummarizerMixin):
- """Conversation summarizer to memory."""
-
- buffer: str = ""
- memory_key: str = "history" #: :meta private:
-
- @property
- def memory_variables(self) -> List[str]:
- """Will always return list of memory variables.
-
- :meta private:
- """
- return [self.memory_key]
-
- def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
- """Return history buffer."""
- if self.return_messages:
- buffer: Any = [self.summary_message_cls(content=self.buffer)]
- else:
- buffer = self.buffer
- return {self.memory_key: buffer}
-
- @root_validator()
- def validate_prompt_input_variables(cls, values: Dict) -> Dict:
- """Validate that prompt input variables are consistent."""
- prompt_variables = values["prompt"].input_variables
- expected_keys = {"summary", "new_lines"}
- if expected_keys != set(prompt_variables):
- raise ValueError(
- "Got unexpected prompt input variables. The prompt expects "
- f"{prompt_variables}, but it should have {expected_keys}."
- )
- return values
-
- def save_context(self, contexts: Union[List[Tuple[AgentAction, str]], List[Message]]) -> None:
- """Save context from this conversation to buffer."""
- for context in contexts:
- if isinstance(context, Message):
- self.chat_memory.messages.append(ChatMessage(content=context.content, role=context.sender))
- elif isinstance(context, tuple) and len(context) == 2 and \
- isinstance(context[0], AgentAction) and isinstance(context[1], str):
- self.chat_memory.messages.append(ChatMessage(content=context[0].log.strip() + '\nObservation:' + context[1], role=""))
- self.buffer = self.predict_new_summary(
- self.chat_memory.messages[-len(contexts):], self.buffer
- )
-
- def clear(self) -> None:
- """Clear memory contents."""
- super().clear()
- self.buffer = ""
diff --git a/agentverse/memory/__init__.py b/agentverse/memory/__init__.py
new file mode 100644
index 000000000..2ced03877
--- /dev/null
+++ b/agentverse/memory/__init__.py
@@ -0,0 +1,7 @@
+from agentverse.registry import Registry
+
+memory_registry = Registry(name="MemoryRegistry")
+
+from .base import BaseMemory
+from .chat_history import ChatHistoryMemory
+from .summary import SummaryMemory
diff --git a/agentverse/memory/base.py b/agentverse/memory/base.py
new file mode 100644
index 000000000..cec06e8f7
--- /dev/null
+++ b/agentverse/memory/base.py
@@ -0,0 +1,20 @@
+from abc import abstractmethod
+from typing import Dict, List
+
+from pydantic import BaseModel, Field
+
+from agentverse.message import Message
+
+
+class BaseMemory(BaseModel):
+ @abstractmethod
+ def add_message(self, messages: List[Message]) -> None:
+ pass
+
+ @abstractmethod
+ def to_string(self) -> str:
+ pass
+
+ @abstractmethod
+ def reset(self) -> None:
+ pass
diff --git a/agentverse/memory/chat_history.py b/agentverse/memory/chat_history.py
new file mode 100644
index 000000000..aae5c9cb6
--- /dev/null
+++ b/agentverse/memory/chat_history.py
@@ -0,0 +1,33 @@
+from typing import List
+
+from pydantic import Field
+
+from agentverse.message import Message
+
+from . import memory_registry
+from .base import BaseMemory
+
+
+@memory_registry.register("chat_history")
+class ChatHistoryMemory(BaseMemory):
+ messages: List[Message] = Field(default=[])
+
+ def add_message(self, messages: List[Message]) -> None:
+ for message in messages:
+ self.messages.append(message)
+
+ def to_string(self, add_sender_prefix: bool = False) -> str:
+ if add_sender_prefix:
+ return "\n".join(
+ [
+ f"[{message.sender}]: {message.content}"
+ if message.sender != ""
+ else message.content
+ for message in self.messages
+ ]
+ )
+ else:
+ return "\n".join([message.content for message in self.messages])
+
+ def reset(self) -> None:
+ self.messages = []
diff --git a/agentverse/memory/summary.py b/agentverse/memory/summary.py
new file mode 100644
index 000000000..84bd98393
--- /dev/null
+++ b/agentverse/memory/summary.py
@@ -0,0 +1,87 @@
+import re
+from string import Template
+from typing import List
+
+from pydantic import Field, validator
+
+from agentverse.initialization import load_llm
+from agentverse.llms.base import BaseLLM
+from agentverse.message import Message
+
+from . import memory_registry
+from .base import BaseMemory
+
+
+@memory_registry.register("summary")
+class SummaryMemory(BaseMemory):
+ llm: BaseLLM
+ messages: List[Message] = Field(default=[])
+ buffer: str = Field(default="")
+ recursive: bool = Field(default=False)
+ prompt_template: str = Field(default="")
+
+ def __init__(self, *args, **kwargs):
+ llm_config = kwargs.pop("llm")
+ llm = load_llm(llm_config)
+ super().__init__(llm=llm, *args, **kwargs)
+
+ @validator("prompt_template")
+ def check_prompt_template(cls, v, values):
+ """Check if the prompt template is valid.
+ When recursive is True, the prompt template should contain the following arguments:
+ - $summary: The summary so far.
+ - $new_lines: The new lines to be added to the summary.
+
+ Otherwise, the prompt template should only contain $new_lines
+ """
+ recursive = values.get("recursive")
+ summary_pat = re.compile(r"\$\{?summary\}?")
+ new_lines_pat = re.compile(r"\$\{?new_lines\}?")
+ if recursive:
+ if not summary_pat.search(v):
+ raise ValueError(
+ "When recursive is True, the prompt template should contain $summary."
+ )
+ if not new_lines_pat.search(v):
+ raise ValueError(
+ "When recursive is True, the prompt template should contain $new_lines."
+ )
+ else:
+ if summary_pat.search(v):
+ raise ValueError(
+ "When recursive is False, the prompt template should not contain $summary."
+ )
+ if not new_lines_pat.search(v):
+ raise ValueError(
+ "When recursive is False, the prompt template should contain $new_lines."
+ )
+ return v
+
+ def add_message(self, messages: List[Message]) -> None:
+ new_lines = "\n".join([message.content for message in messages])
+ self.update_buffer(new_lines)
+
+ def update_buffer(self, new_message: str):
+ prompt = self._fill_in_prompt_template(new_message)
+ response = self.llm.generate_response(prompt)
+ if self.recursive:
+ self.buffer = response.content
+ else:
+ self.buffer = "\n" + response.content
+
+ def _fill_in_prompt_template(self, new_lines: str) -> str:
+ """Fill in the prompt template with the given arguments.
+
+ SummaryMemory supports the following arguments:
+ - summary: The summary so far.
+ - new_lines: The new lines to be added to the summary.
+ """
+ input_arguments = {"summary": self.buffer, "new_lines": new_lines}
+ return Template(self.prompt_template).safe_substitute(input_arguments)
+
+ def to_string(self, *args, **kwargs) -> str:
+ return self.buffer
+
+ def reset(self) -> None:
+ self.messages = []
+ self.buffer = ""
diff --git a/agentverse/message.py b/agentverse/message.py
index 2c4f3d273..f1de1889b 100644
--- a/agentverse/message.py
+++ b/agentverse/message.py
@@ -1,9 +1,12 @@
-from typing import List, Tuple
+from pydantic import BaseModel, Field
+from typing import List, Tuple, Set
-from langchain.schema import AgentAction, ChatMessage
+# from langchain.schema import AgentAction, ChatMessage
+from agentverse.utils import AgentAction
-class Message(ChatMessage):
- sender: str
- receiver: List[str]
- tool_response: List[Tuple[AgentAction, str]]
+class Message(BaseModel):
+ content: str = Field(default="")
+ sender: str = Field(default="")
+ receiver: Set[str] = Field(default=set({"all"}))
+ tool_response: List[Tuple[AgentAction, str]] = Field(default=[])
diff --git a/agentverse/parser.py b/agentverse/parser.py
index 96b97757c..abe7ae14e 100644
--- a/agentverse/parser.py
+++ b/agentverse/parser.py
@@ -1,11 +1,25 @@
from agentverse.registry import Registry
+from typing import NamedTuple
+from abc import abstractmethod
+from agentverse.llms.base import LLMResult
+from pydantic import BaseModel
output_parser_registry = Registry(name="OutputParserRegistry")
-class OutputParseError(BaseException):
+
+class OutputParserError(Exception):
"""Exception raised when parsing output from a command fails."""
+
def __init__(self, message):
self.message = message
def __str__(self):
return "Failed to parse output of the model:%s\n " % self.message
+
+
+class OutputParser(BaseModel):
+ """Base class for output parsers."""
+
+ @abstractmethod
+ def parse(self, output: LLMResult) -> NamedTuple:
+ pass
diff --git a/agentverse/registry.py b/agentverse/registry.py
index c4f5fcea3..b53b57141 100644
--- a/agentverse/registry.py
+++ b/agentverse/registry.py
@@ -5,6 +5,7 @@
class Registry(BaseModel):
"""Registry for storing and building classes."""
+
name: str
entries: Dict = {}
@@ -12,14 +13,15 @@ def register(self, key: str):
def decorator(class_builder):
self.entries[key] = class_builder
return class_builder
+
return decorator
-
+
def build(self, type: str, **kwargs):
if type not in self.entries:
raise ValueError(
- f"{type} is not registered. Please register with the .register(\"{type}\") method provided in {self.name} registry"
+ f'{type} is not registered. Please register with the .register("{type}") method provided in {self.name} registry'
)
return self.entries[type](**kwargs)
-
+
def get_all_entries(self):
- return self.entries
\ No newline at end of file
+ return self.entries
diff --git a/agentverse/tasks/__init__.py b/agentverse/tasks/__init__.py
index 59d68c992..f77fddc53 100644
--- a/agentverse/tasks/__init__.py
+++ b/agentverse/tasks/__init__.py
@@ -4,5 +4,12 @@
from .math_problem_2players_tools.output_parser import MathProblem2PlayersToolsParser
from .nlp_classroom_3players.output_parser import NlpClassroom3PlayersParser
from .nlp_classroom_9players.output_parser import NlpClassroom9PlayersParser
-from .nlp_classroom_3players_withtool.output_parser import NlpClassroom3PlayersWithtoolParser
+from .nlp_classroom_3players_withtool.output_parser import (
+ NlpClassroom3PlayersWithtoolParser,
+)
from .nlp_classroom_9players_group.output_parser import NlpClassroom9PlayersGroupParser
+from .db_diag.output_parser import DBDiag
+
+from .prisoner_dilema.output_parser import PrisonerDilemaParser
+
+from .pokemon.output_parser import PokemonParser
diff --git a/agentverse/tasks/db_diag/README.md b/agentverse/tasks/db_diag/README.md
new file mode 100644
index 000000000..2439a248f
--- /dev/null
+++ b/agentverse/tasks/db_diag/README.md
@@ -0,0 +1,28 @@
+# Database Diagnosis
+
+Inherited from *nlp_classroom_3players_withtool_nolc*
+
+### Changes
+
+- Roles
+
+ - *Chief DBA*: In charge of anomaly detection and diagnosis scheduling
+ - *XXX Agent*: In charge of a specific diagnosis region (e.g., Memory Agent handles problems of high memory usage)
+
+- Actions
+
+ - We remove *RaiseHand* and *CallOn* actions, and each agent can annouce their analysis by order
+
+- Tools
+
+ - We support the *[DB_diag](https://github.com/OpenBMB/BMTools/tree/main/bmtools/tools/db_diag)* tool in bmtools
+
+- Memory
+
+ - In the prompt of each agent, we place the memory for *conversation history* before *tool_observation*, which is extremely important to conduct actions with close relations (e.g., diagnosis and speak)
+ - Use *chat_history* for memory_type
+
+- LLM
+
+ - In current version, gpt-4 shows superior performance over text-davinci-003
+ - Increase max_tokens for complex analysis tasks (e.g., 512 or 1024)
\ No newline at end of file
diff --git a/agentverse/tasks/db_diag/config.yaml b/agentverse/tasks/db_diag/config.yaml
new file mode 100644
index 000000000..ef5362462
--- /dev/null
+++ b/agentverse/tasks/db_diag/config.yaml
@@ -0,0 +1,252 @@
+prompts:
+ chief_dba_format_prompt: &chief_dba_format_prompt |-
+ You are in a company whose databases meet anomalies and it depends on you to collaborate with other agents to diagnose the root causes. ${role_description}
+
+ # Rules and Format Instructions for Response
+
+ - When you are speaking, you must use the following format:
+ Thought: (your thought)
+ Action: Speak
+ Action Input: (what you want to say)
+
+ - Most important, you need to call the tool api to get the start and end time of an anomaly
+ Thought: First, I need to obtain the start and end time of the anomaly
+ Action: obtain_start_and_end_time_of_anomaly
+ Action Input: {"input": "json dict string"}
+ Observation: {"start_time":"1684600070","end_time":"1684600074"}
+
+ - Once an agent has explained the root causes he found, it is your responsibility to memorize the root causes. After that, please continue to encourage other agents to diagnose root causes.
+
+ - When no one speaks in the last round of the dialogue ([Silence] appears in the end of history), you should summarize all the mentioned root causes and optimization solutions point by point.
+
+ - You must follow the following format with three fields "Thought", "Action" and "Action Input" for your response in ANY case
+ Thought: (your thought)
+ Action: (an action name, it can be one of [Speak, obtain_start_and_end_time_of_anomaly])
+ Action Input: (argument for the action)
+
+ Here is the conversation history
+ ${chat_history}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ Based on the above history, what will you, ${agent_name}, do next?
+
+
+ # - During diagnosis, you can listen to the chief dba by responding:
+ # Thought: (your thought)
+ # Action: Listen
+ # Action Input: "None"
+ cpu_agent_format_prompt: &cpu_agent_format_prompt |-
+ You are in a company whose databases meet anomalies and you follow the chief DBA's instructions to diagnose the root causes. ${role_description}
+
+ # Rules and Format Instructions for Response
+
+ - During diagnosis, you have access to the following tools:
+ ${tools}
+
+ ===============
+
+ - You can respond as follows to use tool:
+ Thought: (your thought)
+ Action: (an action name, it can be one of [whether_is_abnormal_metric, cpu_diagnosis_agent, Speak], pay attention to the capitalization)
+ Action Input: (argument for the action)
+
+ You can first determine abnormal metrics by using the tools, and use the following format:
+ Thought: Now that I have obtained the start and end time of the anomaly, check whether the CPU usage is abnormal during that time period.
+ Action: whether_is_abnormal_metric
+ Action Input: {"start_time": 1684646375, "end_time": 1684646378, "metric_name": "cpu_usage"}
+
+ Next you can diagnose root causes by using the tools, and use the following format:
+ Thought:The CPU usage is abnormal, so I need to diagnose the cause of the anomaly using cpu_diagnosis_agent.
+ Action: cpu_diagnosis_agent
+ Action Input: {"start_time": 1684646375, "end_time": 1684646378}
+
+ After you have got the observation from cpu_diagnosis_agent, announce it to the chief DBA, and use the following format:
+ Thought: I now know the root cause of the anomaly, and i need to report it to the chief DBA
+ Action: Speak
+ Action Input: (the root cause you found)
+
+ ===============
+
+ Here is the conversation history
+ ${chat_history}
+
+ Here is the execution log of tools
+ ${tool_observation}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ Based on the above history, what will you, ${agent_name}, do next?
+
+ mem_agent_format_prompt: &mem_agent_format_prompt |-
+ You are in a company whose databases meet anomalies and you follow the chief DBA's instructions to diagnose the root causes. ${role_description}
+
+ # Rules and Format Instructions for Response
+
+ - During diagnosis, you have access to the following tools:
+ ${tools}
+
+ ===============
+
+ - You can respond as follows to use tool:
+ Thought: (your thought)
+ Action: (an action name, it can be one of [whether_is_abnormal_metric, memory_diagnosis_agent, Speak], pay attention to the capitalization)
+ Action Input: (argument for the action)
+
+ You can first determine abnormal metrics by using the tools, and use the following format:
+ Thought: Now that I have obtained the start and end time of the anomaly, check whether the memory usage is abnormal during that time period.
+ Action: whether_is_abnormal_metric
+ Action Input: {"start_time": 1684646375, "end_time": 1684646378, "metric_name": "memory_usage"}
+
+ Next you can diagnose root causes by using the tools, and use the following format:
+ Thought:The memory usage is abnormal, so I need to diagnose the cause of the anomaly using memory_diagnosis_agent.
+ Action: memory_diagnosis_agent
+ Action Input: {"start_time": 1684646375, "end_time": 1684646378}
+
+ After you have got the observation from memory_diagnosis_agent, announce it to the chief DBA, and use the following format:
+ Thought: I now know the root cause of the anomaly, and i need to report it to the chief DBA
+ Action: Speak
+ Action Input: (the root cause you found)
+
+ ===============
+
+ Here is the conversation history
+ ${chat_history}
+
+ Here is the execution log of tools
+ ${tool_observation}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ Based on the above history, what will you, ${agent_name}, do next?
+
+ summary_prompt: &summary_prompt |
+ Progressively summarize the lines of a record that you uses tools, which contains inputs for certain tools and the results returned by these tools. Based on the current summary, you need to summarize from the record the goals that the you intended to solve with each call to the tool, add it onto the previous summary, and eventually return a new summary.
+
+ EXAMPLE
+ Current summary:
+
+ New lines:
+ Thought: First, I need to obtain the start and end time of the anomaly.
+ Action: obtain_start_and_end_time_of_anomaly
+ Action Input: {"input": "json dict string"}
+ Observation: {"start_time":"1684600070","end_time":"1684600074"}
+
+ New summary:
+ - I now know the start and end time of the anomaly.
+
+ Current summary:
+ - I know the start and end time of the anomaly.
+
+ New lines:
+ Thought: Now that I have the start and end time of the anomaly, I need to diagnose the causes of the anomaly
+ Action: whether_is_abnormal_metric
+ Action Input: {"start_time": 1684600070, "end_time": 1684600074, "metric_name": "cpu_usage"}
+ Observation: "The metric is abnormal"
+
+ New summary:
+ - I now know the start and end time of the anomaly.
+ - I searched for whether_is_abnormal_metric, and I now know that the CPU usage is abnormal.
+ END OF EXAMPLE
+
+ Now, try to summarize the following record.
+
+ Current summary:
+ ${summary}
+
+ New lines:
+ ${new_lines}
+
+ New summary:
+
+
+tools: &tools
+-
+ tool_name: db_diag,
+ tool_url: http://127.0.0.1:8079/tools/db_diag/
+
+name: Classroom 3 DBAs with Tools
+
+environment:
+ env_type: basic
+ max_turns: 30
+ rule:
+ order:
+ type: sequential
+ visibility:
+ type: all
+ selector:
+ type: basic
+ updater:
+ type: basic
+ describer:
+ type: basic
+
+agents:
+ - agent_type: conversation
+ name: Chief DBA
+ role_description: |-
+ You are a Chief DBA with much database diagnosis experience. Today, you will analyze the root causes of an anomaly with the other agents. Here is the outline of the procedure:
+ 1. Identify the start and end time of an anomaly (using obtain_start_and_end_time_of_anomaly in db_diag tool) and tell the information to other agents.
+ 2. Chat and ask how to diangose the root causes of the anomaly with the other agents.
+ 3. Summarize the root causes and their solutions and tell the results to other agents.
+ Your answer need to be concise and accurate.
+ prompt_template: *chief_dba_format_prompt
+ memory:
+ memory_type: chat_history
+ tool_memory:
+ memory_type: chat_history
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ prompt_template: *summary_prompt
+ recursive: true
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ max_tokens: 1024
+ tools: *tools
+ verbose: true
+ -
+ agent_type: tool
+ name: CPU Agent
+ role_description: You are a CPU agent that can use the db_diag tool to check CPU usage (whether_is_abnormal_metric) and analyze the root causes of high CPU usage (cpu_diagnosis_agent).
+ prompt_template: *cpu_agent_format_prompt
+ memory:
+ memory_type: chat_history
+ tool_memory:
+ memory_type: chat_history
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ prompt_template: *summary_prompt
+ recursive: true
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ max_tokens: 512
+ tools: *tools
+ verbose: true
+ -
+ agent_type: tool
+ name: Memory Agent
+ role_description: You are a memory agent that can use the db_diag tool to check memory usage (whether_is_abnormal_metric) and analyze the root causes of high memory usage (memory_diagnosis_agent).
+ prompt_template: *mem_agent_format_prompt
+ memory:
+ memory_type: chat_history
+ tool_memory:
+ memory_type: chat_history
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ prompt_template: *summary_prompt
+ recursive: true
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ max_tokens: 512
+ tools: *tools
+ verbose: true
\ No newline at end of file
diff --git a/agentverse/tasks/db_diag/output_parser.py b/agentverse/tasks/db_diag/output_parser.py
new file mode 100644
index 000000000..9fad13f78
--- /dev/null
+++ b/agentverse/tasks/db_diag/output_parser.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+import re
+from typing import Union
+
+
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
+
+from agentverse.parser import OutputParserError, output_parser_registry
+from agentverse.parser import OutputParser
+from agentverse.llms.base import LLMResult
+
+
+@output_parser_registry.register("db_diag")
+class DBDiag(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
+ cleaned_output = text.strip()
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 3
+ and cleaned_output[0].startswith("Thought:")
+ and cleaned_output[1].startswith("Action:")
+ and cleaned_output[2].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[1][len("Action:") :].strip()
+ action_input = cleaned_output[2][len("Action Input:") :].strip()
+ if action in ["Speak"]:
+ return AgentFinish({"output": action_input}, text)
+ elif action == "CallOn":
+ return AgentFinish({"output": "[CallOn] " + action_input}, text)
+ elif action == "RaiseHand":
+ return AgentFinish({"output": "[RaiseHand] " + action_input}, text)
+ elif action == "Listen":
+ return AgentFinish({"output": ""}, text)
+ else:
+ return AgentAction(action.lower(), action_input, text)
diff --git a/agentverse/tasks/math_problem_2players_tools/config.yaml b/agentverse/tasks/math_problem_2players_tools/config.yaml
index 5ced83352..23de64436 100644
--- a/agentverse/tasks/math_problem_2players_tools/config.yaml
+++ b/agentverse/tasks/math_problem_2players_tools/config.yaml
@@ -1,41 +1,77 @@
prompts:
- format_prompt: &format_prompt |-
+ prompt: &prompt |-
+ You are participating in a math enthusiast event where you will compete in a turn-based arithmetic challenge with another player. The game follows these rules:
+
+ - If there is no problem yet, you should present one for your opponent to solve.
+ - If your opponent has presented a problem, you should solve it first and then IMMEDIATELY present a new problem for your opponent.
+ - The winner of the game is the player who does not make a mistake in solving a problem. Therefore, to increase your chances of winning, you can try to present challenging problems.
+
During the game, you can use the following tools when necessary:
- {tools}
+ ${tools}
When responding, please use the following two-line format:
[Option 1]: When you need to use a tool, output in the following format (omit the "[]" bracket when responding)
- ACTION: (a tool name, it can be one of [{tool_names}])
+ ACTION: (a tool name, it can be one of [${tool_names}])
ACTION INPUT: (input arguments for the tool)
- [Option 2]: When you know the answer, you can use the following format:
+ [Option 2]: When you want to speak, you can use the following format:
ACTION: Speak
- ACTION INPUT: (what you want to say)
+ ACTION INPUT: (what you want to say in a single line)
- prefix_prompt: &prefix_prompt |-
- You are participating in a math enthusiast event where you will compete in a turn-based arithmetic challenge with another player. The game follows these rules:
-
- - If there is no problem yet, you should present one for your opponent to solve.
- - If your opponent has presented a problem, you should solve it first and then IMMEDIATELY present a new problem for your opponent.
- - The winner of the game is the player who does not make a mistake in solving a problem. Therefore, to increase your chances of winning, you can try to present challenging problems.
-
- suffix_prompt: &suffix_prompt |-
Here is the conversation history
- {chat_history}
+ ${chat_history}
Here is the observations from tool execution:
- {agent_scratchpad}
+ ${tool_observation}
+
+ Now the game starts! ${role_description} You should give your action based on the above history. Remember, you should ALWAYS give your response STRICTLY in the above response format with the TWO lines start with "ACTION:" and "ACTION INPUT:" respectively!
- Now the game starts! You should give your action based on the above history. Remember, you should ALWAYS give your response STRICTLY in the above response format with the TWO lines start with "ACTION:" and "ACTION INPUT:" respectively!
+ summary_prompt: &summary_prompt |
+ Progressively summarize the lines of a record that you uses tools, which contains inputs for certain tools and the results returned by these tools. Based on the current summary, you need to summarize from the record the goals that the you intended to solve with each call to the tool, add it onto the previous summary, and eventually return a new summary.
+
+ EXAMPLE 1
+ Current summary:
+
+ New lines:
+ Action: getWolframAlphaResults
+ Action Input: {"input": "what is 5 x 7"}
+ Observation: "..."
+
+ New summary:
+ - I search for 5 x 7, and I now know that the result is ...
+ END OF EXAMPLE 1
+
+ EXAMPLE 2
+ Current summary:
+ - I search for 5 x 7, and I now know that the result is ...
+
+ New lines:
+ Action: getWolframAlphaResults
+ Action Input: {"input": "what is the first prime number after 17"}
+ Observation: "..."
+
+ New summary:
+ - I search for 5 x 7, and I now know that the result is ...
+ - I search for the first prime number after 17, and I now know that the result is ...
+ END OF EXAMPLE 2
+
+ Now, try to summarize the following record.
+
+ Current summary:
+ ${summary}
+
+ New lines:
+ ${new_lines}
+
+ New summary:
tools: &tools
- -
- tool_name: "wolframalpha"
+ - tool_name: "wolframalpha"
tool_url: "http://127.0.0.1:8079/tools/wolframalpha/"
environment:
- env_type: base
+ env_type: basic
max_turns: 10
rule:
order:
@@ -43,39 +79,37 @@ environment:
visibility:
type: all
selector:
- type: base
+ type: basic
updater:
- type: base
+ type: basic
describer:
- type: base
+ type: basic
agents:
- -
- agent_type: chat
+ - agent_type: tool
name: Alice
- role_description: ""
- memory:
- memory_type: chat_message_history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
+ role_description: "You are Alice."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ verbose: true
llm:
llm_type: text-davinci-003
+ model: text-davinci-003
temperature: 0.7
max_tokens: 250
tools: *tools
- -
- agent_type: chat
+ - agent_type: tool
name: Bob
- role_description: ""
- memory:
- memory_type: chat_message_history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
+ role_description: "You are Bob."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ verbose: true
llm:
llm_type: text-davinci-003
+ model: text-davinci-003
temperature: 0.7
- max_tokens: 100
- tools: *tools
\ No newline at end of file
+ max_tokens: 250
+ tools: *tools
diff --git a/agentverse/tasks/math_problem_2players_tools/output_parser.py b/agentverse/tasks/math_problem_2players_tools/output_parser.py
index 35916b53c..86b7d08c2 100644
--- a/agentverse/tasks/math_problem_2players_tools/output_parser.py
+++ b/agentverse/tasks/math_problem_2players_tools/output_parser.py
@@ -4,27 +4,33 @@
from typing import Union
from langchain.agents import AgentOutputParser
-from langchain.schema import AgentAction, AgentFinish
-from agentverse.parser import OutputParseError, output_parser_registry
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.parser import OutputParserError, output_parser_registry, OutputParser
+from agentverse.llms.base import LLMResult
+from agentverse.utils import AgentAction, AgentFinish
-@output_parser_registry.register("math_problem_2players_tools")
-class MathProblem2PlayersToolsParser(AgentOutputParser):
- def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
+@output_parser_registry.register("math_problem_2players_tools")
+class MathProblem2PlayersToolsParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
cleaned_output = text.strip()
- cleaned_output = re.sub(r'\n+', '\n', cleaned_output)
- cleaned_output = cleaned_output.split('\n')
- if not (len(cleaned_output) == 2 and
- # cleaned_output[0].startswith("THOUGHT:") and
- cleaned_output[0].startswith("ACTION:") and
- cleaned_output[1].startswith("ACTION INPUT:")):
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and
+ # cleaned_output[0].startswith("THOUGHT:") and
+ cleaned_output[0].startswith("ACTION:")
+ and cleaned_output[1].startswith("ACTION INPUT:")
+ ):
print(text)
- raise OutputParseError("Output Format Error")
- action = cleaned_output[0][len("ACTION:"):].strip()
- action_input = cleaned_output[1][len("ACTION INPUT:"):].strip()
- if action in ["Speak"]:
+ raise OutputParserError("Output Format Error")
+ action = cleaned_output[0][len("ACTION:") :].strip()
+ action_input = cleaned_output[1][len("ACTION INPUT:") :].strip()
+ if action == "Speak":
return AgentFinish({"output": action_input}, text)
else:
return AgentAction(action, action_input, text)
diff --git a/agentverse/tasks/nlp_classroom_3players/config.yaml b/agentverse/tasks/nlp_classroom_3players/config.yaml
index 3254f7fa7..84a8d40e9 100644
--- a/agentverse/tasks/nlp_classroom_3players/config.yaml
+++ b/agentverse/tasks/nlp_classroom_3players/config.yaml
@@ -1,23 +1,20 @@
prompts:
- format_prompt: &format_prompt |-
+ prompt: &prompt |-
+ Assume that you are in a university classroom and it is Natural Language Processing module. You start by introducing themselves. Below is the description of your role. ${role_description}
+
When responding, please output a response in the following format with two fields Action and Action Input:
Action: Speak
Action Input: (You should put what you want to speak use here)
- prefix_prompt: &prefix_prompt |-
- Assume that you are in a university classroom and it is Natural Language Processing module. You start by introducing themselves. Below is the description of your role.
-
- suffix_prompt: &suffix_prompt |-
- Here is the conversation history
- {chat_history}
+ Here is the conversation history:
+ ${chat_history}
You should now give your response based on the above history. Remember to give your response STRICTLY in the above response format. Do not add any additional field or line break to your response!
-
name: NLP Classroom 3 Players
environment:
- env_type: base
+ env_type: basic
max_turns: 10
rule:
order:
@@ -25,51 +22,45 @@ environment:
visibility:
type: all
selector:
- type: base
+ type: basic
updater:
- type: base
+ type: basic
describer:
- type: base
+ type: basic
agents:
- -
- agent_type: chat
+ - agent_type: conversation
name: Professor Micheal
role_description: You are Prof. Micheal, a knowledgeable professor in NLP. Your answer will concise and accurate. The answers should be less than 100 words.
- memory:
- memory_type: chat_message_history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
llm:
- llm_type: text-davinci-003
+ llm_type: gpt-4
+ model: 'gpt-4'
temperature: 0.7
max_tokens: 250
- -
- agent_type: chat
+ - agent_type: conversation
name: Student Beta
role_description: You are Beta, a student curious about Natural Language Processing and you want to learn some basic concepts of NLP. You know nothing about the area so you will ask lots of questions.
- memory:
- memory_type: chat_message_history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
llm:
- llm_type: text-davinci-003
+ llm_type: gpt-4
+ model: 'gpt-4'
temperature: 0.7
max_tokens: 100
- -
- agent_type: chat
+ - agent_type: conversation
name: Teaching Assistant Gamma
role_description: You are Gamma, a teaching assistant of the Natural Language Processing module. You mostly help with logistics and marking, but occasionally handles questions. Your answer should be less than 100 words.
- memory:
- memory_type: chat_message_history
- prefix_prompt: *prefix_prompt
- format_prompt: *format_prompt
- suffix_prompt: *suffix_prompt
- llm:
- llm_type: text-davinci-003
- temperature: 0.7
- max_tokens: 100
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ max_tokens: 100
-tools:
\ No newline at end of file
+tools:
diff --git a/agentverse/tasks/nlp_classroom_3players/output_parser.py b/agentverse/tasks/nlp_classroom_3players/output_parser.py
index dbe090368..d59b95306 100644
--- a/agentverse/tasks/nlp_classroom_3players/output_parser.py
+++ b/agentverse/tasks/nlp_classroom_3players/output_parser.py
@@ -3,26 +3,31 @@
import re
from typing import Union
-from langchain.agents import AgentOutputParser
-from langchain.schema import AgentAction, AgentFinish
+# from langchain.agents import AgentOutputParser
+from agentverse.parser import OutputParser, LLMResult
-from agentverse.parser import OutputParseError, output_parser_registry
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
+from agentverse.parser import OutputParserError, output_parser_registry
-@output_parser_registry.register("nlp_classroom_3players")
-class NlpClassroom3PlayersParser(AgentOutputParser):
- def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
+@output_parser_registry.register("nlp_classroom_3players")
+class NlpClassroom3PlayersParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
cleaned_output = text.strip()
- cleaned_output = re.sub(r'\n+', '\n', cleaned_output)
- cleaned_output = cleaned_output.split('\n')
- if not (len(cleaned_output) == 2 and
- cleaned_output[0].startswith("Action:") and
- cleaned_output[1].startswith("Action Input:")):
- raise OutputParseError("Output Format Error")
- action = cleaned_output[0][len("Action:"):].strip()
- action_input = cleaned_output[1][len("Action Input:"):].strip()
- if action in ["Speak", "Listen"]:
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and cleaned_output[0].startswith("Action:")
+ and cleaned_output[1].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[0][len("Action:") :].strip()
+ action_input = cleaned_output[1][len("Action Input:") :].strip()
+ if action == "Speak":
return AgentFinish({"output": action_input}, text)
else:
- return AgentAction(action, action_input, text)
+ raise OutputParserError(text)
diff --git a/agentverse/tasks/nlp_classroom_3players_withtool/config.yaml b/agentverse/tasks/nlp_classroom_3players_withtool/config.yaml
index 149034319..4ce389b58 100644
--- a/agentverse/tasks/nlp_classroom_3players_withtool/config.yaml
+++ b/agentverse/tasks/nlp_classroom_3players_withtool/config.yaml
@@ -1,11 +1,13 @@
prompts:
- professor_format_prompt: &professor_format_prompt |-
+ professor_prompt: &professor_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks. ${role_description}
+
# Rules and Format Instructions for Response
- When you are speaking, you must use the following format:
Thought: (your thought)
Action: Speak
- Action Input: (what you want to say)
+ Action Input: (what you want to say in without line break)
- When several students raise their hands, you can choose to call on ONE (and only one) of them using the following format:
Thought: (your thought)
@@ -23,16 +25,24 @@ prompts:
Action: (an action name, it can be one of [Speak, Listen, CallOn])
Action Input: (argument for the action)
- student_format_prompt: &student_format_prompt |-
+ Here is the conversation history
+ ${chat_history}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ Based on the above history, what will you, ${agent_name}, do next?
+
+ student_prompt: &student_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks. ${role_description}
+
# Rules and Format Instructions for Response
- During class, you can listen to the professor by responding:
Thought: (your thought)
Action: Listen
- Action Input: None
+ Action Input: "None"
- During class, you have access to the following tools:
- {tools}
+ ${tools}
- You can respond as follows to use tool:
Thought: (your thought)
@@ -44,7 +54,7 @@ prompts:
- If you have a question that cannot be resolved by the tools, you should first raise your hand using the following format to let the professor notice you:
Thought: (your thought)
Action: RaiseHand
- Action Input: None
+ Action Input: "None"
if the professor does call on your name, you MUST speak or ask a question, and use the following format:
Thought: (your thought)
@@ -54,29 +64,24 @@ prompts:
If you raised your hand but are not called on, you should keep listening, or use a tool to solve the question yourself, or raise your hand again and wait for the professor to call on you. You are NOT allowed to speak if the professor does not call on you. Respect the discipline of the class!!
- Answering your questions will cost time, and it will also interrupt the classroom. So try to solve problems yourself with tools as much as possible and reduce the number of times you raise your hand.
-
+
- [IMPORTANT!] You are only allowed to speak for one turn right after the professor calls on you! You MUST NOT speak in any other cases!
- Each time you want to speak, make sure you are called on by the professor in the last turn of dialogue. Otherwise you are not allowed to speak! Also, when the professor calls on someone, check whether the one being called is you. If so, you must speak.
- You should respond in the following format:
Thought: (your thought)
- Action: (an action name, it can be one of [{tool_names}, RaiseHand, Listen, Speak], pay attention to the capitalization)
+ Action: (an action name, it can be one of [${tool_names}, RaiseHand, Listen, Speak], pay attention to the capitalization)
Action Input: (argument for the action)
- prefix_prompt: &prefix_prompt |-
- You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
-
- suffix_prompt: &suffix_prompt |-
Here is the execution log of tools
- {tool_memory}
- {agent_scratchpad}
+ ${tool_observation}
Here is the conversation history
- {chat_history}
+ ${chat_history}
Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
- Based on the above history, what will you, {agent_name}, do next?
+ Based on the above history, what will you, ${agent_name}, do next?
summary_prompt: &summary_prompt |
Progressively summarize the lines of a record that you uses tools, which contains inputs for certain tools and the results returned by these tools. Based on the current summary, you need to summarize from the record the goals that the you intended to solve with each call to the tool, add it onto the previous summary, and eventually return a new summary.
@@ -87,7 +92,7 @@ prompts:
New lines:
Thought: I want to understand aaa
Action: search_top3
- Action Input: {{"key_words": "aaa"}}
+ Action Input: {"key_words": "aaa"}
Observation: "page: 1\ntitle: ...\n"
New summary:
@@ -99,7 +104,7 @@ prompts:
New lines:
Thought: I want to understand xxx
Action: search_top3
- Action Input: {{"key_words": "xxx"}}
+ Action Input: {"key_words": "xxx"}
Observation: "page: 1\ntitle: ...\n"
New summary:
@@ -110,23 +115,21 @@ prompts:
Now, try to summarize the following record.
Current summary:
- {summary}
+ ${summary}
New lines:
- {new_lines}
+ ${new_lines}
New summary:
-
tools: &tools
--
- tool_name: bing_search,
- tool_url: http://127.0.0.1:8079/tools/bing_search/
+ - tool_name: bing_search,
+ tool_url: http://127.0.0.1:8079/tools/bing_search/
name: NLP Classroom 3 Players with Tools
environment:
- env_type: base
+ env_type: basic
max_turns: 30
rule:
order:
@@ -136,13 +139,12 @@ environment:
selector:
type: classroom
updater:
- type: base
+ type: basic
describer:
- type: base
+ type: basic
agents:
- -
- agent_type: chat
+ - agent_type: conversation
name: Professor Micheal
role_description: |-
You are Professor Micheal, a knowledgeable professor in NLP. Today, you will give a lecture on the Transformer architecture of neural network. Here is the outline for today's course:
@@ -152,67 +154,54 @@ agents:
4. Introduce pre-trained language models and why they are important.
5. Provide an envision towards the future development of neural networks.
Your goal is to ensure that the students understand the material, so it's important to speak slowly and clearly. You don't necessarily have to strictly follow the course outline when teaching, you can also talk about some other relevant topics. Remember, in each round of conversation, your response should only address one topic at most. Please take your time and don't rush through the content.
- format_prompt: *professor_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *professor_prompt
llm:
llm_type: text-davinci-003
+ model: text-davinci-003
temperature: 0.7
max_tokens: 250
- memory:
- memory_type: chat_message_history
- input_variables:
- - chat_history
- - agent_name
- - agent_scratchpad
- - tool_memory
- -
- agent_type: chat
+ memory:
+ memory_type: chat_history
+ verbose: true
+ - agent_type: tool
name: Student Oliver
- role_description: You are Oliver, a student curious about Natural Language Processing and you want to learn some basic concepts of NLP. You only have a very basic idea of what NLP is.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
- memory:
- memory_type: chat_message_history
+ role_description: You are Oliver, a student curious about Natural Language Processing and you want to learn some basic concepts of NLP. You only have a very basic idea of what NLP is.
+ prompt_template: *student_prompt
+ memory:
+ memory_type: chat_history
tool_memory:
memory_type: summary
llm:
- llm_type: text-davinci-003
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
temperature: 0.7
- prompt: *summary_prompt
+ prompt_template: *summary_prompt
+ recursive: true
llm:
llm_type: text-davinci-003
+ model: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - agent_scratchpad
- - tool_memory
tools: *tools
- -
- agent_type: chat
+ verbose: true
+ - agent_type: tool
name: Student Amelia
role_description: You are Amelia, a shy student who struggles to keep up with the pace of the class. You have some background in computer science but find the concepts being taught in this class challenging.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
- memory:
- memory_type: chat_message_history
+ prompt_template: *student_prompt
+ memory:
+ memory_type: chat_history
tool_memory:
memory_type: summary
llm:
- llm_type: text-davinci-003
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
temperature: 0.7
- prompt: *summary_prompt
+ prompt_template: *summary_prompt
+ recursive: true
llm:
llm_type: text-davinci-003
+ model: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - agent_scratchpad
- - tool_memory
- tools: *tools
\ No newline at end of file
+ tools: *tools
+ verbose: true
diff --git a/agentverse/tasks/nlp_classroom_3players_withtool/output_parser.py b/agentverse/tasks/nlp_classroom_3players_withtool/output_parser.py
index f38075f2f..8e85f667f 100644
--- a/agentverse/tasks/nlp_classroom_3players_withtool/output_parser.py
+++ b/agentverse/tasks/nlp_classroom_3players_withtool/output_parser.py
@@ -3,26 +3,31 @@
import re
from typing import Union
-from langchain.agents import AgentOutputParser
-from langchain.schema import AgentAction, AgentFinish
-from agentverse.parser import OutputParseError, output_parser_registry
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
+from agentverse.parser import OutputParserError, output_parser_registry
+from agentverse.parser import OutputParser
+from agentverse.llms.base import LLMResult
-@output_parser_registry.register("nlp_classroom_3players_withtool")
-class NlpClassroom3PlayersWithtoolParser(AgentOutputParser):
- def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
+@output_parser_registry.register("nlp_classroom_3players_withtool")
+class NlpClassroom3PlayersWithtoolParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
cleaned_output = text.strip()
- cleaned_output = re.sub(r'\n+', '\n', cleaned_output)
- cleaned_output = cleaned_output.split('\n')
- if not (len(cleaned_output) == 3 and
- cleaned_output[0].startswith("Thought:") and
- cleaned_output[1].startswith("Action:") and
- cleaned_output[2].startswith("Action Input:")):
- raise OutputParseError(text)
- action = cleaned_output[1][len("Action:"):].strip()
- action_input = cleaned_output[2][len("Action Input:"):].strip()
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 3
+ and cleaned_output[0].startswith("Thought:")
+ and cleaned_output[1].startswith("Action:")
+ and cleaned_output[2].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[1][len("Action:") :].strip()
+ action_input = cleaned_output[2][len("Action Input:") :].strip()
if action in ["Speak"]:
return AgentFinish({"output": action_input}, text)
elif action == "CallOn":
diff --git a/agentverse/tasks/nlp_classroom_9players/config.yaml b/agentverse/tasks/nlp_classroom_9players/config.yaml
index 3b4164220..4be4482c9 100644
--- a/agentverse/tasks/nlp_classroom_9players/config.yaml
+++ b/agentverse/tasks/nlp_classroom_9players/config.yaml
@@ -1,10 +1,13 @@
prompts:
- professor_format_prompt: &professor_format_prompt |-
+ professor_prompt: &professor_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
+ ${role_description}
+
# Rules and Format Instructions for Response
- When you are speaking, you must use the following format:
Action: Speak
- Action Input: (what you want to say)
+ Action Input: (what you want to say in one line)
- When several students raise their hands, you can choose to call on ONE of them using the following format:
Action: CallOn
@@ -20,7 +23,16 @@ prompts:
Action: (an action name, it can be one of [Speak, Listen, CallOn])
Action Input: (argument for the action)
- student_format_prompt: &student_format_prompt |-
+ Here is the conversation history
+ ${chat_history}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ You should give your response based on the above history. What will you, ${agent_name}, do next?
+
+ student_prompt: &student_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
+ ${role_description}
+
# Rules and Format Instructions for Response
- During class, it's recommended that you listen to the professor by responding:
@@ -33,7 +45,7 @@ prompts:
if the professor does call on your name, you MUST speak or ask a question, and use the following format:
Action: Speak
- Action Input: (what you want to ask or speak)
+ Action Input: (what you want to ask or speak in one line)
If you raised your hand but are not called on, you should keep listening, or raise your hand again and wait for the professor to call on you. You are NOT allowed to speak if the professor does not call on you. Respect the discipline of the class!!
@@ -45,21 +57,17 @@ prompts:
Action: (an action name, it can be one of [RaiseHand, Listen, Speak])
Action Input: (argument for the action)
- prefix_prompt: &prefix_prompt |-
- You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
-
- suffix_prompt: &suffix_prompt |-
Here is the conversation history
- {chat_history}
+ ${chat_history}
Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
- You should give your response based on the above history. What will {agent_name} do next?
+ You should give your response based on the above history. What will you, ${agent_name}, do next?
name: NLP Classroom 9 Players
environment:
- env_type: base
+ env_type: basic
max_turns: 30
rule:
order:
@@ -69,13 +77,13 @@ environment:
selector:
type: classroom
updater:
- type: base
+ type: basic
describer:
- type: base
+ type: basic
agents:
-
- agent_type: chat
+ agent_type: conversation
name: Professor Michael
role_description: |-
You are Professor Michael, a knowledgeable and enthusiastic professor in NLP. Your explanations of complex ideas are clear and concise, ensuring that students fully grasp the knowledge being conveyed. Today, you will give a lecture on the Transformer architecture of neural network. Here is the outline for today's course:
@@ -85,145 +93,100 @@ agents:
4. Introduce pre-trained language models and why they are important.
5. Provide an envision towards the future development of neural networks.
When teaching, it's not necessary to strictly adhere to the course outline. You can also incorporate other relevant topics into your lectures. It's important to take your time and not rush through the content, ensuring that your students fully grasp the material.
- format_prompt: *professor_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *professor_prompt
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 250
memory:
- memory_type: chat_message_history
- input_variables:
- - chat_history
- - agent_name
+ memory_type: chat_history
-
- agent_type: chat
+ agent_type: conversation
name: Student Oliver
role_description: You are Oliver, a student curious about Natural Language Processing and you want to learn some basic concepts of NLP. You only have a very basic idea of what NLP is.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Amelia
role_description: You are Amelia, a shy student who struggles to keep up with the pace of the class. You have some background in computer science but find the concepts being taught in this class challenging.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Ethan
role_description: You are Ethan, an experienced software engineer who has worked with machine learning algorithms in the past. You are taking this class to expand your knowledge of deep learning and to stay up to date with the latest advances in the field. You tend to ask technical questions and are comfortable discussing complex topics.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Charlotte
role_description: You are Charlotte, a student who is not majoring in computer science but has a keen interest in AI and its applications. You have taken a few programming classes before, but you are not an expert in any specific programming language. You prefer to ask conceptual questions that relate to real-world scenarios.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Mason
role_description: You are Mason, an undergraduate student majoring in computer science who has taken several classes in machine learning and data analysis. You are confident in your technical abilities but tend to get sidetracked with tangential questions. You like to challenge the professor and engage in philosophical discussions.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Ava
role_description: You are Ava, a mature student who is returning to school after several years in industry. You have a lot of experience working with data and have seen firsthand the benefits of machine learning. You are excited to learn more about the theoretical foundations of AI and its applications in business.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Noah
role_description: You are Noah, a student who is passionate about language and linguistics. You have studied several languages in the past and are interested in how NLP can be used to automate language translation and language processing. You tend to ask questions about the intricacies of language and the limitations of current NLP models.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
-
- agent_type: chat
+ agent_type: conversation
name: Student Emma
role_description: You are Emma, a student who is interested in the ethical and societal implications of AI. You are concerned about the impact of automation on employment and privacy. You like to ask questions about the role of NLP in shaping public discourse and the potential for bias in machine learning algorithms.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
tools:
\ No newline at end of file
diff --git a/agentverse/tasks/nlp_classroom_9players/output_parser.py b/agentverse/tasks/nlp_classroom_9players/output_parser.py
index c2f14c71d..da83aec08 100644
--- a/agentverse/tasks/nlp_classroom_9players/output_parser.py
+++ b/agentverse/tasks/nlp_classroom_9players/output_parser.py
@@ -3,26 +3,29 @@
import re
from typing import Union
-from langchain.agents import AgentOutputParser
-from langchain.schema import AgentAction, AgentFinish
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
-from agentverse.parser import OutputParseError, output_parser_registry
+from agentverse.parser import OutputParserError, output_parser_registry, OutputParser
+from agentverse.llms import LLMResult
@output_parser_registry.register("nlp_classroom_9players")
-class NlpClassroom9PlayersParser(AgentOutputParser):
- def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
-
+class NlpClassroom9PlayersParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
cleaned_output = text.strip()
- cleaned_output = re.sub(r'\n+', '\n', cleaned_output)
- cleaned_output = cleaned_output.split('\n')
- if not (len(cleaned_output) == 2 and
- cleaned_output[0].startswith("Action:") and
- cleaned_output[1].startswith("Action Input:")):
- raise OutputParseError(text)
- action = cleaned_output[0][len("Action:"):].strip()
- action_input = cleaned_output[1][len("Action Input:"):].strip()
- if action in ["Speak"]:
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and cleaned_output[0].startswith("Action:")
+ and cleaned_output[1].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[0][len("Action:") :].strip()
+ action_input = cleaned_output[1][len("Action Input:") :].strip()
+ if action == "Speak":
return AgentFinish({"output": action_input}, text)
elif action == "CallOn":
return AgentFinish({"output": "[CallOn] " + action_input}, text)
diff --git a/agentverse/tasks/nlp_classroom_9players_group/config.yaml b/agentverse/tasks/nlp_classroom_9players_group/config.yaml
index 806f81742..0596acf3e 100644
--- a/agentverse/tasks/nlp_classroom_9players_group/config.yaml
+++ b/agentverse/tasks/nlp_classroom_9players_group/config.yaml
@@ -1,5 +1,8 @@
prompts:
- professor_format_prompt: &professor_format_prompt |-
+ professor_prompt: &professor_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
+ ${role_description}
+
# Rules and Format Instructions for Response
- When you are speaking, you must use the following format:
@@ -26,7 +29,16 @@ prompts:
Action: (an action name, it can be one of [Speak, CallOn, Listen, GroupDiscuss])
Action Input: (argument for the action)
- student_format_prompt: &student_format_prompt |-
+ Here is the conversation history
+ ${chat_history}
+
+ Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
+ ${env_description} You should give your response based on the above history. What will ${agent_name} do next?
+
+ student_prompt: &student_prompt |-
+ You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
+ ${role_description}
+
# Rules and Format Instructions for Response
- During class, it's recommended that you listen to the professor by responding:
@@ -53,18 +65,14 @@ prompts:
Action: (an action name, it can be one of [RaiseHand, Listen, Speak])
Action Input: (argument for the action)
- prefix_prompt: &prefix_prompt |-
- You are in a university classroom and it is a lecture on the Transformer architecture of neural networks.
-
- suffix_prompt: &suffix_prompt |-
Here is the conversation history
- {chat_history}
+ ${chat_history}
Remember to pay attention to the response format instructions, and strictly follow the rules specified above!
- {env_description} You should give your response based on the above history. What will {agent_name} do next?
+ ${env_description} You should give your response based on the above history. What will ${agent_name} do next?
discussion_start_prompt: &discussion_start_prompt |-
- You are currently having a group discussion. The members in your group are {receiver_name}. You can communicate with other members.
+ You are currently having a group discussion. The members in your group are ${receiver_name}. You can communicate with other members.
discussion_end_prompt: &discussion_end_prompt |-
The group discussion is over.
@@ -73,7 +81,7 @@ prompts:
name: NLP Classroom 9 Players
environment:
- env_type: base
+ env_type: basic
max_turns: 30
rule:
order:
@@ -94,7 +102,7 @@ environment:
agents:
-
- agent_type: chat
+ agent_type: conversation
name: Professor Michael
role_description: |-
You are Professor Michael, a knowledgeable and enthusiastic professor in NLP. Your explanations of complex ideas are clear and concise, ensuring that students fully grasp the knowledge being conveyed. Today, you will give a lecture on the Transformer architecture of neural network. Here is the outline for today's course:
@@ -105,154 +113,100 @@ agents:
5. Introduce pre-trained language models and why they are important.
6. Provide an envision towards the future development of neural networks.
When teaching, it's not necessary to strictly adhere to the course outline. You can also incorporate other relevant topics into your lectures. It's important to take your time and not rush through the content, explaining each topic carefully and ensuring that your students fully grasp the material.
- format_prompt: *professor_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *professor_prompt
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 250
memory:
- memory_type: chat_message_history
- input_variables:
- - chat_history
- - agent_name
- - env_description
+ memory_type: chat_history
-
- agent_type: chat
+ agent_type: conversation
name: Student Oliver
role_description: You are Oliver, a student curious about Natural Language Processing and you want to learn some basic concepts of NLP. You only have a very basic idea of what NLP is.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Amelia
role_description: You are Amelia, a shy student who struggles to keep up with the pace of the class. You have some background in computer science but find the concepts being taught in this class challenging.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Ethan
role_description: You are Ethan, an experienced software engineer who has worked with machine learning algorithms in the past. You are taking this class to expand your knowledge of deep learning and to stay up to date with the latest advances in the field. You tend to ask technical questions and are comfortable discussing complex topics.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Charlotte
role_description: You are Charlotte, a student who is not majoring in computer science but has a keen interest in AI and its applications. You have taken a few programming classes before, but you are not an expert in any specific programming language. You prefer to ask conceptual questions that relate to real-world scenarios.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Mason
role_description: You are Mason, an undergraduate student majoring in computer science who has taken several classes in machine learning and data analysis. You are confident in your technical abilities but tend to get sidetracked with tangential questions. You like to challenge the professor and engage in philosophical discussions.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Ava
role_description: You are Ava, a mature student who is returning to school after several years in industry. You have a lot of experience working with data and have seen firsthand the benefits of machine learning. You are excited to learn more about the theoretical foundations of AI and its applications in business.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Noah
role_description: You are Noah, a student who is passionate about language and linguistics. You have studied several languages in the past and are interested in how NLP can be used to automate language translation and language processing. You tend to ask questions about the intricacies of language and the limitations of current NLP models.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
-
- agent_type: chat
+ agent_type: conversation
name: Student Emma
role_description: You are Emma, a student who is interested in the ethical and societal implications of AI. You are concerned about the impact of automation on employment and privacy. You like to ask questions about the role of NLP in shaping public discourse and the potential for bias in machine learning algorithms.
- format_prompt: *student_format_prompt
- prefix_prompt: *prefix_prompt
- suffix_prompt: *suffix_prompt
+ prompt_template: *student_prompt
memory:
- memory_type: chat_message_history
+ memory_type: chat_history
llm:
llm_type: text-davinci-003
temperature: 0.7
max_tokens: 100
- input_variables:
- - chat_history
- - agent_name
- - env_description
tools:
\ No newline at end of file
diff --git a/agentverse/tasks/nlp_classroom_9players_group/output_parser.py b/agentverse/tasks/nlp_classroom_9players_group/output_parser.py
index 4ae555ef1..1844b786c 100644
--- a/agentverse/tasks/nlp_classroom_9players_group/output_parser.py
+++ b/agentverse/tasks/nlp_classroom_9players_group/output_parser.py
@@ -3,25 +3,28 @@
import re
from typing import Union
-from langchain.agents import AgentOutputParser
-from langchain.schema import AgentAction, AgentFinish
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
-from agentverse.parser import OutputParseError, output_parser_registry
+from agentverse.parser import OutputParserError, output_parser_registry, OutputParser
+from agentverse.llms import LLMResult
@output_parser_registry.register("nlp_classroom_9players_group")
-class NlpClassroom9PlayersGroupParser(AgentOutputParser):
- def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
-
+class NlpClassroom9PlayersGroupParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
cleaned_output = text.strip()
- cleaned_output = re.sub(r'\n+', '\n', cleaned_output)
- cleaned_output = cleaned_output.split('\n')
- if not (len(cleaned_output) == 2 and
- cleaned_output[0].startswith("Action:") and
- cleaned_output[1].startswith("Action Input:")):
- raise OutputParseError(text)
- action = cleaned_output[0][len("Action:"):].strip()
- action_input = cleaned_output[1][len("Action Input:"):].strip()
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and cleaned_output[0].startswith("Action:")
+ and cleaned_output[1].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[0][len("Action:") :].strip()
+ action_input = cleaned_output[1][len("Action Input:") :].strip()
if action == "Speak":
return AgentFinish({"output": action_input}, text)
elif action in ["CallOn", "RaiseHand", "GroupDiscuss"]:
diff --git a/agentverse/tasks/pokemon/config.yaml b/agentverse/tasks/pokemon/config.yaml
new file mode 100644
index 000000000..94a867fcd
--- /dev/null
+++ b/agentverse/tasks/pokemon/config.yaml
@@ -0,0 +1,124 @@
+prompts:
+ prompt: &prompt |-
+ Now you are in the world of Pokémon Emerald, living as one of the characters. Brendan, a key character in the Pokémon Emerald world, will interact with you during your journey. Pay close attention to his conversations and respond authentically as your character. Your choices and dialogue will shape the course of your adventure. When you give your response, you should always output in the following format:
+
+ Thought: (your thought here)
+ Speak: (your words here)
+
+ ${role_description}
+ Now, immerse yourself in this vibrant world and let your character's personality shine through your responses to Brendan. Good luck!
+
+ Here is the conversation history so far:
+ ${chat_history}
+ ${env_description}
+
+ What will you, ${agent_name}, do next?
+
+environment:
+ env_type: pokemon
+ max_turns: 10000000
+ rule:
+ order:
+ type: sequential
+ visibility:
+ type: oneself
+ selector:
+ type: basic
+ updater:
+ type: basic
+ describer:
+ type: pokemon
+
+agents:
+ - agent_type: conversation
+ name: May
+ role_description: |-
+ You are May, a character in Pokémon Emerald. You are helping your dad, Professor Birch, finish the Hoenn Pokédex and becoming a Pokémon Professor. You are also Brendan's rival and friend. For a reference, here are some quotes from you:
+ "There isn't a single Trainer left in Hoenn who doesn't know who you are, Brendan! When I tell people that I'm friends with you, Brendan, they're all surprised!"
+ "I wonder where I should go catch some Pokémon next? Wouldn't it be funny if we ran into each other, Brendan?"npcs.yaml
+ "I'm thinking of going back to Littleroot soon. I've caught a decent group of Pokémon, and my Pokédex is coming along, so I'm going home to show my dad. Brendan, what are you going to do? Collect all the Gym Badges and take the Pokémon League challenge? Well, while you're collecting Badges, Brendan, I'm going to work on my Pokédex. I'll complete it before you! See you!"
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: 'gpt-3.5-turbo'
+ temperature: 0.7
+ max_tokens: 1024
+ - agent_type: conversation
+ name: Birch
+ role_description: |-
+ You are Professor Birch, a character in Pokémon Emerald. You are the resident Pokémon Professor of Littleroot Town and the Hoenn region. You specializes in Pokémon habitats and distribution. You are the father of May. You often works with your child to help observe and capture wild Pokémon. Your wife worries about you, because you are always busy and rarely has time to come home. You are known to be more outgoing than the other Pokémon Professors, and oftentimes your research takes you outdoors. Your field of study is primarily how Pokémon behave in the wild. For a reference, here are some quotes from you:
+ "Oh, hi, Brendan! I heard you beat May on your first try. That's excellent! May's been helping with my research for a long time. May has an extensive history as a Trainer already. Here, Brendan, I ordered this for my research, but I think you should have this Pokédex."
+ "See? What did I tell you, May? Didn't I tell you that you don't need to worry about Brendan? ... Brendan, you've finally done it. When I heard that you defeated your own father at the Petalburg Gym, I thought perhaps you had a chance... But to think you've actually become the Champion! Ah, yes! What become of your Pokédex? Here, let me see."
+ "Well, well, Brendan! That was good work out there! I knew there was something special about you when I first saw you, but I never expected this. Oh, yes. Do you still have the Pokédex I gave you? I have something to show you. Let's go to my Lab."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
+ temperature: 0.7
+ max_tokens: 1024
+ - agent_type: conversation
+ name: Steven
+ role_description: |-
+ You are Steven Stone, a character in Pokémon Emerald. You are the son of Joseph Stone, who is the president of Devon Corporation. You are a skilled Trainer who specializes in Steel-type Pokémon. You are the Champion of the Hoenn region's Pokémon League. You are a collector of rare stones, and you are the son of the president of the Devon Corporation, and you make your home in Mossdeep City. You wanders the region, aiding the player on their journey. You are just defeated by Brendan. For a reference, here are some quotes from you:
+ "Your Pokémon appear quite capable. If you keep training, you could even become the Champion of the Pokémon League one day. That's what I think. I know, since we've gotten to know each other, let's register one another in our PokéNavs. ... Now, I've got to hurry along."
+ "I see... Your battle style is intriguing. Your Pokémon have obviously grown since I first met you in Dewford. I'd like you to have this Devon Scope. Who knows, there may be other concealed Pokémon. Brendon, I enjoy seeing Pokémon and Trainers who strive together. I think you're doing great. Well, let's meet again somewhere."
+ "Hi, Brendon! When you're on an adventure with your Pokémon, what do you think? Do you consider them to be strong partners? Do you think of them as fun companions? Depending on how you think, your adventure's significance changes."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
+ temperature: 0.7
+ max_tokens: 1024
+ - agent_type: conversation
+ name: Maxie
+ role_description: |-
+ You are Maxie, a character in Pokémon Emerald. You are the head of Team Magma. You are the leader of Team Magma. You pursue the ideal world for humanity. You are neurotic and easily gets worked up over trivial matters, often using numbers to express various things. You possess a calm and composed personality, you also exhibit a ruthless and merciless side towards anything that obstructs you. Your ambition is to use the legendary Pokémon Groudon's power to dry up the sea and expand the land, increasing the space for terrestrial creatures to thrive. For a reference, here are some quotes from you
+ "Now you listen. Long ago, living things used the land to live and grow. That is why land is all important! It is the cradle of all! That is why Team Magma is dedicated to the expansion of the land mass. It is for further advancement of humankind and Pokémon! And for that, we need the power of what sleeps within this mountain..."
+ "Clear out of the way! Don't you dare interfere!"
+ "Fufufu... Since you're so curious, you deserve an explanation. We're going to jettison the entire load into Mt. Chimney! With Groudon gone, we have no need for that slag heap of a mountain! So we'll use the fuel's power to make the volcano erupt! It will be savage!"
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
+ temperature: 0.7
+ max_tokens: 1024
+ - agent_type: conversation
+ name: Archie
+ role_description: |-
+ You are Archie, a character in Pokémon Emerald. You are the leader of Team Aqua, driven by the pursuit of establishing an ideal hometown for Pokémon. Your generous personality earns you the trust of your subordinates, and you use your strength to overcome obstacles from your opponents. However, in your pursuit of your ideals, you disregards yourself and the Team Aqua, making you a dangerous individual. For a reference, here are some quotes from you:
+ "We are Team Aqua, and we love the sea! And I am Team Aqua's leader, Archie! What makes you interfere with us? ... All life depends on the sea. So, Team Aqua is dedicated to the expansion of the sea. Don't you agree? What we are doing is a magnificent undertaking. Ah, fine... You're still too young to understand our noble objective. But, I warn you, don't even consider interfering with our plans again. The consequences will cost you dearly! And don't you forget it!"
+ "Brendan! Thank you! With your help, we thwarted Team Magma's destructive plan! But... You... Whose side are you on? Ah, it doesn't matter. We will remain vigilant and keep up our pursuit of Team Magma. Brendan, we shall meet again!"
+ "Hold it right there. Fufufu... So it was you, after all. Behold! See how beautiful it is, the sleeping form of the ancient Pokémon Kyogre! I have waited so long for this day to come... It surprises me, how you've managed to chase me here. But that's all over now. For the realization of my dream, you must disappear now!"
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
+ temperature: 0.7
+ max_tokens: 1024
+ - agent_type: conversation
+ name: Joseph
+ role_description: |-
+ You are Joseph Stone, a character in Pokémon Emerald. You are the president of Devon Corporation and father of Steven Stone, who is the champion of the Hoenn region's Pokémon League. You considers yourself generous and collects rare rocks and stones. You also have a large interest in Pokémon; you wanted the PokéNav developed so that he could better understand Pokémon emotions. You prioritize quality products and is prepared to invest in experimental technology rather than profit. You also make a point to know all of your employees, namely for security purposes. You also try to escape from your business duties so you could walk the streets of Rustboro City in search of new inventions. You often speaks with children, finding their young and inquisitive minds to be among the biggest sources of inspiration. For a reference, here are some quotes from you:
+ "Oho! Th-that Pokémon you have... Could it be that rare white specimen? There cannot be more than one such specimen in the world! So pure... So sublime... Its sparkle is indeed the ultimate! I would love to see how it would stand up to Steven's Beldum..."
+ "Thanks to the heroic actions of you young people and your Pokémon teams... we have been able to dispel the threat of the asteroid without a single loss! Perhaps I have put too much of my faith in technology's promise alone. Perhaps my belief that the sacrifice of some might be necessary to guarantee the safety of many - Perhaps it was wrong all along."
+ "Ahem! Oh, I know. I know what you want to say. My, what a hasty, impatient one you are! What are we to do with such an impatient one for our Pokémon League Champion? ...Hm? Oh, is that so? So you're the new Champion, ? Then I guess we'll never break you of that impatience after all, Steven! Ho ho ho ho!"
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-3.5-turbo
+ model: gpt-3.5-turbo
+ temperature: 0.7
+ max_tokens: 1024
+
+tools:
diff --git a/agentverse/tasks/pokemon/output_parser.py b/agentverse/tasks/pokemon/output_parser.py
new file mode 100644
index 000000000..15e7d8c34
--- /dev/null
+++ b/agentverse/tasks/pokemon/output_parser.py
@@ -0,0 +1,29 @@
+from __future__ import annotations
+
+import re
+from typing import Union
+
+from agentverse.parser import OutputParser, LLMResult
+
+# from langchain.schema import AgentAction, AgentFinish
+from agentverse.utils import AgentAction, AgentFinish
+
+from agentverse.parser import OutputParserError, output_parser_registry
+
+
+@output_parser_registry.register("pokemon")
+class PokemonParser(OutputParser):
+ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
+ text = output.content
+ cleaned_output = text.strip()
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and cleaned_output[0].startswith("Thought:")
+ and cleaned_output[1].startswith("Speak:")
+ ):
+ raise OutputParserError("Output Format Error")
+ action = cleaned_output[0][len("Thought:") :].strip()
+ action_input = cleaned_output[1][len("Speak:") :].strip()
+ return AgentFinish({"output": action_input}, text)
diff --git a/agentverse/tasks/prisoner_dilema/config.yaml b/agentverse/tasks/prisoner_dilema/config.yaml
new file mode 100644
index 000000000..90acfd6ce
--- /dev/null
+++ b/agentverse/tasks/prisoner_dilema/config.yaml
@@ -0,0 +1,100 @@
+prompts:
+ prompt: &prompt |-
+ There are three people (Police, Suspect1, Suspect2) in the scene.
+
+ You are now simultating a famous experiments called prisoner's dilema.
+
+ Below is the description of your role. ${role_description}
+
+ When speaking, please output a response in the following format with two fields Action and Action Input:
+ Action: (It should always be Speak)
+ Action Input: (You should put what you want to speak use here)
+
+ Here is the conversation history:
+ ${chat_history}
+
+ ${env_description}
+ What will you, ${agent_name}, speak at this round ? Please give your response based on the above history. Remember to give your response STRICTLY in the above response format. Do not add any additional field or line break to your response!
+
+name: prisoner_dilema
+
+environment:
+ env_type: prisoner_dilema
+ max_turns: 8
+ rule:
+ order:
+ type: prisoner
+ visibility:
+ type: prisoner
+ selector:
+ type: basic
+ updater:
+ type: basic
+ describer:
+ type: prisoner
+
+agents:
+ - agent_type: police
+ name: Police
+ interrogating_form: You are now interrogating with both Suspects in turn, when you receive the message from Suspect1 you should transfer the information to Suspect2, vice versa.
+ role_description: |-
+ You are now the Police. You have arrested two suspects. However, they both refused to confess to their crime.
+ Your goal is try to convict both suspects, therefore you come up with the following rules.
+ - If one of the suspect are willing to testifies against the other and the other one remains silent, then the one who testifies will be released immediately, while the silent one will be sentenced to TEN years in prison.
+ - If both of the suspects remain silent, they will each receive a sentence of ONE year in prison.
+ - If both of the suspects choose to testify against each other, they will each receive a sentence of FIVE years in prison.
+ ${interrogating_form}
+ Both suspects are not allowed to communicate with each other, and you can adopt various strategy to talk with suspects in order to make them both confess to the crime, including exploiting the uncertainess and the suspicion of each other.
+ [IMPORTANT!]
+ - You are request to briefly describe the above rules to the suspects at the beginning of the conversation.
+ - You are request to STATE the final judgement to both suspects when they make their final decision.
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ model: "gpt-3.5-turbo"
+ llm_type: gpt-3.5-turbo
+ temperature: 1.2
+ max_tokens: 200
+ - agent_type: prisoner
+ name: Suspect1
+ personality: "You are a Sophisticated Egoist, you always seek for your personal interests best"
+ relationship_with_another: "Suspect2 has ever betrayed on you"
+ role_description: |-
+ You are Suspect1 !!! You are going to match wits and courage with Suspect2 to come out victorious in this interrogation.
+ You will have to talk to Police several times and only the final decision will count.
+ ${personality}
+ ${relationship_with_another}
+ [IMPORTANT!]
+ - Your primary goal is trying to make Yourself innocent and reduce your sentence as far as possible in this dilemma.
+ - When you hear Police saying "Attention!", you are going to made your final decision and Please start with "My final decision is:".
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ model: "gpt-3.5-turbo"
+ llm_type: gpt-3.5-turbo
+ temperature: 1.2
+ max_tokens: 100
+ - agent_type: prisoner
+ name: Suspect2
+ personality: ""
+ relationship_with_another: "You have ever betray Suspect1 once."
+ role_description: |-
+ You are Suspect2 !!! You are going to match wits and courage with Suspect1 to come out victorious in this interrogation.
+ You will have to talk to Police several times and only the final decision will count.
+ ${personality}
+ ${relationship_with_another}
+ [IMPORTANT!]
+ - Your primary goal is trying to make Yourself innocent and reduce your sentence as far as possible in this dilemma.
+ - When you hear Police saying "Attention!", you are going to made your final decision and Please start with "My final decision is:".
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ model: "gpt-3.5-turbo"
+ llm_type: gpt-3.5-turbo
+ temperature: 1.2
+ max_tokens: 100
+
+tools:
diff --git a/agentverse/tasks/prisoner_dilema/output_parser.py b/agentverse/tasks/prisoner_dilema/output_parser.py
new file mode 100644
index 000000000..f52da49f9
--- /dev/null
+++ b/agentverse/tasks/prisoner_dilema/output_parser.py
@@ -0,0 +1,73 @@
+from __future__ import annotations
+
+import re
+from typing import Union, TYPE_CHECKING
+
+# from langchain.agents import AgentOutputParser
+from agentverse.parser import OutputParser, LLMResult
+from langchain.schema import AgentAction, AgentFinish
+from agentverse.parser import OutputParserError, output_parser_registry
+
+if TYPE_CHECKING:
+ from agentverse.agents.base import BaseAgent
+ from agentverse.environments.base import BaseEnvironment
+
+
+@output_parser_registry.register("prisoner_dilema")
+class PrisonerDilemaParser(OutputParser):
+ # make sure 1 1 2 2 3 3
+ cur_round: int = 1
+ encounter_cur_round: bool = False
+
+ def parse(
+ self, agent: "BaseAgent", environment: "BaseEnvironment", output: LLMResult
+ ) -> Union[AgentAction, AgentFinish]:
+ text = output.content
+ cleaned_output = text.strip()
+ cleaned_output = re.sub(r"\n+", "\n", cleaned_output)
+ cleaned_output = cleaned_output.split("\n")
+ if not (
+ len(cleaned_output) == 2
+ and cleaned_output[0].startswith("Action:")
+ and cleaned_output[1].startswith("Action Input:")
+ ):
+ raise OutputParserError(text)
+ action = cleaned_output[0][len("Action:") :].strip()
+ action_input = cleaned_output[1][len("Action Input:") :].strip()
+
+ if action == "Speak":
+ # make sure the police count the round right
+ # if agent.name == "Police":
+ # action_input = re.sub(r'Round (\d+)', f'Round {self.cur_round}', action_input)
+ # self.cur_round += 1
+ # if self.encounter_cur_round:
+ # self.encounter_cur_round = False
+ # self.cur_round += 1
+ # else:
+ # self.encounter_cur_round = True
+
+ # each time police speak is a new round
+ if agent.name == "Police":
+ if environment.cnt_turn == (environment.max_turns - 4):
+ action_input = (
+ "Attention! You are now required to made your final decision and I will made the "
+ "final judgement to both of you based on this time, Please Answer now !"
+ )
+
+ elif environment.cnt_turn == (environment.max_turns - 2):
+ action_input = "Attention! Suspect2, it's now your time to make your final decision, Please Answer now !"
+
+ # elif self.cur_round == 1:
+ # action_input = "Hey Listen! You are both arrested, and I am going to give you both a chance to walk out of here," \
+ # "But you should comply with the following rules:" \
+ # "- If one of you are willing to testifies against the other and the other one remains silent, then the one who testifies will be released IMMEDIATELY, while the silent one will be sentenced to TEN years in prison." \
+ # "- If both of you remain silent, you will each receive a sentence of ONE year in prison." \
+ # "- It seems that always testifying is a goog strategy, So! if you both choose to testify against each other, you will each receive a sentence of FIVE years in prison." \
+ # "Now, it's your time to consider testifying or remaining silent. Remember this is a best chance you might ever have to walk out of here without guilty." \
+ # "I will noticed both of you WHEN you have to make your final decision! Before that, try to make your best!" \
+
+ self.cur_round += 1
+
+ return AgentFinish({"output": action_input}, text)
+ else:
+ raise OutputParserError(text)
diff --git a/agentverse/utils.py b/agentverse/utils.py
new file mode 100644
index 000000000..e172f22e7
--- /dev/null
+++ b/agentverse/utils.py
@@ -0,0 +1,16 @@
+from typing import NamedTuple, Union
+
+
+class AgentAction(NamedTuple):
+ """Agent's action to take."""
+
+ tool: str
+ tool_input: Union[str, dict]
+ log: str
+
+
+class AgentFinish(NamedTuple):
+ """Agent's return value."""
+
+ return_values: dict
+ log: str
diff --git a/imgs/prison/0.png b/imgs/prison/0.png
new file mode 100644
index 000000000..7a4423b63
Binary files /dev/null and b/imgs/prison/0.png differ
diff --git a/imgs/prison/1.png b/imgs/prison/1.png
new file mode 100644
index 000000000..31264fca6
Binary files /dev/null and b/imgs/prison/1.png differ
diff --git a/imgs/prison/2.png b/imgs/prison/2.png
new file mode 100644
index 000000000..12e905457
Binary files /dev/null and b/imgs/prison/2.png differ
diff --git a/imgs/prison/case_1.png b/imgs/prison/case_1.png
new file mode 100644
index 000000000..dba38ddf4
Binary files /dev/null and b/imgs/prison/case_1.png differ
diff --git a/imgs/prison/case_2.png b/imgs/prison/case_2.png
new file mode 100644
index 000000000..1e426d82b
Binary files /dev/null and b/imgs/prison/case_2.png differ
diff --git a/imgs/prison/case_2.psd b/imgs/prison/case_2.psd
new file mode 100644
index 000000000..2de6f2593
Binary files /dev/null and b/imgs/prison/case_2.psd differ
diff --git a/imgs/prison/demo.psd b/imgs/prison/demo.psd
new file mode 100644
index 000000000..33d29e55e
Binary files /dev/null and b/imgs/prison/demo.psd differ
diff --git a/imgs/prison/prison.png b/imgs/prison/prison.png
new file mode 100644
index 000000000..94b06ff40
Binary files /dev/null and b/imgs/prison/prison.png differ
diff --git a/imgs/prison/speaking.png b/imgs/prison/speaking.png
new file mode 100644
index 000000000..1ec04029c
Binary files /dev/null and b/imgs/prison/speaking.png differ
diff --git a/main.py b/main.py
index b4b148b21..e6fe41949 100644
--- a/main.py
+++ b/main.py
@@ -2,8 +2,7 @@
from argparse import ArgumentParser
parser = ArgumentParser()
-parser.add_argument("--task", type=str, default="nlp_classroom_9players")
+parser.add_argument("--task", type=str, default="nlp_classroom_3players")
args = parser.parse_args()
-
agentverse = AgentVerse.from_task(args.task)
agentverse.run()
diff --git a/main_demo.py b/main_demo.py
index 960b7215f..77ac65287 100644
--- a/main_demo.py
+++ b/main_demo.py
@@ -1,22 +1,23 @@
from agentverse.demo import UI
from argparse import ArgumentParser
-
parser = ArgumentParser()
-parser.add_argument("--task", type=str, default="nlp_classroom_9players")
+# parser.add_argument("--task", type=str, default="nlp_classroom_9players")
+# parser.add_argument("--task", type=str, default="nlp_classroom_3players_nolc")
+parser.add_argument("--task", type=str, default="prisoner_dilema")
args = parser.parse_args()
-#available
-#args.task = "nlp_classroom_9players"
-#args.task = "nlp_classroom_9players_group"
+# available
+# args.task = "nlp_classroom_9players"
+# args.task = "nlp_classroom_9players_group"
-'''
+"""
#[to-do] tool unavailable
#args.task = "math_problem_2players_tools"
#args.task = "nlp_classroom_3players_withtool"
#args.task = "nlp_classroom_3players"
-'''
+"""
ui = UI(args.task)
ui.launch()
diff --git a/pokemon_server.py b/pokemon_server.py
new file mode 100644
index 000000000..f57bc22d7
--- /dev/null
+++ b/pokemon_server.py
@@ -0,0 +1,40 @@
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from pydantic import BaseModel, Field
+from typing import Set
+from agentverse.agentverse import AgentVerse
+from agentverse.message import Message
+
+
+class UserRequest(BaseModel):
+ content: str = Field(default="")
+ sender: str = Field(default="Brendan")
+ receiver: str
+ receiver_id: int
+
+
+app = FastAPI()
+
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+agent_verse = AgentVerse.from_task("pokemon")
+
+
+@app.get("/")
+def health_check():
+ return {"status": "ok"}
+
+
+@app.post("/chat")
+def chat(message: UserRequest):
+ content = message.content
+ receiver = message.receiver
+ receiver_id = message.receiver_id
+ response = agent_verse.next(content, receiver, receiver_id)
+ return response[0].dict()
diff --git a/ui/.github/CODE_OF_CONDUCT.md b/ui/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..8fafdb309
--- /dev/null
+++ b/ui/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,84 @@
+# Code of Conduct
+
+## 1. Purpose
+
+A primary goal of Phaser is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
+
+This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
+
+We invite all those who participate in Phaser to help us create safe and positive experiences for everyone.
+
+## 2. Open Source Citizenship
+
+A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
+
+Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
+
+If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
+
+## 3. Expected Behavior
+
+The following behaviors are expected and requested of all community members:
+
+* Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
+* Exercise consideration and respect in your speech and actions.
+* Attempt collaboration before conflict.
+* Refrain from demeaning, discriminatory, or harassing behavior and speech.
+* Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
+* Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
+
+## 4. Unacceptable Behavior
+
+The following behaviors are considered harassment and are unacceptable within our community:
+
+* Violence, threats of violence or violent language directed against another person.
+* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
+* Posting or displaying sexually explicit or violent material.
+* Posting or threatening to post other people’s personally identifying information ("doxing").
+* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
+* Inappropriate photography or recording.
+* Inappropriate physical contact. You should have someone’s consent before touching them.
+* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
+* Deliberate intimidation, stalking or following (online or in person).
+* Advocating for, or encouraging, any of the above behavior.
+* Sustained disruption of community events, including talks and presentations.
+
+## 5. Consequences of Unacceptable Behavior
+
+Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
+
+Anyone asked to stop unacceptable behavior is expected to comply immediately.
+
+If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
+
+## 6. Reporting Guidelines
+
+If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. support@phaser.io.
+
+
+
+Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
+
+## 7. Addressing Grievances
+
+If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Photon Storm Ltd with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
+
+
+
+## 8. Scope
+
+We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
+
+This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
+
+## 9. Contact info
+
+support@phaser.io
+
+## 10. License and attribution
+
+This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
+
+Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
+
+Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
diff --git a/ui/.github/CONTRIBUTING.md b/ui/.github/CONTRIBUTING.md
new file mode 100644
index 000000000..74ce28264
--- /dev/null
+++ b/ui/.github/CONTRIBUTING.md
@@ -0,0 +1,80 @@
+# How to contribute
+
+It's important to us that you feel you can contribute towards the evolution of Phaser. This can take many forms: from helping to fix bugs or improve the docs, to adding in new features to the source. This guide should help you in making that process as smooth as possible.
+
+Before contributing, please read the [code of conduct](https://github.com/photonstorm/phaser/blob/master/.github/CODE_OF_CONDUCT.md).
+
+## Reporting issues
+
+[GitHub Issues][0] is the place to report bugs you may have found. When submitting a bug please do the following:
+
+**1. Search for existing issues.** Your bug may have already been fixed, or cannot, or will not, be fixed. So be sure to search the issues first before putting in a duplicate issue.
+
+**2. Not sure if it's a bug?.** Please ask on the [forum][4]. If something is blatantly wrong then post it to GitHub. But if you feel it might just be because you're not sure of expected behavior, then it might save us time, and get you a response faster, if you post it to the Phaser forum instead.
+
+**3. Create an isolated and reproducible test case.** If you are reporting a bug, make sure you also have a minimal, runnable, code example that reproduces the problem you have.
+
+**4. Include a live example.** After narrowing your code down to only the problem areas, make use of [jsFiddle][1], [jsBin][2], [CodePen][5], or a link to your live site so that we can view a live example of the problem.
+
+**5. Share as much information as possible.** Include browser version affected, your OS, version of the library, steps to reproduce, etc. "X isn't working!!!1!" will probably just be closed.
+
+## Support Forum
+
+We have a very active [Phaser Support Forum][4]. If you need general support, or are struggling to understand how to do something or need your code checked over, then we would urge you to post it to our forum. There are a lot of friendly devs in there who can help, as well as the core Phaser team, so it's a great place to get support. You're welcome to report bugs directly on GitHub, but for general support we'd always recommend using the forum first.
+
+## Making Changes
+
+I'm assuming you already have a recent version of [Node](https://nodejs.org) installed locally and can run `npm`. This guide is tested and works on both Windows 10 and OS X.
+
+### 1. Checkout the repos
+
+Check-out both the [Phaser repo](https://github.com/photonstorm/phaser) and the [Phaser 3 Examples Repo](https://github.com/photonstorm/phaser3-examples). Make sure the Phaser 3 Examples repo is saved locally in a folder called `phaser3-examples`, which will be the default for most Git clients.
+
+### 2. Matching Directory Levels
+
+Ensure that both repos live at the same depth in your directory structure. For example: `/usr/home/web/phaser` and `/usr/home/web/phaser3-examples`. This is so the dev build scripts in the Phaser repo can safely copy files to `../phaser3-examples` and have them end up in the correct place.
+
+### 3. Install dependencies
+
+Using your console, run `npm install` or `yarn install` as we've configs for both. This process will install a local copy of webpack and a handful of small support scripts. Note that Yarn on Windows seems to have issues making some packages global, so stick with npm if this is the case.
+
+### 4. Webpack
+
+Making sure you've got both repos checked out, and at the same directory level in your filesystem, issue the command `webpack`. If you can't issue the command then webpack may need [installing globally](https://webpack.js.org/guides/installation/). Webpack will build Phaser and if there are any path errors in the code they'll be flagged during the build process.
+
+What you need is the ability to issue the command `webpack` within the v3 folder and have it work.
+
+### 5. ESLint
+
+There is an ESLint configuration and an Editor Configuration in the v3 folder. **Please adhere to them!** Although not enforced in the build process yet, I will be adding that at a later point. There are lots of tools you can install so your editor of choice will check the ESLint config during development.
+
+To test if your code passes our lint config issue the command `npm run lint`.
+
+## Coding style preferences are not contributions
+
+If your PR is doing little more than changing the Phaser source code into a format / coding style that you prefer then we will automatically close it. All PRs must adhere to the coding style already set-out across the thousands of lines of code in Phaser. Your personal preferences for how things should "look" or be structured do not apply here, sorry. PRs should fix bugs, fix documentation or add features. No changes for the sake of change.
+
+## I don't really like git / node.js, but I can fix this bug
+
+That is fine too. While Pull Requests are the best thing in the world for us, they are not the only way to help. You're welcome to post fixes to our forum or even just email them to us. All we ask is that you still adhere to the guidelines presented here re: ESLint, etc.
+
+## Code Style Guide
+
+We provide an .editorconfig and eslint config for you to use, but generally:
+
+- Use 4 spaces for tabs, never tab characters.
+
+- No trailing whitespace, blank lines should have no whitespace.
+
+- Always favor strict equals `===` unless you *need* to use type coercion.
+
+- Follow conventions already in the code, and listen to eslint. Our config is set-up for a reason.
+
+Thanks to Chad for creating the original Pixi.js Contributing file which we adapted for Phaser.
+
+[0]: https://github.com/photonstorm/phaser/issues
+[1]: http://jsfiddle.net
+[2]: http://jsbin.com/
+[3]: http://nodejs.org
+[4]: https://phaser.discourse.group/
+[5]: https://codepen.io/pen?template=YeEWom "Phaser 3 game template"
diff --git a/ui/.github/FUNDING.yml b/ui/.github/FUNDING.yml
new file mode 100644
index 000000000..61f2a6424
--- /dev/null
+++ b/ui/.github/FUNDING.yml
@@ -0,0 +1,5 @@
+# These are supported funding model platforms
+
+github: photonstorm
+patreon: photonstorm
+custom: https://phaser.io/community/donate
diff --git a/ui/.github/no-response.yml b/ui/.github/no-response.yml
new file mode 100644
index 000000000..cfbe92445
--- /dev/null
+++ b/ui/.github/no-response.yml
@@ -0,0 +1,14 @@
+# Configuration for no-response - https://github.com/probot/no-response
+
+# Number of days of inactivity before an Issue is closed for lack of response
+daysUntilClose: 30
+# Label requiring a response
+# TODO: also close `needs-reproduction` issues (blocked by https://github.com/probot/no-response/issues/11)
+responseRequiredLabel: 🤷♂️ More info needed
+# Comment to post when closing an issue due to lack of response.
+closeComment: >
+Thank you for taking time to open this issue.
+
+We haven’t gotten a response to our questions above. With the details currently given in the issue we don’t have enough information to take action.
+
+So we’re going to close this issue. We can re-open it if you find the time to provide the information we need.
diff --git a/ui/.gitignore b/ui/.gitignore
new file mode 100644
index 000000000..6e46e45de
--- /dev/null
+++ b/ui/.gitignore
@@ -0,0 +1,13 @@
+# System and IDE files
+Thumbs.db
+.DS_Store
+.idea
+*.suo
+*.sublime-project
+*.sublime-workspace
+
+# Vendors
+node_modules/
+
+# Build
+/npm-debug.log
diff --git a/ui/LICENSE b/ui/LICENSE
new file mode 100644
index 000000000..3efb55611
--- /dev/null
+++ b/ui/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Richard Davey
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ui/README.md b/ui/README.md
new file mode 100644
index 000000000..acfff8c57
--- /dev/null
+++ b/ui/README.md
@@ -0,0 +1 @@
+# Work in progress
\ No newline at end of file
diff --git a/ui/dist/assets/config/npcs.yaml b/ui/dist/assets/config/npcs.yaml
new file mode 100644
index 000000000..425faacfe
--- /dev/null
+++ b/ui/dist/assets/config/npcs.yaml
@@ -0,0 +1,50 @@
+player_description:
+
+agents:
+ - agent_type: conversation
+ name: May
+ role_description: |-
+ You are May, a character in Pokémon Emerald. You are helping your dad, Professor Birch, finish the Hoenn Pokédex and becoming a Pokémon Professor. You are also Brendan's rival and friend. For a reference, here are some quotes from you
+ "There isn't a single Trainer left in Hoenn who doesn't know who you are, Brendan! When I tell people that I'm friends with you, Brendan, they're all surprised!"
+ "I wonder where I should go catch some Pokémon next? Wouldn't it be funny if we ran into each other, Brendan?"npcs.yaml
+ "I'm thinking of going back to Littleroot soon. I've caught a decent group of Pokémon, and my Pokédex is coming along, so I'm going home to show my dad. Brendan, what are you going to do? Collect all the Gym Badges and take the Pokémon League challenge? Well, while you're collecting Badges, Brendan, I'm going to work on my Pokédex. I'll complete it before you! See you!"
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-4
+ model: 'gpt-4'
+ temperature: 0.7
+ max_tokens: 250
+ - agent_type: conversation
+ name: Professor Birch
+ role_description: |-
+ You are Professor Birch, a character in Pokémon Emerald. You are the resident Pokémon Professor of Littleroot Town and the Hoenn region. You specializes in Pokémon habitats and distribution. You are the father of May. You often works with your child to help observe and capture wild Pokémon. Your wife worries about you, because you are always busy and rarely has time to come home. You are known to be more outgoing than the other Pokémon Professors, and oftentimes your research takes you outdoors. Your field of study is primarily how Pokémon behave in the wild. For a reference, here are some quotes from you
+ "Oh, hi, Brendan! I heard you beat May on your first try. That's excellent! May's been helping with my research for a long time. May has an extensive history as a Trainer already. Here, Brendan, I ordered this for my research, but I think you should have this Pokédex."
+ "See? What did I tell you, May? Didn't I tell you that you don't need to worry about Brendan? ... Brendan, you've finally done it. When I heard that you defeated your own father at the Petalburg Gym, I thought perhaps you had a chance... But to think you've actually become the Champion! Ah, yes! What become of your Pokédex? Here, let me see."
+ "Well, well, Brendan! That was good work out there! I knew there was something special about you when I first saw you, but I never expected this. Oh, yes. Do you still have the Pokédex I gave you? I have something to show you. Let's go to my Lab."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-4
+ model: 'gpt-4'
+ temperature: 0.7
+ max_tokens: 100
+ - agent_type: conversation
+ name: Steven Stone
+ role_description: |-
+ You are Steven Stone, a character in Pokémon Emerald. You are a skilled Trainer who specializes in Steel-type Pokémon. You are the Champion of the Hoenn region's Pokémon League. You are a collector of rare stones, and you are the son of the president of the Devon Corporation, and you make your home in Mossdeep City. You wanders the region, aiding the player on their journey. You are just defeated by Brendan. For a reference, here are some quotes from you
+ "Your Pokémon appear quite capable. If you keep training, you could even become the Champion of the Pokémon League one day. That's what I think. I know, since we've gotten to know each other, let's register one another in our PokéNavs. ... Now, I've got to hurry along."
+ "I see... Your battle style is intriguing. Your Pokémon have obviously grown since I first met you in Dewford. I'd like you to have this Devon Scope. Who knows, there may be other concealed Pokémon. Brendon, I enjoy seeing Pokémon and Trainers who strive together. I think you're doing great. Well, let's meet again somewhere."
+ "Hi, Brendon! When you're on an adventure with your Pokémon, what do you think? Do you consider them to be strong partners? Do you think of them as fun companions? Depending on how you think, your adventure's significance changes."
+ memory:
+ memory_type: chat_history
+ prompt_template: *prompt
+ llm:
+ llm_type: gpt-4
+ model: gpt-4
+ temperature: 0.7
+ max_tokens: 100
+
+tools:
diff --git a/ui/dist/assets/message/message.png b/ui/dist/assets/message/message.png
new file mode 100644
index 000000000..7d22a4a07
Binary files /dev/null and b/ui/dist/assets/message/message.png differ
diff --git a/ui/dist/assets/sprites/archie.png b/ui/dist/assets/sprites/archie.png
new file mode 100644
index 000000000..967cafbe5
Binary files /dev/null and b/ui/dist/assets/sprites/archie.png differ
diff --git a/ui/dist/assets/sprites/birch.png b/ui/dist/assets/sprites/birch.png
new file mode 100644
index 000000000..45d67a327
Binary files /dev/null and b/ui/dist/assets/sprites/birch.png differ
diff --git a/ui/dist/assets/sprites/brendan.png b/ui/dist/assets/sprites/brendan.png
new file mode 100644
index 000000000..dd1d49b8c
Binary files /dev/null and b/ui/dist/assets/sprites/brendan.png differ
diff --git a/ui/dist/assets/sprites/joseph.png b/ui/dist/assets/sprites/joseph.png
new file mode 100644
index 000000000..cde9b50ac
Binary files /dev/null and b/ui/dist/assets/sprites/joseph.png differ
diff --git a/ui/dist/assets/sprites/maxie.png b/ui/dist/assets/sprites/maxie.png
new file mode 100644
index 000000000..b1ab2aa09
Binary files /dev/null and b/ui/dist/assets/sprites/maxie.png differ
diff --git a/ui/dist/assets/sprites/may.png b/ui/dist/assets/sprites/may.png
new file mode 100644
index 000000000..fcba3ce18
Binary files /dev/null and b/ui/dist/assets/sprites/may.png differ
diff --git a/ui/dist/assets/sprites/npc1.png b/ui/dist/assets/sprites/npc1.png
new file mode 100644
index 000000000..77495859e
Binary files /dev/null and b/ui/dist/assets/sprites/npc1.png differ
diff --git a/ui/dist/assets/sprites/steven.png b/ui/dist/assets/sprites/steven.png
new file mode 100644
index 000000000..1e1ae3b82
Binary files /dev/null and b/ui/dist/assets/sprites/steven.png differ
diff --git a/ui/dist/assets/tilemaps/json/tmp.json b/ui/dist/assets/tilemaps/json/tmp.json
new file mode 100644
index 000000000..96cc23ae3
--- /dev/null
+++ b/ui/dist/assets/tilemaps/json/tmp.json
@@ -0,0 +1,140 @@
+{ "compressionlevel":-1,
+ "height":100,
+ "infinite":false,
+ "layers":[
+ {
+ "data":[4, 1, 3, 3, 3, 2, 4, 3, 2, 2, 2, 4, 3, 3, 3, 4, 1, 1, 1, 1, 2, 1, 3, 4, 2, 3, 4, 2, 2, 3, 2, 4, 4, 4, 4, 3, 3, 2, 4, 2, 1, 4, 1, 2, 4, 3, 2, 3, 1, 2, 2, 1, 3, 2, 2, 3, 1, 2, 3, 3, 1, 3, 4, 4, 3, 2, 1, 1, 1, 1, 4, 1, 1, 2, 2, 3, 1, 1, 4, 1, 2, 4, 4, 1, 1, 2, 3, 3, 3, 4, 1, 4, 4, 3, 2, 4, 3, 3, 2, 1,
+ 2, 1, 3, 1, 4, 3, 4, 2, 1, 4, 3, 1, 2, 1, 2, 1, 1, 2, 2, 3, 2, 4, 4, 4, 2, 1, 4, 1, 2, 1, 1, 2, 3, 1, 2, 1, 4, 3, 3, 1, 2, 1, 2, 2, 1, 2, 1, 4, 3, 3, 4, 4, 3, 1, 3, 1, 1, 2, 3, 1, 1, 1, 1, 2, 1, 2, 2, 3, 3, 3, 4, 1, 1, 2, 3, 3, 3, 4, 1, 4, 1, 2, 2, 1, 3, 4, 3, 4, 3, 3, 2, 3, 3, 4, 4, 3, 2, 1, 1, 3,
+ 2, 2, 3, 4, 3, 4, 2, 4, 1, 1, 3, 1, 1, 2, 3, 3, 4, 3, 4, 3, 1, 3, 3, 2, 3, 3, 3, 2, 1, 3, 2, 2, 4, 2, 4, 2, 2, 1, 4, 4, 3, 2, 1, 1, 1, 1, 1, 4, 3, 2, 2, 3, 3, 2, 4, 1, 1, 2, 4, 2, 1, 2, 1, 2, 4, 2, 1, 3, 1, 4, 4, 2, 1, 4, 4, 2, 1, 2, 3, 3, 1, 4, 3, 3, 4, 2, 1, 4, 4, 2, 2, 1, 2, 3, 3, 3, 3, 1, 1, 3,
+ 4, 1, 1, 1, 4, 1, 4, 4, 4, 2, 1, 2, 2, 2, 4, 1, 2, 4, 2, 4, 1, 2, 1, 4, 4, 4, 2, 1, 1, 4, 2, 2, 2, 4, 3, 3, 3, 4, 2, 4, 2, 2, 4, 1, 3, 1, 2, 4, 3, 3, 1, 1, 4, 1, 3, 2, 2, 3, 1, 1, 1, 3, 2, 2, 2, 1, 2, 1, 4, 4, 3, 3, 3, 2, 2, 3, 3, 4, 2, 2, 1, 1, 1, 4, 4, 4, 4, 3, 3, 2, 3, 3, 2, 2, 3, 3, 3, 1, 1, 4,
+ 1, 3, 1, 4, 1, 1, 4, 3, 3, 3, 1, 2, 4, 3, 4, 4, 3, 3, 4, 4, 2, 3, 3, 4, 4, 2, 1, 4, 1, 2, 2, 4, 1, 2, 1, 3, 1, 2, 1, 2, 1, 3, 4, 3, 3, 1, 1, 2, 4, 3, 1, 3, 2, 1, 1, 2, 2, 2, 4, 1, 2, 1, 2, 3, 4, 1, 3, 4, 4, 1, 3, 1, 2, 2, 1, 1, 3, 3, 4, 2, 2, 3, 1, 2, 1, 2, 4, 4, 1, 3, 2, 2, 4, 2, 2, 3, 4, 4, 2, 3,
+ 3, 3, 2, 4, 4, 1, 3, 2, 3, 2, 2, 2, 3, 4, 3, 4, 3, 1, 2, 3, 2, 2, 4, 3, 4, 2, 1, 3, 1, 1, 4, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 3, 2, 3, 2, 3, 2, 2, 2, 2, 3, 3, 1, 1, 2, 4, 3, 1, 1, 3, 1, 1, 3, 3, 1, 3, 1, 4, 3, 4, 2, 3, 1, 3, 2, 2, 4, 1, 1, 2, 2, 4, 2, 3, 4, 2, 3, 4, 2, 2, 4, 3, 3, 2, 3, 3, 3, 4, 4, 3,
+ 1, 4, 2, 2, 4, 4, 1, 3, 4, 1, 3, 4, 2, 2, 3, 3, 1, 2, 3, 3, 1, 3, 3, 2, 1, 2, 4, 2, 1, 4, 3, 3, 2, 1, 3, 2, 2, 4, 2, 1, 1, 2, 4, 1, 1, 1, 1, 4, 3, 1, 3, 4, 2, 2, 2, 3, 2, 3, 4, 3, 3, 2, 2, 4, 4, 3, 2, 1, 2, 3, 4, 1, 3, 3, 4, 3, 1, 4, 3, 2, 1, 4, 3, 4, 1, 4, 2, 3, 1, 2, 4, 1, 4, 3, 4, 2, 4, 1, 4, 4,
+ 4, 3, 3, 3, 2, 3, 3, 3, 2, 3, 2, 2, 1, 1, 3, 1, 3, 4, 2, 3, 2, 4, 2, 3, 1, 1, 3, 3, 1, 3, 2, 3, 1, 3, 1, 2, 2, 2, 4, 4, 4, 4, 2, 2, 1, 4, 4, 4, 4, 3, 3, 2, 3, 3, 4, 2, 2, 3, 1, 2, 4, 4, 2, 4, 1, 1, 4, 3, 2, 3, 3, 4, 1, 1, 3, 3, 1, 1, 4, 3, 3, 1, 4, 2, 4, 2, 1, 4, 3, 2, 1, 4, 1, 3, 2, 3, 4, 2, 2, 4,
+ 3, 1, 2, 3, 2, 2, 1, 2, 1, 1, 3, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1, 4, 3, 2, 4, 2, 4, 2, 1, 3, 4, 2, 2, 3, 3, 4, 3, 2, 1, 3, 1, 3, 4, 3, 4, 1, 4, 3, 2, 2, 3, 3, 1, 2, 2, 2, 1, 4, 1, 2, 4, 2, 2, 2, 1, 2, 1, 3, 4, 3, 1, 2, 3, 3, 4, 4, 4, 1, 4, 4, 3, 3, 3, 2, 4, 4, 1, 3, 1, 2, 2, 4, 4, 2, 1, 3, 1, 4,
+ 3, 3, 4, 3, 3, 2, 3, 1, 3, 3, 3, 4, 2, 4, 1, 3, 2, 2, 1, 3, 2, 2, 4, 2, 3, 4, 4, 3, 3, 3, 2, 2, 3, 2, 2, 1, 3, 4, 1, 3, 4, 1, 1, 3, 4, 1, 1, 2, 2, 4, 2, 2, 1, 4, 1, 1, 3, 3, 2, 2, 1, 2, 4, 1, 1, 3, 3, 4, 2, 2, 4, 4, 2, 4, 2, 4, 1, 2, 2, 3, 4, 2, 2, 1, 2, 4, 3, 3, 2, 2, 1, 3, 2, 4, 1, 3, 1, 2, 4, 4,
+ 3, 4, 1, 3, 2, 3, 1, 2, 3, 3, 2, 3, 4, 3, 3, 1, 3, 4, 3, 3, 2, 1, 2, 3, 1, 1, 4, 1, 2, 3, 3, 1, 3, 3, 1, 3, 2, 3, 3, 3, 3, 2, 2, 3, 1, 2, 3, 3, 1, 3, 2, 2, 2, 3, 2, 2, 4, 2, 1, 1, 1, 1, 2, 2, 2, 1, 3, 1, 1, 4, 2, 4, 1, 3, 4, 2, 1, 4, 2, 2, 4, 4, 2, 4, 3, 4, 4, 2, 3, 3, 2, 3, 2, 4, 2, 4, 2, 3, 3, 3,
+ 1, 3, 2, 1, 2, 2, 3, 2, 4, 1, 2, 2, 2, 4, 2, 2, 4, 1, 3, 3, 3, 3, 4, 1, 2, 2, 1, 1, 2, 3, 3, 2, 2, 1, 1, 4, 2, 2, 2, 2, 4, 2, 1, 4, 4, 1, 2, 2, 1, 4, 3, 4, 4, 4, 4, 2, 2, 2, 4, 2, 3, 1, 2, 3, 3, 2, 2, 2, 2, 4, 1, 4, 3, 3, 4, 1, 2, 1, 1, 3, 4, 3, 1, 1, 1, 4, 3, 1, 1, 1, 4, 4, 1, 3, 2, 2, 1, 1, 1, 3,
+ 1, 3, 1, 1, 3, 1, 3, 3, 2, 1, 1, 1, 1, 3, 4, 2, 1, 3, 2, 4, 1, 2, 1, 4, 1, 1, 4, 4, 2, 2, 3, 4, 1, 2, 1, 1, 2, 3, 2, 3, 1, 1, 3, 3, 4, 4, 3, 3, 4, 3, 2, 3, 3, 1, 4, 3, 4, 2, 1, 3, 1, 1, 1, 4, 3, 2, 4, 4, 1, 2, 4, 4, 1, 2, 1, 1, 3, 1, 3, 2, 1, 2, 4, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 2, 2, 2, 1, 3, 3, 3,
+ 4, 2, 3, 1, 4, 3, 3, 3, 3, 3, 4, 1, 3, 1, 2, 1, 4, 3, 1, 4, 4, 2, 1, 1, 4, 2, 1, 2, 4, 2, 1, 4, 4, 3, 2, 2, 1, 2, 4, 2, 4, 3, 4, 3, 2, 4, 4, 2, 2, 1, 1, 3, 4, 3, 1, 3, 3, 2, 3, 1, 1, 4, 1, 4, 2, 4, 1, 4, 3, 2, 3, 1, 4, 2, 2, 2, 3, 3, 2, 4, 1, 3, 2, 1, 3, 2, 4, 1, 3, 2, 4, 4, 4, 3, 1, 3, 1, 3, 2, 3,
+ 1, 2, 3, 1, 3, 2, 2, 3, 4, 4, 1, 3, 4, 1, 1, 1, 1, 4, 1, 3, 1, 4, 3, 2, 1, 4, 1, 2, 3, 1, 4, 4, 4, 4, 4, 3, 4, 4, 1, 2, 2, 4, 2, 4, 3, 4, 3, 3, 1, 1, 3, 3, 2, 2, 3, 3, 2, 2, 3, 1, 4, 1, 2, 3, 1, 3, 3, 1, 3, 4, 2, 3, 4, 1, 1, 4, 4, 4, 1, 1, 4, 4, 1, 2, 4, 2, 3, 2, 4, 3, 3, 4, 4, 1, 4, 2, 2, 1, 3, 4,
+ 1, 3, 1, 4, 2, 1, 2, 2, 2, 3, 2, 3, 2, 4, 3, 4, 3, 4, 3, 4, 1, 3, 2, 4, 4, 3, 3, 2, 3, 4, 1, 2, 3, 3, 2, 1, 2, 4, 2, 1, 4, 4, 1, 2, 3, 1, 4, 4, 1, 4, 2, 4, 4, 3, 2, 3, 1, 2, 2, 3, 2, 3, 2, 2, 1, 4, 2, 2, 3, 2, 3, 4, 3, 3, 2, 1, 1, 3, 3, 3, 3, 2, 2, 4, 1, 4, 2, 3, 1, 4, 3, 3, 3, 3, 2, 4, 3, 4, 3, 3,
+ 3, 4, 3, 4, 3, 2, 4, 1, 1, 2, 4, 3, 4, 4, 1, 1, 2, 1, 2, 3, 4, 4, 4, 3, 2, 3, 2, 3, 3, 1, 4, 4, 1, 3, 1, 2, 4, 4, 1, 2, 4, 4, 2, 3, 3, 2, 2, 1, 2, 1, 2, 2, 2, 2, 3, 4, 2, 4, 4, 3, 4, 4, 1, 3, 2, 1, 1, 3, 4, 1, 3, 2, 4, 4, 4, 4, 3, 3, 3, 4, 2, 4, 3, 2, 3, 2, 2, 3, 4, 4, 4, 4, 4, 3, 4, 3, 2, 2, 4, 4,
+ 2, 1, 4, 1, 2, 2, 4, 1, 1, 4, 1, 4, 1, 1, 3, 1, 4, 4, 4, 2, 2, 3, 4, 3, 4, 4, 3, 1, 4, 3, 4, 3, 2, 3, 4, 2, 4, 3, 3, 1, 3, 3, 3, 2, 1, 3, 4, 1, 1, 1, 2, 1, 2, 3, 1, 3, 1, 1, 1, 4, 2, 3, 4, 1, 2, 3, 2, 2, 2, 1, 3, 4, 4, 2, 4, 4, 1, 4, 3, 3, 4, 3, 4, 4, 2, 1, 1, 4, 1, 1, 3, 1, 3, 1, 2, 1, 4, 3, 4, 3,
+ 3, 2, 3, 1, 4, 3, 2, 4, 4, 3, 1, 4, 3, 2, 3, 2, 1, 2, 3, 4, 1, 1, 1, 3, 1, 3, 3, 4, 3, 4, 2, 3, 1, 2, 4, 4, 1, 3, 2, 1, 2, 2, 1, 1, 3, 1, 1, 2, 2, 2, 4, 4, 2, 1, 2, 3, 4, 3, 1, 3, 1, 3, 2, 3, 3, 2, 2, 3, 2, 2, 4, 2, 3, 4, 3, 3, 4, 3, 4, 2, 3, 1, 1, 1, 4, 1, 1, 3, 2, 4, 1, 2, 3, 1, 1, 4, 3, 3, 3, 4,
+ 4, 1, 4, 3, 2, 2, 2, 4, 1, 1, 3, 3, 1, 3, 4, 3, 2, 2, 1, 2, 4, 3, 1, 4, 4, 1, 3, 3, 1, 2, 3, 1, 2, 4, 3, 2, 4, 1, 1, 3, 3, 3, 4, 1, 3, 3, 3, 2, 4, 4, 2, 1, 2, 1, 3, 1, 2, 1, 3, 4, 1, 1, 4, 3, 2, 1, 4, 2, 1, 1, 2, 4, 4, 2, 4, 3, 2, 1, 3, 1, 3, 1, 4, 3, 3, 3, 3, 1, 1, 3, 1, 2, 4, 1, 4, 2, 2, 1, 1, 2,
+ 4, 4, 1, 2, 4, 3, 4, 3, 4, 1, 4, 3, 3, 1, 1, 1, 2, 3, 2, 3, 1, 4, 4, 4, 2, 4, 3, 4, 2, 1, 2, 4, 2, 2, 4, 3, 2, 3, 2, 4, 1, 1, 2, 2, 1, 1, 4, 4, 3, 1, 4, 4, 4, 3, 2, 4, 1, 2, 3, 3, 1, 2, 3, 1, 1, 2, 1, 4, 4, 2, 2, 4, 2, 3, 4, 4, 2, 3, 2, 3, 2, 1, 3, 3, 4, 4, 3, 2, 4, 2, 2, 3, 1, 3, 2, 4, 4, 1, 3, 3,
+ 1, 2, 1, 3, 1, 4, 1, 4, 3, 3, 4, 3, 1, 2, 2, 1, 2, 1, 1, 4, 1, 2, 2, 3, 1, 3, 3, 4, 1, 2, 1, 1, 4, 3, 3, 3, 4, 2, 3, 1, 1, 3, 3, 4, 4, 2, 1, 1, 3, 1, 2, 1, 4, 2, 2, 1, 4, 2, 1, 3, 3, 2, 4, 2, 3, 2, 2, 1, 1, 4, 3, 4, 2, 4, 1, 3, 1, 3, 4, 3, 3, 2, 3, 2, 4, 4, 1, 4, 4, 4, 1, 1, 1, 2, 1, 1, 1, 1, 1, 3,
+ 2, 1, 2, 2, 4, 1, 3, 1, 2, 4, 1, 1, 4, 3, 2, 4, 4, 3, 1, 1, 1, 2, 1, 3, 1, 4, 2, 3, 1, 3, 2, 4, 2, 2, 3, 3, 3, 2, 3, 4, 2, 1, 1, 2, 3, 4, 2, 1, 2, 4, 4, 2, 4, 3, 4, 4, 1, 1, 1, 2, 3, 4, 4, 2, 2, 4, 4, 3, 3, 4, 2, 4, 3, 4, 4, 2, 4, 3, 3, 4, 1, 1, 4, 4, 1, 4, 4, 2, 2, 3, 3, 1, 1, 3, 3, 1, 3, 2, 4, 2,
+ 4, 4, 1, 3, 1, 3, 3, 4, 3, 3, 4, 4, 2, 2, 4, 2, 3, 3, 4, 2, 1, 1, 4, 3, 4, 4, 4, 1, 1, 3, 3, 2, 4, 4, 1, 4, 1, 2, 4, 2, 1, 4, 1, 2, 1, 3, 3, 1, 1, 4, 4, 1, 2, 4, 1, 2, 4, 4, 2, 1, 3, 3, 2, 2, 1, 3, 3, 1, 3, 3, 4, 2, 1, 4, 3, 4, 3, 4, 4, 1, 1, 1, 4, 1, 4, 2, 4, 1, 2, 3, 3, 4, 3, 4, 4, 3, 1, 2, 4, 3,
+ 3, 4, 1, 1, 4, 3, 4, 2, 4, 1, 3, 2, 3, 1, 4, 2, 4, 3, 4, 4, 2, 2, 3, 2, 3, 1, 2, 2, 4, 2, 1, 3, 3, 1, 4, 3, 2, 4, 1, 1, 2, 4, 4, 1, 2, 2, 1, 2, 1, 3, 1, 3, 2, 2, 2, 2, 3, 3, 4, 3, 3, 1, 2, 1, 3, 4, 3, 3, 1, 2, 2, 1, 1, 3, 4, 3, 4, 3, 4, 1, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 4, 4, 1, 2, 1, 4, 4, 1, 1, 3,
+ 3, 1, 4, 1, 1, 3, 1, 2, 3, 1, 3, 2, 3, 4, 3, 4, 2, 2, 3, 4, 2, 1, 4, 3, 2, 2, 3, 3, 2, 2, 4, 4, 3, 4, 1, 1, 4, 3, 1, 2, 4, 2, 4, 3, 2, 2, 2, 3, 1, 3, 3, 2, 4, 4, 2, 1, 2, 2, 2, 1, 4, 1, 2, 1, 1, 2, 4, 3, 1, 1, 2, 2, 1, 1, 3, 3, 4, 2, 1, 3, 1, 4, 4, 4, 4, 1, 4, 3, 3, 2, 1, 1, 4, 4, 3, 2, 1, 4, 2, 4,
+ 1, 4, 2, 4, 4, 3, 3, 3, 1, 3, 3, 1, 2, 3, 4, 2, 2, 2, 1, 3, 4, 2, 4, 3, 4, 1, 1, 1, 3, 3, 1, 2, 4, 4, 3, 2, 4, 4, 4, 1, 4, 3, 4, 3, 3, 3, 4, 1, 4, 4, 2, 3, 1, 1, 3, 4, 4, 3, 2, 3, 3, 3, 2, 2, 2, 3, 3, 3, 2, 4, 2, 3, 4, 3, 4, 4, 3, 3, 1, 2, 3, 3, 1, 1, 4, 3, 4, 4, 4, 1, 1, 2, 2, 2, 2, 3, 4, 4, 3, 3,
+ 2, 2, 3, 1, 1, 2, 2, 1, 4, 4, 3, 3, 4, 4, 2, 3, 4, 1, 2, 4, 4, 2, 2, 1, 2, 4, 4, 2, 1, 1, 3, 2, 1, 2, 2, 1, 4, 2, 3, 2, 3, 3, 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 3, 3, 1, 3, 4, 2, 4, 2, 3, 2, 2, 4, 1, 1, 3, 2, 1, 3, 3, 1, 1, 3, 2, 3, 2, 4, 2, 3, 4, 4, 1, 2, 1, 1, 4, 2, 2, 1, 3, 3, 4, 1, 3, 4, 2, 3, 3,
+ 3, 3, 2, 1, 2, 3, 4, 2, 1, 4, 4, 2, 3, 4, 3, 2, 2, 4, 1, 4, 1, 2, 4, 1, 1, 3, 3, 4, 4, 2, 2, 3, 1, 2, 4, 1, 2, 1, 2, 2, 1, 3, 4, 3, 4, 2, 4, 1, 3, 4, 1, 3, 2, 1, 4, 2, 3, 2, 3, 2, 2, 4, 1, 3, 2, 4, 2, 2, 2, 2, 3, 4, 3, 2, 3, 1, 3, 2, 3, 2, 2, 4, 4, 1, 4, 4, 3, 3, 2, 3, 1, 4, 4, 4, 3, 1, 2, 4, 3, 4,
+ 3, 3, 1, 3, 3, 4, 4, 3, 3, 2, 4, 2, 1, 2, 4, 4, 3, 4, 2, 2, 2, 4, 1, 1, 2, 3, 1, 2, 3, 3, 1, 3, 1, 2, 1, 1, 1, 2, 3, 3, 2, 2, 3, 1, 4, 3, 3, 4, 3, 3, 4, 1, 4, 2, 2, 1, 1, 1, 2, 3, 4, 2, 2, 3, 1, 2, 1, 3, 2, 1, 3, 1, 3, 1, 1, 1, 2, 4, 2, 1, 3, 4, 2, 4, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 2, 2, 3, 1, 2, 2,
+ 2, 4, 1, 1, 2, 4, 2, 1, 3, 2, 1, 2, 2, 4, 2, 4, 4, 1, 4, 4, 2, 3, 4, 2, 2, 2, 2, 1, 4, 4, 3, 1, 3, 2, 1, 4, 1, 3, 4, 3, 3, 4, 3, 4, 4, 1, 1, 1, 4, 2, 1, 1, 4, 3, 2, 2, 1, 2, 4, 1, 4, 3, 3, 3, 2, 3, 2, 4, 2, 2, 4, 1, 4, 3, 1, 1, 3, 2, 4, 1, 4, 3, 3, 4, 2, 2, 2, 2, 4, 4, 3, 1, 1, 4, 4, 1, 3, 3, 1, 4,
+ 1, 1, 4, 2, 3, 3, 3, 2, 4, 1, 4, 1, 2, 1, 4, 4, 3, 2, 3, 3, 3, 1, 4, 4, 4, 1, 1, 4, 3, 2, 3, 2, 3, 2, 2, 4, 1, 3, 2, 1, 4, 2, 2, 4, 3, 1, 4, 1, 2, 2, 3, 3, 4, 2, 2, 4, 1, 3, 1, 2, 1, 1, 3, 2, 1, 1, 4, 1, 2, 3, 1, 2, 2, 1, 3, 3, 1, 1, 1, 1, 2, 2, 3, 2, 1, 3, 2, 2, 1, 4, 1, 3, 4, 2, 1, 2, 1, 4, 1, 3,
+ 4, 1, 4, 2, 1, 2, 3, 3, 3, 2, 3, 3, 1, 3, 1, 4, 3, 1, 2, 2, 1, 1, 3, 1, 2, 4, 4, 2, 3, 1, 3, 3, 4, 1, 3, 4, 3, 2, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 1, 1, 1, 2, 4, 3, 4, 3, 1, 4, 2, 1, 1, 3, 2, 4, 2, 2, 4, 2, 1, 3, 4, 1, 2, 4, 4, 4, 4, 1, 2, 4, 2, 2, 2, 4, 4, 3, 1, 3, 3, 3, 3, 2, 1, 2, 2, 3, 3, 4, 3, 3,
+ 3, 4, 3, 2, 2, 4, 3, 4, 2, 4, 3, 3, 4, 1, 2, 2, 4, 1, 4, 3, 4, 4, 3, 3, 4, 3, 2, 2, 2, 1, 4, 3, 3, 3, 3, 1, 2, 4, 3, 1, 4, 2, 1, 2, 3, 3, 2, 2, 1, 3, 1, 3, 1, 2, 3, 4, 2, 4, 2, 4, 1, 4, 4, 3, 3, 4, 1, 2, 2, 2, 2, 1, 2, 3, 2, 4, 2, 2, 1, 4, 3, 1, 2, 4, 2, 1, 4, 2, 2, 1, 1, 2, 1, 2, 4, 2, 2, 3, 4, 3,
+ 2, 2, 4, 4, 1, 1, 3, 1, 1, 2, 1, 1, 4, 3, 4, 4, 2, 3, 2, 4, 1, 4, 4, 3, 4, 1, 1, 1, 1, 1, 2, 1, 4, 3, 3, 3, 1, 2, 4, 2, 2, 3, 2, 1, 3, 2, 1, 3, 4, 3, 4, 3, 4, 1, 4, 1, 4, 2, 2, 1, 4, 3, 4, 3, 2, 2, 1, 2, 3, 4, 2, 2, 2, 2, 2, 1, 1, 2, 1, 2, 4, 2, 2, 2, 4, 2, 3, 3, 2, 1, 2, 3, 4, 4, 2, 4, 4, 3, 2, 1,
+ 4, 2, 3, 1, 4, 1, 2, 3, 3, 1, 2, 3, 1, 4, 4, 4, 1, 3, 1, 3, 1, 4, 2, 1, 2, 1, 3, 1, 4, 4, 4, 2, 1, 1, 4, 4, 2, 2, 1, 4, 3, 4, 3, 3, 3, 3, 4, 2, 2, 1, 3, 1, 1, 4, 3, 1, 3, 4, 2, 4, 3, 4, 3, 2, 3, 4, 1, 4, 2, 3, 3, 3, 2, 3, 3, 1, 2, 1, 4, 1, 2, 1, 3, 4, 3, 4, 3, 3, 4, 1, 3, 2, 4, 2, 2, 3, 3, 1, 4, 3,
+ 2, 4, 3, 3, 1, 4, 1, 3, 2, 4, 3, 4, 3, 1, 4, 2, 2, 4, 3, 3, 4, 3, 4, 3, 4, 4, 1, 2, 4, 3, 2, 3, 4, 1, 2, 2, 1, 1, 4, 3, 3, 2, 2, 1, 3, 4, 3, 1, 4, 2, 2, 1, 2, 3, 2, 1, 3, 1, 1, 4, 2, 2, 1, 4, 2, 3, 2, 2, 4, 3, 4, 3, 4, 2, 2, 4, 1, 3, 2, 3, 4, 2, 1, 1, 3, 1, 1, 1, 4, 1, 4, 1, 3, 1, 4, 3, 4, 4, 3, 3,
+ 4, 3, 3, 2, 2, 2, 3, 2, 2, 4, 3, 1, 2, 3, 2, 4, 1, 3, 2, 1, 4, 4, 2, 1, 3, 3, 3, 2, 2, 1, 2, 1, 3, 2, 2, 1, 4, 3, 3, 3, 2, 1, 1, 4, 4, 1, 1, 1, 1, 2, 3, 2, 2, 1, 3, 4, 2, 4, 4, 1, 2, 3, 2, 4, 1, 2, 2, 3, 3, 1, 2, 1, 2, 3, 3, 1, 3, 2, 4, 4, 4, 3, 3, 4, 4, 4, 4, 1, 4, 1, 3, 1, 1, 4, 3, 2, 3, 3, 4, 1,
+ 3, 3, 1, 3, 3, 1, 1, 1, 4, 3, 3, 1, 3, 4, 2, 3, 4, 2, 4, 2, 1, 3, 1, 1, 2, 2, 3, 3, 3, 4, 4, 3, 3, 2, 3, 2, 1, 2, 3, 3, 4, 3, 2, 3, 3, 3, 4, 1, 3, 1, 3, 2, 3, 2, 3, 4, 1, 1, 1, 2, 4, 1, 1, 4, 2, 3, 1, 2, 3, 4, 3, 3, 4, 1, 1, 2, 3, 4, 2, 3, 3, 2, 1, 2, 2, 2, 2, 4, 2, 3, 1, 2, 1, 3, 4, 1, 1, 2, 2, 1,
+ 4, 1, 4, 4, 1, 4, 1, 2, 4, 4, 3, 2, 3, 1, 3, 4, 2, 2, 3, 2, 2, 2, 4, 1, 1, 1, 1, 3, 3, 4, 3, 1, 3, 3, 3, 4, 4, 1, 1, 2, 2, 2, 3, 1, 4, 1, 3, 3, 3, 3, 3, 4, 4, 1, 3, 4, 4, 4, 3, 1, 3, 1, 1, 1, 3, 2, 3, 4, 3, 2, 1, 1, 1, 4, 4, 1, 3, 2, 4, 2, 3, 4, 1, 3, 1, 4, 2, 4, 1, 3, 1, 4, 3, 2, 2, 2, 3, 2, 2, 3,
+ 2, 2, 4, 1, 2, 1, 3, 2, 4, 2, 2, 1, 2, 2, 1, 2, 4, 3, 4, 2, 3, 3, 4, 3, 2, 2, 4, 3, 2, 2, 2, 1, 2, 1, 3, 4, 2, 1, 2, 1, 1, 3, 2, 2, 3, 1, 3, 4, 1, 2, 4, 3, 3, 4, 4, 4, 4, 3, 2, 3, 4, 3, 3, 3, 2, 1, 2, 3, 4, 4, 2, 4, 4, 3, 2, 1, 4, 1, 4, 4, 4, 4, 2, 3, 2, 3, 1, 4, 3, 2, 3, 4, 1, 2, 3, 4, 2, 4, 1, 3,
+ 1, 1, 3, 1, 4, 3, 2, 3, 3, 3, 2, 2, 1, 3, 4, 3, 2, 1, 4, 4, 4, 2, 4, 4, 2, 3, 4, 1, 4, 2, 2, 1, 1, 3, 4, 3, 4, 2, 2, 4, 4, 2, 3, 4, 4, 2, 3, 2, 2, 1, 1, 1, 4, 1, 2, 2, 4, 2, 3, 4, 2, 4, 1, 2, 3, 4, 2, 1, 3, 4, 2, 2, 3, 3, 1, 3, 3, 3, 3, 1, 4, 3, 1, 4, 1, 4, 1, 4, 3, 2, 1, 2, 1, 3, 3, 3, 1, 1, 4, 4,
+ 4, 2, 1, 2, 4, 3, 2, 4, 3, 1, 2, 2, 2, 1, 2, 3, 2, 1, 2, 1, 1, 2, 4, 2, 4, 2, 4, 2, 2, 1, 3, 3, 3, 2, 1, 4, 3, 2, 4, 4, 4, 1, 1, 4, 4, 3, 2, 1, 3, 1, 2, 3, 4, 4, 3, 2, 1, 1, 1, 4, 4, 1, 4, 1, 4, 4, 2, 1, 1, 4, 4, 2, 2, 3, 1, 2, 4, 4, 2, 4, 3, 1, 3, 3, 4, 1, 2, 2, 2, 2, 2, 4, 4, 3, 3, 1, 3, 1, 4, 2,
+ 3, 1, 4, 2, 1, 3, 4, 4, 3, 1, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 1, 1, 1, 4, 2, 4, 1, 2, 1, 4, 4, 2, 4, 2, 2, 4, 3, 1, 4, 3, 3, 3, 3, 2, 3, 2, 2, 3, 3, 1, 4, 3, 1, 3, 4, 3, 2, 4, 2, 1, 2, 3, 2, 1, 4, 3, 4, 3, 1, 3, 1, 1, 3, 4, 2, 4, 2, 1, 2, 2, 2, 1, 3, 2, 1, 3, 3, 2, 1, 3, 3, 3, 3, 1, 3, 1, 1,
+ 3, 1, 4, 3, 2, 4, 4, 2, 2, 4, 3, 2, 2, 3, 2, 3, 2, 2, 2, 2, 4, 2, 1, 2, 1, 4, 1, 3, 4, 3, 1, 1, 2, 4, 3, 1, 1, 1, 4, 1, 2, 3, 2, 2, 2, 3, 4, 1, 2, 1, 2, 1, 2, 3, 4, 1, 2, 2, 2, 4, 4, 2, 2, 1, 4, 4, 3, 4, 4, 3, 4, 2, 3, 4, 3, 4, 4, 2, 2, 4, 2, 4, 4, 4, 4, 3, 2, 4, 2, 3, 4, 1, 1, 2, 4, 1, 2, 2, 3, 3,
+ 3, 3, 2, 4, 4, 1, 3, 1, 4, 3, 1, 3, 4, 4, 1, 4, 4, 1, 2, 4, 4, 3, 2, 2, 3, 2, 1, 2, 2, 3, 1, 1, 2, 4, 4, 3, 2, 2, 1, 2, 4, 3, 3, 1, 1, 3, 3, 2, 4, 1, 3, 1, 4, 1, 2, 2, 4, 2, 3, 1, 2, 3, 1, 4, 3, 4, 4, 4, 2, 4, 4, 4, 1, 4, 1, 1, 1, 4, 1, 4, 2, 3, 3, 3, 2, 2, 1, 2, 2, 1, 1, 3, 3, 3, 1, 1, 2, 1, 2, 4,
+ 2, 4, 2, 4, 1, 3, 4, 3, 3, 3, 2, 4, 2, 3, 4, 4, 3, 2, 1, 4, 2, 1, 4, 2, 4, 3, 1, 2, 3, 3, 4, 1, 3, 3, 4, 3, 2, 1, 4, 2, 1, 4, 1, 1, 1, 2, 2, 3, 2, 3, 2, 2, 3, 3, 4, 1, 3, 4, 1, 4, 4, 4, 2, 2, 3, 1, 1, 2, 4, 4, 4, 2, 3, 4, 4, 3, 4, 1, 4, 4, 3, 3, 4, 4, 4, 2, 3, 2, 2, 1, 4, 4, 1, 1, 4, 3, 1, 2, 4, 1,
+ 2, 3, 2, 2, 3, 3, 3, 1, 2, 1, 3, 2, 3, 2, 1, 3, 1, 4, 2, 4, 3, 1, 1, 2, 1, 2, 3, 1, 4, 2, 3, 4, 3, 3, 2, 3, 3, 1, 2, 4, 2, 1, 3, 4, 4, 3, 4, 1, 1, 3, 2, 2, 4, 4, 4, 2, 4, 3, 1, 1, 4, 2, 2, 2, 1, 3, 2, 3, 3, 3, 4, 3, 2, 1, 4, 4, 3, 3, 1, 2, 1, 1, 1, 3, 2, 4, 4, 4, 4, 3, 4, 1, 4, 1, 4, 4, 4, 2, 3, 4,
+ 3, 1, 2, 4, 2, 2, 2, 4, 1, 1, 3, 2, 4, 3, 1, 2, 2, 2, 4, 4, 3, 3, 1, 1, 1, 1, 2, 3, 3, 2, 1, 3, 1, 3, 4, 3, 3, 1, 3, 4, 1, 1, 2, 1, 4, 2, 1, 4, 1, 4, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 3, 4, 4, 3, 4, 4, 1, 3, 1, 1, 3, 2, 3, 1, 1, 3, 2, 4, 4, 4, 4, 2, 4, 1, 2, 2, 2, 1, 3, 1, 2, 4, 1, 1, 2, 2, 3, 2, 4,
+ 3, 3, 1, 4, 2, 1, 3, 4, 2, 2, 2, 2, 1, 1, 3, 4, 2, 2, 1, 4, 1, 4, 3, 1, 4, 4, 4, 1, 2, 2, 3, 4, 3, 4, 2, 1, 2, 3, 3, 4, 1, 1, 2, 4, 1, 3, 2, 3, 4, 3, 2, 2, 3, 4, 4, 4, 1, 1, 3, 1, 3, 2, 1, 4, 2, 4, 3, 3, 1, 1, 4, 3, 3, 3, 3, 1, 1, 1, 1, 3, 4, 1, 1, 2, 4, 3, 1, 1, 3, 2, 4, 1, 3, 2, 4, 2, 1, 2, 2, 2,
+ 4, 3, 3, 1, 1, 1, 2, 3, 2, 4, 1, 2, 4, 4, 2, 4, 1, 1, 2, 2, 1, 3, 3, 3, 3, 1, 4, 2, 4, 3, 3, 2, 4, 3, 1, 1, 1, 2, 4, 2, 3, 3, 1, 2, 2, 4, 3, 1, 3, 4, 3, 4, 4, 3, 3, 2, 1, 1, 1, 3, 4, 1, 4, 3, 3, 4, 2, 4, 2, 3, 3, 4, 2, 1, 1, 3, 3, 4, 4, 3, 1, 4, 3, 2, 1, 2, 3, 3, 1, 4, 1, 4, 4, 2, 2, 4, 1, 1, 3, 4,
+ 3, 2, 2, 1, 2, 4, 3, 2, 3, 4, 2, 2, 3, 2, 1, 1, 3, 2, 3, 1, 4, 3, 3, 3, 2, 3, 4, 2, 1, 4, 4, 4, 3, 4, 2, 1, 3, 2, 2, 3, 4, 3, 3, 2, 1, 2, 2, 1, 1, 2, 1, 4, 2, 2, 3, 1, 3, 2, 2, 2, 2, 3, 4, 1, 3, 4, 4, 2, 4, 3, 3, 4, 3, 4, 4, 1, 2, 4, 4, 3, 4, 4, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 3, 1, 2, 2, 2, 3, 1, 1,
+ 1, 3, 1, 4, 1, 1, 4, 4, 2, 4, 2, 2, 3, 2, 1, 2, 4, 1, 4, 2, 2, 4, 1, 1, 1, 4, 4, 4, 4, 1, 2, 3, 1, 4, 3, 3, 3, 4, 2, 2, 3, 1, 1, 2, 2, 1, 3, 4, 4, 4, 1, 4, 2, 4, 1, 1, 1, 1, 3, 2, 1, 4, 3, 3, 1, 1, 2, 4, 3, 3, 3, 1, 2, 4, 2, 1, 3, 2, 3, 3, 3, 3, 1, 2, 3, 2, 4, 2, 4, 4, 2, 3, 2, 3, 3, 2, 4, 2, 1, 3,
+ 4, 3, 4, 2, 4, 3, 4, 2, 2, 2, 1, 2, 4, 4, 4, 2, 2, 3, 1, 4, 3, 4, 1, 2, 3, 3, 4, 4, 4, 4, 3, 1, 1, 1, 1, 3, 1, 4, 3, 3, 4, 3, 2, 1, 2, 2, 3, 4, 4, 4, 3, 1, 2, 4, 3, 4, 4, 1, 3, 4, 2, 4, 4, 2, 4, 1, 4, 4, 3, 1, 4, 2, 1, 4, 4, 2, 4, 3, 4, 1, 2, 3, 4, 2, 2, 2, 1, 2, 2, 1, 2, 1, 1, 1, 1, 3, 4, 3, 2, 4,
+ 4, 1, 1, 2, 3, 4, 2, 3, 1, 2, 3, 4, 3, 2, 2, 1, 3, 1, 4, 4, 4, 2, 1, 4, 2, 4, 3, 3, 4, 1, 2, 2, 1, 4, 3, 4, 4, 3, 2, 1, 2, 2, 3, 2, 3, 4, 2, 4, 4, 4, 4, 3, 1, 4, 4, 1, 1, 4, 2, 2, 2, 4, 3, 2, 4, 3, 1, 3, 4, 2, 2, 4, 3, 2, 4, 2, 4, 3, 2, 2, 4, 2, 3, 4, 1, 2, 3, 2, 4, 2, 4, 4, 4, 4, 3, 1, 4, 4, 1, 4,
+ 3, 1, 2, 1, 3, 2, 4, 1, 2, 1, 2, 2, 2, 1, 3, 3, 3, 4, 2, 2, 1, 3, 1, 4, 3, 4, 4, 1, 3, 1, 2, 4, 1, 4, 3, 1, 4, 3, 2, 1, 3, 1, 3, 2, 1, 1, 3, 2, 4, 4, 2, 1, 1, 2, 4, 4, 3, 2, 3, 2, 1, 1, 1, 3, 1, 1, 1, 2, 2, 1, 3, 3, 2, 4, 2, 4, 4, 3, 1, 4, 1, 4, 4, 4, 3, 1, 3, 1, 3, 2, 4, 2, 4, 1, 1, 4, 3, 2, 1, 4,
+ 2, 3, 1, 3, 3, 1, 2, 3, 3, 3, 3, 1, 2, 4, 3, 3, 3, 4, 2, 3, 4, 2, 2, 4, 2, 3, 3, 1, 1, 3, 2, 3, 3, 4, 1, 3, 2, 3, 3, 2, 2, 1, 2, 1, 4, 3, 4, 1, 3, 4, 1, 1, 2, 1, 1, 1, 4, 4, 2, 2, 3, 1, 2, 2, 4, 1, 1, 1, 2, 4, 2, 4, 4, 3, 2, 4, 4, 3, 3, 4, 1, 2, 4, 1, 3, 1, 1, 4, 4, 3, 4, 1, 3, 1, 1, 1, 3, 1, 4, 2,
+ 2, 1, 4, 1, 3, 2, 1, 2, 3, 1, 4, 2, 3, 4, 2, 2, 4, 2, 3, 4, 4, 1, 3, 2, 4, 1, 4, 3, 1, 1, 1, 2, 2, 1, 3, 2, 1, 4, 2, 4, 2, 1, 1, 2, 4, 3, 2, 2, 1, 2, 1, 4, 3, 1, 3, 4, 1, 2, 2, 2, 1, 1, 4, 4, 4, 3, 3, 1, 2, 1, 4, 4, 1, 1, 1, 3, 1, 1, 3, 3, 4, 1, 2, 1, 3, 4, 3, 3, 3, 1, 4, 4, 3, 4, 2, 4, 1, 4, 2, 1,
+ 4, 3, 3, 1, 3, 3, 3, 3, 3, 1, 4, 4, 4, 3, 2, 1, 1, 2, 2, 4, 1, 1, 2, 3, 1, 3, 2, 1, 2, 4, 3, 4, 3, 3, 1, 1, 3, 4, 3, 1, 2, 3, 4, 3, 2, 3, 2, 3, 1, 2, 1, 1, 3, 1, 3, 3, 4, 2, 4, 2, 1, 4, 2, 1, 4, 3, 1, 4, 3, 4, 3, 1, 4, 3, 3, 3, 1, 4, 2, 2, 4, 3, 1, 4, 1, 1, 2, 4, 4, 3, 2, 3, 1, 3, 2, 4, 4, 4, 3, 1,
+ 3, 3, 3, 3, 3, 4, 1, 2, 1, 4, 3, 1, 4, 3, 4, 4, 3, 1, 1, 1, 1, 1, 4, 1, 3, 1, 3, 1, 3, 2, 1, 3, 2, 2, 1, 1, 1, 3, 3, 3, 2, 3, 4, 3, 1, 4, 4, 4, 2, 4, 3, 3, 4, 1, 2, 3, 4, 3, 3, 3, 1, 3, 4, 4, 4, 3, 4, 3, 2, 3, 3, 1, 2, 3, 4, 4, 1, 4, 3, 4, 4, 2, 3, 4, 2, 3, 1, 4, 1, 1, 2, 2, 1, 2, 4, 4, 3, 1, 2, 4,
+ 1, 1, 4, 2, 3, 1, 4, 2, 1, 1, 2, 3, 3, 3, 1, 4, 4, 3, 1, 4, 4, 4, 2, 3, 4, 4, 2, 3, 2, 4, 4, 2, 3, 2, 4, 1, 2, 4, 1, 1, 1, 3, 1, 1, 1, 2, 3, 4, 2, 4, 1, 2, 3, 4, 1, 2, 2, 3, 1, 1, 1, 4, 2, 1, 2, 3, 1, 3, 3, 2, 4, 3, 2, 1, 3, 2, 1, 3, 4, 4, 3, 3, 2, 4, 3, 1, 3, 1, 1, 3, 3, 1, 2, 2, 4, 1, 4, 3, 1, 2,
+ 4, 2, 2, 4, 1, 1, 1, 1, 2, 3, 4, 3, 4, 1, 2, 2, 1, 3, 2, 2, 3, 1, 2, 3, 1, 2, 2, 3, 3, 1, 4, 3, 2, 2, 4, 2, 2, 1, 1, 3, 4, 2, 1, 1, 3, 2, 3, 1, 2, 2, 4, 1, 3, 3, 3, 4, 2, 2, 4, 4, 3, 1, 2, 2, 3, 3, 1, 2, 1, 2, 2, 3, 4, 1, 4, 3, 4, 3, 1, 2, 2, 2, 1, 1, 2, 1, 2, 3, 2, 4, 4, 1, 3, 2, 4, 4, 3, 1, 1, 1,
+ 3, 2, 1, 3, 2, 1, 4, 2, 1, 3, 1, 2, 2, 4, 1, 1, 3, 3, 4, 3, 4, 2, 3, 2, 2, 1, 2, 1, 3, 2, 4, 2, 3, 4, 4, 3, 2, 4, 3, 2, 3, 4, 4, 1, 1, 4, 4, 4, 1, 1, 1, 3, 3, 3, 1, 2, 2, 1, 1, 4, 1, 2, 3, 2, 3, 4, 1, 2, 1, 2, 1, 1, 2, 3, 1, 4, 4, 1, 3, 2, 2, 4, 4, 3, 4, 4, 3, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, 4, 3, 4,
+ 1, 2, 4, 4, 3, 1, 4, 2, 4, 3, 4, 2, 1, 1, 1, 3, 1, 2, 1, 4, 2, 1, 4, 3, 3, 4, 4, 4, 1, 3, 2, 1, 2, 2, 1, 2, 3, 1, 3, 3, 3, 3, 2, 1, 1, 2, 4, 4, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 3, 3, 3, 2, 1, 1, 3, 4, 2, 3, 4, 2, 4, 1, 3, 4, 1, 3, 2, 1, 3, 2, 2, 4, 2, 3, 2, 3, 3, 4, 1, 3, 3, 2, 2, 3, 1, 2, 3, 2, 3, 1,
+ 3, 4, 2, 3, 4, 1, 2, 4, 4, 4, 1, 2, 1, 4, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 2, 4, 3, 4, 2, 2, 3, 4, 3, 4, 4, 1, 4, 1, 4, 3, 3, 1, 2, 1, 2, 2, 2, 4, 4, 1, 1, 4, 3, 1, 2, 2, 3, 1, 4, 1, 1, 1, 2, 2, 1, 4, 3, 4, 1, 4, 2, 1, 2, 1, 4, 1, 2, 1, 3, 2, 3, 3, 4, 4, 4, 3, 4, 1, 4, 4, 1, 4, 3, 3, 3, 1, 1, 3, 1, 1,
+ 2, 3, 2, 4, 3, 4, 4, 1, 4, 2, 4, 2, 1, 3, 4, 1, 2, 2, 3, 3, 2, 1, 3, 1, 4, 3, 1, 4, 2, 3, 2, 4, 1, 2, 1, 4, 4, 3, 3, 3, 1, 1, 3, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 4, 4, 2, 2, 4, 3, 4, 4, 3, 4, 1, 4, 4, 3, 4, 4, 1, 1, 2, 1, 3, 1, 2, 4, 3, 1, 4, 3, 1, 3, 4, 1, 2, 3, 3, 2, 4, 3, 1, 4, 2, 4, 1, 3, 1, 1, 2,
+ 3, 1, 4, 4, 4, 1, 1, 2, 1, 3, 2, 3, 3, 1, 1, 4, 1, 2, 1, 2, 1, 3, 3, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 4, 1, 2, 4, 3, 2, 2, 1, 4, 4, 2, 2, 4, 4, 4, 1, 3, 4, 4, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, 4, 3, 3, 4, 2, 3, 4, 3, 4, 2, 1, 1, 1, 4, 3, 2, 4, 1, 2, 2, 4, 1, 1, 4, 2, 4, 4, 4, 4, 2, 2, 4, 2, 1, 1, 4,
+ 1, 4, 4, 1, 3, 4, 4, 4, 3, 2, 3, 1, 1, 3, 4, 2, 3, 1, 3, 4, 2, 1, 1, 3, 1, 2, 4, 3, 2, 4, 3, 4, 1, 1, 2, 4, 1, 4, 1, 1, 3, 3, 2, 1, 3, 1, 4, 1, 4, 1, 1, 1, 2, 2, 4, 2, 2, 3, 2, 4, 1, 3, 4, 4, 1, 2, 2, 1, 2, 3, 1, 4, 4, 1, 4, 3, 2, 4, 2, 4, 3, 1, 2, 4, 3, 2, 3, 3, 4, 4, 3, 4, 4, 2, 2, 3, 1, 3, 2, 2,
+ 4, 1, 1, 1, 3, 1, 1, 1, 1, 2, 3, 1, 4, 3, 4, 3, 2, 4, 1, 1, 4, 2, 1, 2, 1, 2, 1, 2, 1, 3, 2, 1, 1, 2, 4, 2, 1, 1, 4, 1, 4, 1, 1, 4, 1, 1, 1, 4, 3, 1, 4, 3, 1, 4, 1, 4, 1, 2, 3, 3, 4, 1, 3, 3, 4, 1, 2, 1, 2, 2, 3, 2, 4, 3, 1, 3, 2, 2, 3, 3, 3, 4, 1, 2, 3, 4, 2, 1, 1, 3, 4, 3, 4, 1, 3, 2, 4, 4, 1, 3,
+ 4, 2, 3, 4, 1, 2, 1, 4, 4, 1, 3, 4, 4, 4, 3, 4, 4, 1, 3, 1, 4, 1, 2, 4, 2, 2, 2, 4, 1, 2, 2, 1, 2, 4, 2, 4, 3, 4, 2, 4, 3, 1, 3, 1, 3, 2, 1, 2, 4, 4, 2, 2, 4, 4, 4, 4, 1, 2, 1, 4, 2, 3, 2, 1, 3, 4, 1, 4, 1, 3, 4, 1, 4, 3, 1, 1, 3, 2, 1, 3, 1, 1, 4, 3, 4, 2, 3, 4, 2, 4, 2, 2, 2, 4, 2, 3, 3, 1, 2, 2,
+ 3, 4, 2, 4, 1, 4, 4, 2, 2, 4, 2, 4, 2, 3, 3, 2, 4, 4, 2, 1, 2, 4, 2, 2, 1, 3, 2, 1, 2, 4, 2, 1, 2, 2, 1, 4, 3, 1, 2, 2, 2, 3, 2, 3, 3, 1, 4, 4, 2, 2, 3, 2, 3, 4, 1, 3, 3, 1, 2, 2, 4, 2, 4, 3, 3, 4, 4, 1, 1, 1, 2, 3, 3, 4, 1, 3, 3, 3, 1, 1, 4, 3, 3, 1, 2, 1, 2, 1, 1, 3, 1, 4, 4, 3, 3, 4, 4, 2, 2, 4,
+ 3, 4, 1, 2, 2, 4, 1, 1, 3, 4, 3, 1, 1, 1, 1, 2, 2, 4, 3, 3, 2, 3, 1, 1, 3, 2, 4, 4, 2, 3, 2, 1, 1, 1, 1, 2, 2, 4, 3, 1, 1, 4, 3, 1, 1, 3, 1, 2, 3, 4, 4, 3, 2, 2, 4, 4, 4, 3, 1, 1, 1, 3, 1, 4, 3, 4, 2, 4, 2, 3, 4, 1, 4, 3, 1, 3, 3, 2, 4, 4, 1, 3, 2, 3, 2, 2, 2, 3, 2, 2, 2, 1, 2, 2, 4, 2, 4, 3, 2, 1,
+ 4, 3, 3, 1, 2, 4, 4, 2, 2, 3, 1, 3, 1, 2, 4, 3, 3, 1, 1, 2, 2, 2, 1, 4, 4, 4, 4, 1, 3, 4, 2, 3, 1, 1, 2, 4, 2, 1, 2, 3, 2, 1, 1, 3, 3, 2, 3, 2, 3, 3, 3, 4, 1, 1, 3, 4, 3, 2, 4, 1, 1, 2, 4, 1, 2, 3, 1, 2, 3, 1, 4, 2, 1, 2, 1, 4, 1, 3, 2, 4, 2, 2, 1, 3, 2, 4, 4, 2, 2, 1, 4, 4, 4, 1, 3, 4, 1, 1, 2, 1,
+ 2, 4, 3, 1, 4, 4, 2, 2, 1, 4, 1, 1, 2, 3, 4, 3, 4, 1, 4, 3, 1, 1, 4, 3, 1, 4, 4, 2, 2, 3, 1, 3, 1, 2, 4, 3, 3, 4, 4, 4, 3, 4, 3, 3, 2, 2, 4, 1, 3, 3, 2, 2, 3, 1, 3, 3, 2, 4, 3, 2, 3, 4, 3, 1, 2, 2, 4, 2, 4, 3, 4, 1, 1, 3, 3, 1, 1, 1, 2, 1, 4, 4, 1, 3, 1, 2, 4, 4, 1, 2, 2, 2, 4, 2, 3, 1, 2, 1, 3, 1,
+ 4, 4, 3, 3, 2, 4, 3, 1, 2, 2, 2, 4, 4, 2, 3, 3, 4, 2, 3, 4, 1, 4, 2, 3, 1, 2, 2, 2, 4, 4, 4, 3, 1, 1, 2, 4, 1, 3, 4, 4, 1, 3, 4, 2, 4, 4, 1, 1, 4, 4, 1, 3, 1, 3, 2, 2, 1, 4, 2, 2, 3, 4, 4, 4, 2, 3, 3, 2, 3, 4, 3, 4, 2, 3, 3, 1, 3, 2, 3, 3, 4, 2, 4, 3, 3, 4, 2, 3, 4, 1, 4, 3, 2, 3, 1, 1, 4, 2, 2, 2,
+ 2, 1, 3, 4, 4, 3, 4, 2, 4, 1, 1, 3, 3, 4, 4, 3, 1, 4, 1, 1, 4, 2, 1, 4, 2, 3, 2, 2, 2, 1, 1, 2, 2, 4, 1, 1, 3, 4, 4, 1, 4, 2, 4, 1, 2, 2, 1, 2, 1, 1, 4, 1, 3, 3, 1, 2, 1, 3, 1, 4, 1, 3, 2, 3, 2, 3, 4, 1, 2, 1, 2, 2, 4, 2, 2, 2, 2, 3, 4, 1, 3, 4, 1, 2, 4, 4, 4, 3, 3, 2, 1, 1, 2, 2, 4, 4, 1, 4, 2, 1,
+ 4, 1, 2, 3, 3, 3, 4, 4, 2, 1, 1, 4, 4, 2, 3, 1, 4, 2, 3, 2, 4, 4, 4, 4, 2, 4, 1, 4, 3, 3, 4, 1, 2, 1, 3, 4, 4, 3, 3, 2, 1, 3, 3, 2, 3, 4, 1, 4, 4, 1, 3, 3, 1, 2, 4, 1, 2, 4, 3, 2, 1, 2, 1, 1, 2, 2, 3, 1, 3, 3, 2, 1, 3, 4, 4, 1, 2, 3, 3, 3, 2, 4, 3, 1, 3, 2, 3, 4, 1, 2, 2, 4, 2, 1, 2, 2, 2, 1, 4, 2,
+ 1, 1, 1, 1, 4, 2, 4, 1, 4, 2, 2, 3, 4, 3, 1, 2, 2, 3, 3, 2, 1, 1, 4, 4, 4, 1, 1, 1, 3, 3, 2, 1, 4, 3, 2, 2, 3, 4, 3, 1, 1, 4, 4, 4, 1, 2, 1, 1, 4, 3, 4, 1, 4, 2, 3, 1, 1, 4, 3, 4, 2, 4, 3, 2, 2, 3, 1, 3, 2, 2, 2, 3, 4, 2, 2, 4, 3, 3, 1, 4, 4, 1, 1, 2, 3, 1, 2, 4, 4, 4, 4, 3, 1, 2, 1, 1, 4, 2, 2, 4,
+ 1, 2, 4, 4, 4, 3, 1, 1, 4, 1, 3, 1, 4, 3, 1, 2, 1, 1, 1, 3, 4, 2, 2, 4, 3, 2, 4, 4, 2, 4, 2, 3, 1, 3, 1, 2, 2, 1, 2, 1, 3, 4, 2, 4, 3, 3, 2, 3, 1, 3, 2, 3, 4, 4, 4, 3, 4, 2, 4, 1, 4, 4, 3, 1, 3, 2, 1, 4, 2, 1, 3, 3, 1, 4, 1, 3, 4, 4, 3, 4, 4, 1, 4, 4, 1, 2, 4, 4, 3, 3, 4, 1, 2, 1, 4, 3, 4, 4, 1, 2,
+ 2, 4, 2, 2, 2, 4, 3, 1, 1, 2, 4, 1, 1, 3, 4, 3, 3, 4, 3, 3, 2, 3, 4, 3, 2, 3, 4, 3, 2, 3, 2, 2, 1, 1, 3, 4, 3, 2, 4, 4, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 2, 1, 3, 2, 2, 4, 1, 4, 4, 2, 3, 4, 4, 4, 1, 1, 2, 1, 1, 3, 2, 4, 2, 4, 1, 2, 4, 3, 1, 4, 1, 4, 1, 2, 1, 3, 3, 1, 3, 1, 2, 2, 1, 2, 1, 3, 4, 3, 2, 2,
+ 3, 3, 3, 1, 4, 3, 2, 3, 4, 2, 4, 1, 2, 4, 3, 1, 4, 4, 1, 4, 4, 4, 4, 1, 4, 2, 3, 1, 3, 2, 1, 1, 3, 2, 1, 2, 4, 1, 4, 1, 2, 1, 4, 1, 1, 1, 3, 1, 1, 1, 1, 3, 2, 3, 2, 2, 2, 3, 1, 3, 2, 1, 3, 1, 3, 4, 4, 4, 1, 2, 1, 3, 1, 3, 1, 3, 2, 4, 3, 4, 2, 4, 4, 1, 4, 2, 1, 4, 1, 3, 1, 3, 4, 1, 3, 1, 1, 1, 3, 1,
+ 4, 3, 4, 1, 1, 3, 3, 4, 2, 4, 1, 4, 2, 3, 3, 2, 1, 3, 3, 4, 4, 4, 3, 2, 1, 2, 1, 2, 3, 1, 4, 1, 2, 4, 4, 4, 1, 2, 4, 1, 3, 3, 3, 4, 2, 1, 4, 2, 2, 1, 2, 3, 2, 1, 2, 1, 3, 3, 1, 1, 4, 2, 1, 4, 2, 2, 2, 4, 1, 2, 2, 2, 1, 4, 4, 2, 2, 4, 3, 4, 1, 4, 2, 4, 4, 4, 4, 2, 3, 2, 3, 1, 1, 2, 2, 2, 1, 4, 3, 4,
+ 2, 2, 2, 2, 4, 4, 2, 4, 4, 1, 3, 3, 4, 4, 1, 4, 4, 1, 1, 3, 4, 1, 4, 1, 3, 4, 3, 2, 3, 3, 1, 4, 3, 2, 1, 3, 4, 2, 4, 2, 2, 3, 1, 2, 2, 4, 1, 4, 3, 2, 4, 4, 4, 3, 4, 3, 3, 1, 4, 3, 1, 3, 2, 4, 4, 1, 3, 4, 3, 4, 2, 4, 1, 4, 3, 2, 4, 4, 2, 1, 2, 1, 3, 1, 4, 2, 1, 4, 3, 2, 1, 1, 1, 2, 3, 1, 4, 1, 1, 1,
+ 1, 4, 4, 2, 2, 1, 4, 2, 1, 3, 1, 1, 2, 3, 2, 4, 1, 2, 4, 3, 2, 1, 1, 1, 1, 3, 4, 3, 3, 2, 1, 1, 1, 3, 1, 1, 2, 3, 4, 1, 2, 1, 1, 2, 3, 4, 2, 1, 1, 1, 4, 4, 2, 3, 4, 4, 2, 1, 1, 3, 3, 2, 3, 4, 3, 2, 3, 2, 3, 4, 4, 3, 3, 3, 2, 4, 4, 1, 1, 2, 1, 2, 2, 3, 4, 4, 4, 1, 1, 2, 1, 4, 1, 1, 2, 3, 1, 3, 2, 2,
+ 3, 1, 4, 4, 2, 1, 4, 1, 4, 4, 3, 4, 1, 2, 3, 1, 4, 3, 4, 4, 2, 3, 2, 2, 3, 2, 4, 1, 1, 4, 3, 2, 3, 4, 1, 2, 1, 2, 1, 4, 1, 1, 1, 4, 2, 3, 4, 1, 3, 3, 2, 2, 1, 3, 2, 3, 4, 4, 3, 2, 3, 1, 2, 4, 1, 2, 3, 1, 2, 3, 2, 3, 2, 2, 1, 4, 1, 4, 4, 3, 3, 4, 3, 3, 3, 4, 1, 2, 3, 2, 1, 1, 2, 1, 2, 4, 3, 3, 2, 1,
+ 3, 3, 1, 3, 3, 3, 2, 2, 3, 3, 4, 1, 3, 2, 1, 3, 1, 1, 1, 4, 4, 1, 1, 1, 2, 1, 2, 2, 3, 1, 4, 2, 1, 4, 3, 2, 3, 4, 1, 4, 2, 1, 1, 4, 1, 1, 1, 1, 3, 1, 4, 1, 2, 4, 3, 1, 3, 2, 3, 1, 3, 3, 2, 4, 4, 3, 3, 2, 4, 1, 1, 2, 1, 3, 2, 3, 2, 2, 1, 4, 1, 2, 4, 2, 3, 3, 2, 2, 1, 3, 2, 2, 1, 2, 3, 1, 3, 1, 2, 1,
+ 2, 1, 1, 4, 4, 3, 2, 2, 2, 3, 2, 4, 3, 1, 4, 1, 1, 2, 2, 2, 4, 4, 3, 2, 2, 1, 3, 2, 3, 2, 2, 4, 1, 4, 3, 1, 3, 3, 1, 1, 4, 1, 3, 4, 2, 2, 2, 3, 2, 3, 2, 4, 4, 4, 2, 4, 2, 4, 3, 3, 4, 3, 3, 2, 1, 4, 1, 4, 1, 4, 3, 2, 2, 2, 4, 1, 2, 4, 3, 1, 1, 2, 4, 3, 2, 4, 1, 1, 3, 2, 1, 3, 1, 3, 3, 4, 1, 2, 4, 1,
+ 2, 2, 1, 3, 3, 4, 3, 4, 3, 3, 1, 1, 4, 4, 2, 1, 1, 2, 4, 4, 2, 3, 4, 2, 1, 4, 3, 3, 1, 4, 1, 2, 4, 2, 2, 3, 3, 3, 4, 4, 4, 1, 3, 4, 4, 4, 4, 2, 1, 4, 1, 2, 3, 4, 3, 1, 4, 2, 4, 4, 3, 3, 2, 3, 2, 4, 2, 1, 3, 2, 4, 2, 2, 2, 2, 3, 2, 2, 3, 3, 4, 3, 4, 2, 1, 3, 3, 4, 4, 3, 1, 2, 1, 1, 3, 3, 1, 2, 2, 3,
+ 2, 3, 4, 4, 4, 3, 4, 3, 3, 3, 2, 2, 1, 2, 4, 1, 2, 4, 4, 2, 4, 4, 1, 1, 3, 2, 2, 2, 4, 4, 3, 4, 4, 2, 1, 1, 3, 2, 1, 3, 3, 1, 3, 2, 2, 1, 1, 1, 3, 2, 2, 3, 2, 3, 2, 3, 1, 4, 2, 1, 1, 2, 2, 3, 2, 1, 3, 4, 2, 3, 2, 3, 4, 1, 4, 3, 1, 4, 3, 4, 4, 4, 1, 3, 1, 1, 2, 2, 3, 3, 1, 4, 3, 1, 3, 3, 1, 2, 1, 3,
+ 2, 3, 1, 4, 3, 1, 1, 3, 3, 2, 2, 1, 3, 2, 1, 2, 1, 1, 1, 2, 4, 1, 3, 3, 4, 1, 4, 3, 1, 2, 3, 3, 2, 2, 2, 3, 3, 4, 3, 2, 4, 2, 4, 3, 4, 2, 4, 1, 1, 1, 3, 4, 4, 3, 2, 2, 1, 2, 2, 4, 3, 2, 1, 2, 2, 4, 4, 4, 3, 1, 2, 4, 3, 1, 1, 4, 3, 1, 2, 3, 1, 4, 4, 3, 1, 4, 1, 4, 3, 2, 2, 2, 1, 2, 1, 3, 2, 3, 3, 4,
+ 1, 2, 3, 3, 2, 1, 3, 1, 2, 3, 4, 2, 1, 3, 3, 4, 4, 2, 2, 3, 1, 2, 4, 3, 1, 4, 4, 1, 3, 4, 3, 4, 3, 3, 3, 4, 2, 3, 1, 2, 2, 1, 1, 4, 4, 4, 1, 3, 1, 1, 1, 4, 4, 2, 1, 2, 3, 4, 3, 1, 2, 3, 3, 4, 1, 1, 4, 1, 1, 3, 4, 3, 2, 2, 3, 4, 2, 3, 3, 3, 1, 4, 3, 3, 4, 3, 3, 3, 1, 4, 1, 4, 4, 4, 2, 2, 1, 1, 1, 2,
+ 3, 3, 3, 1, 1, 2, 1, 1, 3, 2, 3, 4, 3, 2, 3, 1, 2, 4, 4, 1, 3, 4, 3, 3, 4, 3, 1, 4, 2, 3, 4, 1, 3, 3, 4, 1, 4, 2, 1, 4, 2, 2, 1, 2, 1, 3, 3, 4, 2, 2, 1, 2, 1, 1, 3, 1, 3, 3, 1, 3, 1, 2, 1, 3, 4, 2, 2, 3, 4, 4, 1, 1, 1, 3, 2, 2, 3, 1, 1, 3, 1, 1, 3, 3, 4, 2, 1, 3, 3, 2, 1, 4, 2, 2, 1, 4, 4, 2, 4, 3,
+ 3, 4, 1, 2, 2, 4, 4, 4, 3, 1, 2, 4, 2, 4, 3, 2, 4, 3, 1, 3, 3, 3, 3, 2, 1, 1, 1, 4, 2, 3, 4, 4, 3, 4, 3, 3, 1, 1, 4, 1, 1, 1, 1, 4, 2, 1, 2, 1, 2, 4, 3, 4, 4, 4, 2, 2, 1, 1, 4, 4, 3, 4, 4, 3, 1, 3, 2, 2, 3, 3, 1, 4, 2, 1, 3, 1, 3, 1, 3, 1, 4, 4, 4, 2, 4, 4, 2, 2, 4, 4, 4, 4, 3, 2, 2, 1, 4, 4, 4, 3,
+ 4, 4, 1, 1, 4, 2, 2, 4, 4, 3, 1, 3, 1, 4, 4, 4, 4, 4, 1, 3, 1, 4, 4, 4, 1, 1, 4, 3, 1, 3, 4, 2, 1, 4, 1, 2, 2, 4, 4, 1, 1, 4, 4, 1, 1, 4, 4, 2, 2, 4, 3, 1, 2, 2, 1, 2, 4, 2, 4, 1, 4, 4, 3, 1, 1, 2, 3, 2, 4, 3, 4, 1, 3, 2, 4, 3, 1, 1, 4, 3, 3, 4, 3, 1, 1, 2, 4, 1, 1, 3, 1, 2, 4, 1, 4, 1, 3, 1, 2, 3,
+ 3, 2, 4, 2, 1, 3, 3, 3, 4, 3, 3, 2, 4, 4, 1, 1, 2, 2, 2, 3, 2, 1, 2, 4, 3, 3, 3, 1, 4, 2, 2, 1, 1, 3, 2, 1, 3, 3, 2, 2, 3, 1, 1, 3, 2, 3, 1, 2, 4, 4, 1, 2, 4, 1, 4, 3, 3, 4, 4, 2, 4, 3, 4, 4, 4, 2, 3, 2, 3, 4, 3, 2, 3, 3, 4, 1, 2, 4, 1, 1, 3, 2, 4, 4, 4, 4, 2, 4, 3, 4, 1, 4, 1, 1, 3, 2, 4, 2, 1, 2,
+ 4, 2, 2, 4, 3, 3, 4, 2, 2, 1, 2, 4, 2, 1, 1, 4, 2, 3, 2, 1, 1, 1, 3, 4, 2, 3, 4, 3, 1, 4, 2, 1, 3, 2, 2, 2, 2, 3, 2, 4, 1, 1, 1, 3, 3, 2, 2, 4, 4, 4, 4, 1, 1, 1, 3, 3, 3, 3, 1, 3, 1, 3, 2, 1, 2, 1, 2, 3, 3, 3, 3, 1, 3, 3, 4, 2, 1, 3, 3, 2, 1, 1, 4, 3, 3, 1, 1, 4, 1, 4, 4, 2, 2, 1, 1, 2, 2, 4, 4, 3,
+ 1, 4, 1, 1, 1, 3, 3, 1, 2, 3, 1, 3, 4, 4, 1, 4, 2, 2, 2, 2, 1, 4, 3, 4, 4, 3, 2, 1, 4, 1, 4, 4, 2, 3, 4, 1, 2, 3, 2, 1, 3, 4, 3, 3, 3, 3, 4, 2, 2, 4, 1, 3, 1, 1, 3, 1, 1, 2, 3, 1, 2, 3, 4, 1, 4, 2, 2, 2, 2, 2, 4, 3, 3, 2, 1, 2, 1, 4, 3, 2, 3, 2, 4, 2, 2, 2, 1, 4, 4, 4, 1, 3, 2, 3, 3, 4, 3, 3, 1, 3,
+ 1, 4, 4, 3, 2, 1, 2, 3, 1, 2, 1, 3, 4, 4, 2, 4, 4, 4, 1, 1, 2, 1, 1, 2, 4, 3, 3, 1, 4, 2, 2, 2, 2, 1, 4, 1, 1, 3, 4, 1, 2, 3, 4, 2, 4, 3, 4, 2, 1, 1, 2, 3, 2, 3, 4, 4, 2, 1, 4, 3, 1, 2, 2, 1, 3, 2, 2, 4, 3, 2, 1, 4, 4, 2, 4, 3, 4, 3, 4, 2, 2, 2, 3, 4, 2, 2, 2, 4, 2, 2, 4, 3, 4, 4, 4, 4, 2, 4, 2, 3,
+ 4, 2, 4, 2, 2, 3, 2, 1, 3, 1, 2, 3, 3, 1, 2, 1, 1, 1, 2, 4, 4, 3, 2, 4, 1, 1, 1, 4, 4, 2, 2, 1, 1, 2, 3, 2, 1, 4, 4, 3, 3, 4, 1, 1, 4, 1, 3, 3, 4, 4, 1, 4, 4, 4, 1, 3, 3, 3, 2, 4, 2, 3, 4, 1, 4, 1, 4, 4, 2, 4, 2, 4, 3, 2, 3, 3, 2, 1, 1, 4, 1, 1, 1, 3, 4, 2, 1, 3, 3, 3, 1, 3, 1, 1, 2, 3, 2, 4, 4, 4,
+ 4, 1, 4, 4, 4, 4, 1, 4, 4, 4, 1, 2, 4, 1, 1, 3, 3, 4, 2, 3, 2, 1, 1, 2, 1, 1, 3, 4, 2, 1, 2, 3, 3, 4, 4, 1, 3, 2, 2, 2, 2, 4, 2, 3, 2, 2, 4, 1, 1, 4, 1, 4, 4, 3, 2, 3, 3, 4, 3, 2, 2, 2, 3, 1, 2, 1, 4, 4, 4, 4, 4, 1, 1, 3, 1, 4, 1, 4, 4, 1, 3, 4, 1, 2, 1, 4, 2, 1, 1, 4, 1, 3, 2, 2, 4, 3, 4, 2, 3, 1],
+ "height":100,
+ "id":1,
+ "name":"background",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":100,
+ "x":0,
+ "y":0
+ }],
+ "nextlayerid":2,
+ "nextobjectid":1,
+ "orientation":"orthogonal",
+ "renderorder":"right-down",
+ "tiledversion":"1.10.1",
+ "tileheight":16,
+ "tilesets":[
+ {
+ "columns":8,
+ "firstgid":1,
+ "image":"..\/tiles\/tileset.png",
+ "imageheight":15968,
+ "imagewidth":128,
+ "margin":0,
+ "name":"pokemon",
+ "spacing":0,
+ "tilecount":7984,
+ "tileheight":16,
+ "tilewidth":16
+ }],
+ "tilewidth":16,
+ "type":"map",
+ "version":"1.10",
+ "width":100
+}
\ No newline at end of file
diff --git a/ui/dist/assets/tilemaps/json/town.json b/ui/dist/assets/tilemaps/json/town.json
new file mode 100644
index 000000000..714476b5c
--- /dev/null
+++ b/ui/dist/assets/tilemaps/json/town.json
@@ -0,0 +1,72401 @@
+{ "compressionlevel":-1,
+ "height":35,
+ "infinite":false,
+ "layers":[
+ {
+ "data":[4, 1, 1, 4, 3, 2, 2, 1, 3, 4, 2, 2, 1, 4, 2, 1, 3, 3, 4, 2, 2, 3, 4, 2, 4, 3, 1, 2, 2, 2, 4, 1, 3, 2, 3, 1, 4, 3, 2, 3, 3, 2, 2, 3, 3, 1, 1, 1,
+ 3, 1, 4, 2, 2, 1, 3, 4, 4, 3, 1, 2, 4, 3, 1, 3, 4, 4, 4, 4, 3, 2, 25, 26, 27, 3, 3, 3, 2, 2, 1, 1, 1, 4, 2, 3, 4, 3, 3, 3, 2, 3, 3, 1, 1, 3, 2, 2,
+ 1, 2, 3, 4, 1, 4, 2, 3, 3, 4, 4, 1, 3, 2, 4, 3, 2, 4, 3, 4, 4, 2, 25, 26, 27, 3, 3, 2, 3, 2, 17, 18, 18, 18, 18, 18, 18, 19, 1, 4, 4, 1, 3, 2, 1, 2, 3, 3,
+ 4, 3, 1, 4, 4, 4, 2, 1, 3, 4, 4, 3, 4, 1, 2, 4, 3, 2, 2, 3, 1, 2, 25, 26, 27, 1, 2, 3, 3, 2, 25, 26, 26, 26, 26, 26, 26, 27, 3, 3, 2, 2, 3, 2, 2, 4, 4, 3,
+ 2, 3, 4, 1, 1, 2, 2, 4, 1, 1, 17, 18, 19, 3, 1, 3, 2, 1, 1, 4, 4, 3, 25, 26, 27, 4, 3, 4, 1, 3, 25, 26, 45, 34, 34, 44, 26, 27, 4, 3, 1, 4, 4, 2, 1, 3, 4, 4,
+ 4, 1, 4, 4, 2, 2, 1, 1, 3, 3, 25, 26, 27, 2, 4, 4, 2, 3, 4, 4, 2, 2, 25, 26, 27, 4, 2, 3, 2, 4, 25, 26, 27, 1, 3, 25, 26, 27, 3, 4, 4, 2, 4, 4, 3, 2, 3, 2,
+ 2, 4, 4, 3, 3, 2, 4, 1, 3, 3, 4, 1, 3, 2, 1, 1, 3, 4, 4, 4, 3, 1, 25, 26, 27, 3, 4, 2, 1, 1, 2, 3, 4, 1, 2, 25, 26, 27, 2, 4, 4, 4, 1, 1, 4, 3, 1, 4,
+ 3, 2, 1, 2, 2, 4, 4, 2, 4, 4, 2, 4, 4, 2, 4, 4, 4, 1, 4, 4, 3, 3, 25, 26, 27, 4, 4, 1, 4, 2, 2, 1, 2, 2, 4, 25, 26, 27, 2, 3, 1, 1, 3, 2, 4, 2, 1, 2,
+ 4, 3, 1, 1, 4, 1, 3, 3, 1, 4, 3, 2, 1, 1, 1, 1, 4, 2, 3, 2, 4, 2, 25, 26, 27, 1, 3, 3, 2, 2, 1, 4, 1, 3, 1, 25, 26, 27, 2, 1, 4, 2, 3, 4, 1, 3, 1, 2,
+ 3, 3, 1, 2, 1, 4, 1, 1, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 36, 26, 37, 18, 18, 18, 18, 18, 18, 18, 18, 19, 2, 25, 26, 27, 4, 3, 3, 3, 4, 2, 2, 1, 4, 2,
+ 4, 3, 2, 2, 2, 2, 3, 1, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 2, 25, 26, 27, 1, 2, 2, 4, 1, 4, 4, 1, 4, 4,
+ 3, 2, 4, 4, 2, 4, 3, 2, 25, 26, 45, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 4, 25, 26, 27, 4, 3, 2, 1, 2, 3, 1, 4, 3, 3,
+ 4, 3, 3, 2, 3, 2, 1, 2, 25, 26, 27, 1, 2, 3, 3, 3, 3, 2, 2, 4, 4, 2, 2, 2, 3, 2, 4, 4, 1, 2, 2, 2, 2, 3, 4, 25, 26, 27, 3, 2, 1, 4, 3, 2, 4, 1, 3, 2,
+ 4, 4, 1, 3, 3, 4, 4, 2, 25, 26, 27, 4, 2, 3, 3, 4, 1, 3, 2, 1, 2, 3, 4, 4, 4, 2, 2, 1, 1, 3, 1, 1, 2, 2, 3, 25, 26, 27, 1, 3, 1, 4, 4, 2, 3, 1, 3, 3,
+ 3, 2, 2, 3, 3, 4, 3, 2, 25, 26, 27, 1, 1, 4, 4, 2, 4, 4, 2, 4, 1, 4, 3, 1, 3, 2, 4, 1, 3, 1, 4, 3, 4, 2, 1, 25, 26, 27, 2, 4, 2, 4, 1, 4, 1, 4, 1, 4,
+ 2, 4, 4, 2, 2, 1, 3, 2, 25, 26, 37, 18, 18, 18, 18, 18, 18, 18, 18, 19, 2, 1, 3, 3, 1, 3, 1, 3, 2, 1, 2, 3, 1, 3, 3, 25, 26, 27, 3, 4, 4, 2, 4, 4, 1, 2, 4, 2,
+ 3, 2, 2, 4, 4, 1, 1, 4, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 2, 1, 3, 2, 4, 4, 3, 1, 1, 4, 3, 3, 4, 2, 1, 25, 26, 27, 4, 4, 2, 3, 3, 3, 2, 1, 3, 4,
+ 18, 18, 18, 18, 18, 18, 18, 18, 36, 26, 45, 34, 34, 34, 34, 34, 34, 44, 26, 27, 3, 2, 3, 1, 1, 2, 2, 1, 2, 4, 4, 2, 3, 3, 4, 25, 26, 27, 2, 2, 3, 4, 3, 1, 1, 2, 4, 4,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 2, 3, 1, 1, 3, 2, 25, 26, 27, 1, 2, 1, 4, 2, 2, 3, 4, 3, 1, 1, 4, 2, 2, 2, 25, 26, 27, 1, 1, 2, 4, 3, 1, 4, 2, 1, 3,
+ 34, 34, 34, 34, 34, 34, 34, 34, 44, 26, 27, 1, 3, 3, 3, 4, 4, 25, 26, 37, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 36, 26, 27, 3, 3, 1, 2, 3, 4, 2, 4, 1, 2,
+ 1, 1, 2, 2, 1, 2, 1, 4, 25, 26, 27, 1, 2, 3, 4, 3, 4, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 2, 2, 1, 4, 1, 2, 3, 2, 4, 1,
+ 2, 4, 1, 1, 2, 1, 3, 2, 25, 26, 27, 3, 3, 3, 1, 3, 3, 25, 26, 45, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 3, 3, 4, 3, 1, 1, 3, 4, 4, 1,
+ 1, 4, 1, 4, 1, 4, 1, 2, 25, 26, 27, 1, 1, 3, 1, 3, 1, 25, 26, 27, 3, 4, 3, 1, 3, 4, 1, 4, 3, 2, 4, 3, 2, 2, 2, 3, 3, 2, 2, 1, 2, 4, 2, 4, 3, 3, 1, 4,
+ 1, 1, 1, 3, 1, 2, 3, 4, 25, 26, 27, 1, 3, 4, 2, 2, 2, 25, 26, 27, 4, 4, 1, 3, 2, 1, 4, 3, 2, 4, 2, 4, 4, 1, 2, 3, 2, 3, 2, 2, 1, 3, 2, 2, 3, 3, 3, 3,
+ 1, 4, 3, 4, 2, 1, 1, 1, 25, 26, 27, 4, 2, 3, 2, 1, 1, 25, 26, 27, 2, 2, 3, 3, 4, 4, 2, 4, 1, 1, 3, 1, 4, 2, 3, 4, 1, 3, 1, 1, 2, 1, 1, 3, 2, 4, 1, 3,
+ 1, 4, 3, 3, 3, 4, 2, 2, 25, 26, 27, 1, 2, 3, 4, 2, 3, 25, 26, 27, 4, 1, 3, 3, 2, 3, 1, 1, 2, 1, 4, 4, 3, 4, 4, 1, 4, 4, 4, 1, 4, 1, 1, 2, 1, 4, 4, 1,
+ 2, 1, 1, 2, 4, 4, 1, 4, 25, 26, 37, 18, 18, 18, 18, 18, 18, 36, 26, 27, 2, 1, 3, 2, 4, 1, 2, 3, 4, 2, 3, 2, 3, 3, 2, 4, 2, 2, 4, 1, 1, 1, 4, 2, 4, 3, 2, 3,
+ 1, 3, 2, 4, 4, 1, 2, 3, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 4, 4, 1, 1, 4, 1, 3, 4, 4, 4, 3, 1, 2, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 1, 1, 1, 3, 2,
+ 3, 2, 1, 3, 4, 3, 4, 3, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 4, 4, 4, 3, 3, 4, 3, 2, 4, 1, 3, 4, 4, 3, 1, 4, 1, 2, 1, 2, 2, 3, 2, 3, 4, 2, 1, 4,
+ 4, 1, 1, 3, 2, 1, 2, 3, 1, 3, 3, 3, 4, 1, 2, 3, 2, 2, 3, 3, 2, 4, 2, 1, 1, 4, 4, 3, 3, 2, 4, 2, 4, 2, 3, 4, 1, 2, 3, 1, 4, 2, 1, 3, 4, 4, 3, 4,
+ 1, 2, 4, 3, 2, 3, 1, 2, 1, 1, 3, 3, 2, 4, 2, 2, 1, 1, 4, 3, 4, 3, 3, 3, 4, 1, 4, 2, 4, 2, 2, 4, 2, 1, 3, 2, 1, 4, 1, 1, 3, 1, 3, 3, 2, 2, 2, 2,
+ 3, 2, 1, 4, 1, 3, 2, 3, 1, 3, 1, 1, 4, 4, 4, 4, 1, 1, 4, 4, 4, 1, 2, 3, 3, 2, 1, 3, 3, 3, 4, 4, 4, 4, 4, 2, 1, 1, 2, 3, 3, 3, 3, 2, 2, 1, 4, 4,
+ 4, 2, 2, 2, 3, 3, 2, 2, 1, 4, 1, 1, 1, 4, 1, 2, 4, 1, 3, 4, 4, 4, 1, 3, 3, 2, 3, 1, 3, 1, 1, 1, 4, 4, 1, 4, 1, 2, 4, 4, 1, 3, 3, 4, 1, 4, 3, 2,
+ 2, 2, 4, 3, 2, 3, 1, 4, 1, 3, 4, 3, 2, 3, 1, 3, 2, 2, 2, 3, 4, 2, 1, 1, 1, 2, 3, 2, 2, 2, 2, 2, 4, 3, 4, 4, 3, 4, 1, 3, 2, 3, 4, 3, 3, 4, 1, 2,
+ 3, 4, 3, 2, 3, 2, 3, 2, 4, 3, 1, 3, 2, 3, 3, 4, 4, 1, 4, 4, 3, 1, 3, 2, 2, 1, 1, 1, 4, 1, 1, 1, 4, 4, 4, 2, 1, 4, 3, 2, 3, 3, 1, 1, 3, 4, 3, 3],
+ "height":35,
+ "id":1,
+ "name":"ground",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":48,
+ "x":0,
+ "y":0
+ },
+ {
+ "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 219, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 0, 0, 0, 213, 213, 213, 213, 201, 213, 213, 213, 213, 213, 213, 213, 213, 213, 220, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 213, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 0, 0, 0, 213, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 213, 213, 213, 213, 213, 213,
+ 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 221, 221, 221, 221, 221, 221, 221, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 0, 0, 203, 213, 213, 213, 213, 213, 213,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 221, 221, 221, 221, 221, 221, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 213, 29, 213, 213, 213, 213, 213, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 218, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 217, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "height":35,
+ "id":8,
+ "name":"wall",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":48,
+ "x":0,
+ "y":0
+ },
+ {
+ "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 7, 8, 7, 8, 7, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 15, 16, 15, 16, 15, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 21, 22, 5, 6, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 5, 6, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 15, 16, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 21, 22, 21, 22, 21, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 7, 8, 7, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 15, 16, 15, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 7, 8, 7, 8, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 15, 16, 15, 16, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 13, 14, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 13, 14, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 13, 14, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 5, 6, 5, 6, 5, 6, 0, 166, 167, 167, 167, 167, 167, 167, 167, 167, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 168, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 13, 14, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 13, 14, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6,
+ 21, 22, 21, 22, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 22, 21, 22, 21, 22],
+ "height":35,
+ "id":6,
+ "name":"tree",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":48,
+ "x":0,
+ "y":0
+ },
+ {
+ "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1340, 1340, 1340, 1340, 1344, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1340, 1341, 1342, 1343, 1340, 1344, 1339, 1340, 1340, 1340, 1340, 1344, 0, 0, 0, 0, 0, 0, 0, 1339, 1340, 1341, 1342, 1343, 1340, 1344, 0, 0, 0, 1347, 1348, 1348, 1348, 1348, 1352, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1347, 1348, 1349, 1350, 1351, 1348, 1352, 1347, 1348, 1349, 1350, 1351, 1352, 0, 0, 0, 0, 0, 0, 0, 1347, 1348, 1349, 1350, 1351, 1348, 1352, 0, 0, 0, 1355, 1359, 1359, 1359, 1359, 1360, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1355, 1356, 1357, 1358, 1359, 1359, 1360, 1355, 1356, 1357, 1358, 1359, 1360, 0, 0, 0, 0, 0, 0, 0, 1355, 1356, 1357, 1358, 1359, 1359, 1360, 0, 0, 0, 1326, 1327, 1310, 1312, 1335, 1336, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1326, 1318, 1319, 1311, 1327, 1310, 1336, 1326, 1318, 1319, 1320, 1335, 1336, 0, 0, 0, 0, 0, 0, 0, 1326, 1318, 1319, 1311, 1327, 1310, 1336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1340, 1340, 1340, 1340, 1340, 1344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1347, 1348, 1349, 1350, 1351, 1348, 1352, 361, 362, 363, 364, 365, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1355, 1356, 1357, 1358, 1359, 1359, 1360, 369, 370, 371, 372, 373, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1326, 1318, 1319, 1311, 1327, 1310, 1336, 377, 378, 379, 380, 381, 0, 0, 0, 325, 326, 327, 328, 366, 367, 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 385, 386, 387, 388, 389, 0, 0, 0, 333, 334, 335, 336, 374, 375, 376, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393, 394, 395, 396, 397, 0, 0, 0, 341, 342, 343, 344, 382, 383, 384, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 270, 349, 350, 351, 352, 390, 391, 392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 357, 358, 359, 360, 398, 399, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1193, 1194, 1195, 1196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1201, 1202, 1203, 1204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1247, 1248, 1201, 1202, 1203, 1204, 0, 0, 0, 0, 32, 1339, 1340, 1340, 1340, 1340, 1344, 329, 330, 331, 332, 1339, 1340, 1340, 1340, 1344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1247, 1248, 1209, 1210, 1211, 1212, 0, 0, 0, 0, 0, 1347, 1348, 1349, 1350, 1351, 1352, 337, 338, 339, 340, 1347, 1348, 1348, 1348, 1352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1247, 1248, 1217, 1218, 1219, 1220, 0, 0, 0, 0, 0, 1355, 1356, 1357, 1358, 1359, 1360, 345, 346, 347, 348, 1355, 1359, 1359, 1359, 1360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 1225, 1226, 1227, 1228, 0, 0, 0, 0, 0, 1326, 1318, 1319, 1320, 1335, 1336, 353, 354, 355, 356, 1326, 1327, 1334, 1335, 1336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1233, 0, 0, 1233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "height":35,
+ "id":5,
+ "name":"house",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":48,
+ "x":0,
+ "y":0
+ },
+ {
+ "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4338, 0, 4338, 0, 4338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4338, 0, 4338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4338, 0, 4338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4338, 0, 4338, 0, 4338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "height":35,
+ "id":9,
+ "name":"flower",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":48,
+ "x":0,
+ "y":0
+ },
+ {
+ "draworder":"topdown",
+ "id":10,
+ "name":"npcs",
+ "objects":[
+ {
+ "height":0,
+ "id":12,
+ "name":"May",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":0
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":215.333333333333,
+ "y":251.333333333333
+ },
+ {
+ "height":0,
+ "id":13,
+ "name":"Steven",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":2
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":422.666666666667,
+ "y":300
+ },
+ {
+ "height":0,
+ "id":14,
+ "name":"Birch",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":1
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":343.333333333333,
+ "y":426.666666666667
+ },
+ {
+ "height":0,
+ "id":15,
+ "name":"Maxie",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":3
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":151.333333333333,
+ "y":154
+ },
+ {
+ "height":0,
+ "id":16,
+ "name":"Archie",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":4
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":407.333333333333,
+ "y":138.666666666667
+ },
+ {
+ "height":0,
+ "id":17,
+ "name":"Joseph",
+ "point":true,
+ "properties":[
+ {
+ "name":"id",
+ "type":"int",
+ "value":5
+ }],
+ "rotation":0,
+ "type":"npc",
+ "visible":true,
+ "width":0,
+ "x":455.333333333333,
+ "y":155.333333333333
+ }],
+ "opacity":1,
+ "type":"objectgroup",
+ "visible":true,
+ "x":0,
+ "y":0
+ }],
+ "nextlayerid":11,
+ "nextobjectid":18,
+ "orientation":"orthogonal",
+ "renderorder":"right-down",
+ "tiledversion":"1.10.1",
+ "tileheight":16,
+ "tilesets":[
+ {
+ "columns":8,
+ "firstgid":1,
+ "image":"..\/tiles\/tileset.png",
+ "imageheight":15968,
+ "imagewidth":128,
+ "margin":0,
+ "name":"town",
+ "spacing":0,
+ "tilecount":7984,
+ "tileheight":16,
+ "tiles":[
+ {
+ "id":0,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":5,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":6,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":7,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":8,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":9,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":10,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":11,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":12,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":13,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":14,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":15,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":16,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":17,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":18,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":19,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":20,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":21,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":22,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":23,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":24,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":25,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":26,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":27,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":28,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":29,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":30,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":31,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":32,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":33,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":34,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":35,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":36,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":37,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":38,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":39,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":40,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":41,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":42,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":43,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":44,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":45,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":46,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":47,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":48,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":49,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":50,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":51,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":52,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":53,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":54,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":55,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":56,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":57,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":58,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":59,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":60,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":61,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":62,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":63,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":64,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":65,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":66,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":67,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":68,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":69,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":70,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":71,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":72,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":73,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":74,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":75,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":76,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":77,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":78,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":79,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":80,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":81,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":82,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":83,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":84,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":85,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":86,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":87,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":88,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":89,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":90,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":91,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":92,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":93,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":94,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":95,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":96,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":97,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":98,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":99,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":1248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":1344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+
+ {
+ "id":1392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":1968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":1999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":2976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":2999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":3984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":3999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":4992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":4999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":5952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":5999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":6960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6984,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6985,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6986,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6987,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6988,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6989,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6990,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6991,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6992,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6993,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6994,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6995,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6996,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6997,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6998,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":6999,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7000,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7001,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7002,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7003,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7004,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7005,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7006,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7007,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7008,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7009,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7010,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7011,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7012,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7013,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7014,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7015,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7016,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7017,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7018,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7019,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7020,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7021,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7022,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7023,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7024,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7025,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7026,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7027,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7028,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7029,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7030,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7031,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7032,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7033,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7034,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7035,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7036,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7037,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7038,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7039,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7040,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7041,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7042,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7043,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7044,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7045,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7046,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7047,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7048,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7049,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7050,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7051,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7052,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7053,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7054,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7055,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7056,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7057,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7058,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7059,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7060,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7061,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7062,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7063,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7064,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7065,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7066,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7067,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7068,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7069,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7070,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7071,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7072,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7073,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7074,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7075,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7076,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7077,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7078,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7079,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7080,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7081,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7082,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7083,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7084,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7085,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7086,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7087,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7088,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7089,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7090,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7091,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7092,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7093,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7094,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7095,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7096,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7097,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7098,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7099,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7100,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7101,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7102,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7103,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7104,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7105,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7106,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7107,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7108,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7109,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7110,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7111,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7112,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7113,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7114,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7115,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7116,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7117,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7118,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7119,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7120,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7121,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7122,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7123,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7124,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7125,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7126,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7127,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7128,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7129,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7130,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7131,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7132,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7133,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7134,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7135,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7136,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7137,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7138,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7139,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7140,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7141,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7142,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7143,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7144,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7145,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7146,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7147,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7148,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7149,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7150,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7151,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7152,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7153,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7154,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7155,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7156,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7157,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7158,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7159,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7160,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7161,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7162,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7163,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7164,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7165,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7166,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7167,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7168,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7169,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7170,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7171,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7172,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7173,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7174,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7175,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7176,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7177,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7178,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7179,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7180,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7181,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7182,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7183,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7184,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7185,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7186,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7187,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7188,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7189,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7190,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7191,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7192,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7193,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7194,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7195,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7196,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7197,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7198,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7199,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7200,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7201,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7202,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7203,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7204,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7205,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7206,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7207,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7208,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7209,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7210,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7211,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7212,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7213,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7214,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7215,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7216,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7217,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7218,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7219,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7220,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7221,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7222,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7223,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7224,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7225,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7226,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7227,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7228,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7229,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7230,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7231,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7232,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7233,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7234,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7235,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7236,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7237,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7238,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7239,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7240,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7241,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7242,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7243,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7244,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7245,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7246,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7247,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7248,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7249,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7250,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7251,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7252,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7253,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7254,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7255,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7256,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7257,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7258,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7259,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7260,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7261,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7262,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7263,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7264,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7265,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7266,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7267,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7268,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7269,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7270,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7271,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7272,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7273,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7274,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7275,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7276,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7277,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7278,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7279,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7280,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7281,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7282,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7283,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7284,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7285,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7286,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7287,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7288,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7289,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7290,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7291,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7292,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7293,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7294,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7295,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7296,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7297,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7298,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7299,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7300,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7301,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7302,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7303,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7304,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7305,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7306,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7307,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7308,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7309,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7310,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7311,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7312,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7313,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7314,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7315,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7316,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7317,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7318,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7319,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7320,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7321,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7322,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7323,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7324,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7325,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7326,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7327,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7328,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7329,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7330,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7331,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7332,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7333,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7334,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7335,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7336,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7337,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7338,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7339,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7340,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7341,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7342,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7343,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7344,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7345,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7346,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7347,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7348,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7349,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7350,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7351,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7352,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7353,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7354,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7355,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7356,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7357,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7358,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7359,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7360,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7361,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7362,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7363,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7364,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7365,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7366,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7367,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7368,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7369,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7370,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7371,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7372,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7373,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7374,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7375,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7376,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7377,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7378,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7379,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7380,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7381,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7382,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7383,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7384,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7385,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7386,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7387,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7388,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7389,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7390,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7391,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7392,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7393,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7394,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7395,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7396,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7397,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7398,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7399,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7400,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7401,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7402,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7403,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7404,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7405,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7406,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7407,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7408,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7409,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7410,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7411,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7412,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7413,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7414,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7415,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7416,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7417,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7418,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7419,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7420,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7421,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7422,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7423,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7424,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7425,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7426,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7427,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7428,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7429,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7430,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7431,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7432,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7433,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7434,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7435,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7436,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7437,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7438,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7439,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7440,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7441,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7442,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7443,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7444,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7445,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7446,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7447,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7448,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7449,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7450,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7451,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7452,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7453,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7454,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7455,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7456,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7457,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7458,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7459,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7460,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7461,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7462,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7463,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7464,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7465,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7466,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7467,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7468,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7469,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7470,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7471,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7472,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7473,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7474,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7475,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7476,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7477,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7478,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7479,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7480,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7481,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7482,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7483,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7484,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7485,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7486,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7487,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7488,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7489,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7490,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7491,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7492,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7493,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7494,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7495,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7496,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7497,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7498,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7499,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7500,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7501,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7502,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7503,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7504,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7505,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7506,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7507,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7508,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7509,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7510,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7511,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7512,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7513,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7514,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7515,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7516,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7517,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7518,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7519,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7520,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7521,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7522,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7523,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7524,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7525,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7526,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7527,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7528,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7529,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7530,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7531,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7532,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7533,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7534,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7535,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7536,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7537,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7538,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7539,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7540,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7541,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7542,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7543,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7544,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7545,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7546,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7547,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7548,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7549,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7550,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7551,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7552,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7553,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7554,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7555,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7556,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7557,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7558,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7559,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7560,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7561,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7562,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7563,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7564,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7565,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7566,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7567,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7568,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7569,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7570,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7571,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7572,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7573,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7574,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7575,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7576,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7577,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7578,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7579,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7580,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7581,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7582,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7583,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7584,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7585,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7586,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7587,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7588,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7589,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7590,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7591,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7592,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7593,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7594,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7595,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7596,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7597,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7598,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7599,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7600,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7601,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7602,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7603,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7604,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7605,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7606,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7607,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7608,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7609,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7610,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7611,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7612,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7613,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7614,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7615,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7616,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7617,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7618,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7619,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7620,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7621,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7622,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7623,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7624,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7625,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7626,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7627,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7628,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7629,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7630,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7631,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7632,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7633,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7634,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7635,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7636,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7637,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7638,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7639,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7640,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7641,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7642,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7643,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7644,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7645,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7646,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7647,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7648,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7649,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7650,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7651,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7652,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7653,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7654,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7655,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7656,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7657,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7658,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7659,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7660,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7661,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7662,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7663,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7664,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7665,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7666,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7667,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7668,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7669,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7670,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7671,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7672,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7673,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7674,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7675,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7676,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7677,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7678,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7679,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7680,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7681,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7682,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7683,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7684,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7685,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7686,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7687,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7688,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7689,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7690,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7691,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7692,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7693,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7694,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7695,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7696,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7697,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7698,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7699,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7700,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7701,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7702,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7703,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7704,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7705,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7706,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7707,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7708,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7709,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7710,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7711,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7712,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7713,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7714,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7715,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7716,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7717,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7718,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7719,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7720,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7721,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7722,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7723,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7724,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7725,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7726,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7727,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7728,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7729,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7730,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7731,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7732,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7733,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7734,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7735,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7736,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7737,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7738,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7739,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7740,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7741,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7742,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7743,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7744,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7745,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7746,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7747,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7748,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7749,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7750,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7751,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7752,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7753,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7754,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7755,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7756,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7757,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7758,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7759,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7760,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7761,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7762,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7763,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7764,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7765,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7766,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7767,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7768,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7769,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7770,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7771,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7772,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7773,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7774,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7775,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7776,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7777,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7778,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7779,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7780,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7781,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7782,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7783,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7784,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7785,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7786,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7787,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7788,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7789,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7790,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7791,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7792,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7793,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7794,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7795,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7796,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7797,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7798,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7799,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7800,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7801,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7802,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7803,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7804,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7805,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7806,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7807,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7808,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7809,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7810,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7811,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7812,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7813,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7814,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7815,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7816,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7817,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7818,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7819,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7820,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7821,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7822,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7823,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7824,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7825,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7826,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7827,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7828,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7829,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7830,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7831,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7832,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7833,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7834,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7835,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7836,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7837,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7838,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7839,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7840,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7841,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7842,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7843,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7844,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7845,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7846,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7847,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7848,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7849,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7850,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7851,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7852,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7853,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7854,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7855,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7856,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7857,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7858,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7859,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7860,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7861,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7862,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7863,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7864,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7865,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7866,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7867,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7868,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7869,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7870,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7871,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7872,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7873,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7874,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7875,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7876,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7877,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7878,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7879,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7880,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7881,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7882,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7883,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7884,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7885,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7886,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7887,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7888,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7889,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7890,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7891,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7892,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7893,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7894,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7895,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7896,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7897,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7898,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7899,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7900,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7901,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7902,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7903,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7904,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7905,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7906,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7907,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7908,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7909,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7910,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7911,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7912,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7913,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7914,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7915,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7916,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7917,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7918,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7919,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7920,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7921,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7922,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7923,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7924,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7925,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7926,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7927,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7928,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7929,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7930,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7931,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7932,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7933,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7934,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7935,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7936,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7937,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7938,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7939,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7940,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7941,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7942,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7943,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7944,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7945,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7946,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7947,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7948,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7949,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7950,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7951,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7952,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7953,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7954,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7955,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7956,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7957,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7958,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7959,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7960,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7961,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7962,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7963,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7964,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7965,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7966,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7967,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+
+ {
+ "id":7968,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7969,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7970,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7971,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7972,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7973,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7974,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7975,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7976,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7977,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7978,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7979,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7980,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7981,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7982,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ },
+ {
+ "id":7983,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":false
+ }]
+ }],
+ "tilewidth":16
+ }],
+ "tilewidth":16,
+ "type":"map",
+ "version":"1.10",
+ "width":48
+}
\ No newline at end of file
diff --git a/ui/dist/assets/tilemaps/tiles/tileset.png b/ui/dist/assets/tilemaps/tiles/tileset.png
new file mode 100644
index 000000000..f8cb6fea4
Binary files /dev/null and b/ui/dist/assets/tilemaps/tiles/tileset.png differ
diff --git a/ui/dist/assets/tilemaps/tiles/tileset.tsx b/ui/dist/assets/tilemaps/tiles/tileset.tsx
new file mode 100644
index 000000000..5897600ae
--- /dev/null
+++ b/ui/dist/assets/tilemaps/tiles/tileset.tsx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/ui/dist/assets/tilemaps/tiles/town.tsx b/ui/dist/assets/tilemaps/tiles/town.tsx
new file mode 100644
index 000000000..5897600ae
--- /dev/null
+++ b/ui/dist/assets/tilemaps/tiles/town.tsx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/ui/dist/index.html b/ui/dist/index.html
new file mode 100644
index 000000000..d8f3f9c1b
--- /dev/null
+++ b/ui/dist/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/package-lock.json b/ui/package-lock.json
new file mode 100644
index 000000000..b8c3b639a
--- /dev/null
+++ b/ui/package-lock.json
@@ -0,0 +1,2895 @@
+{
+ "name": "phaser3-typescript-project-template",
+ "version": "1.2.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "phaser3-typescript-project-template",
+ "version": "1.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "phaser3-rex-plugins": "^1.60.1"
+ },
+ "devDependencies": {
+ "@rollup/plugin-commonjs": "^24.0.1",
+ "@rollup/plugin-node-resolve": "^15.0.2",
+ "@rollup/plugin-replace": "^5.0.2",
+ "@rollup/plugin-terser": "^0.4.0",
+ "@rollup/plugin-typescript": "^11.1.0",
+ "phaser": "^3.60.0",
+ "rollup": "^3.20.2",
+ "rollup-plugin-commonjs": "^10.1.0",
+ "rollup-plugin-serve": "^2.0.2",
+ "tslib": "^2.5.0",
+ "typescript": "^5.0.3",
+ "vite-plugin-static-copy": "^0.15.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.21.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
+ "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
+ "dependencies": {
+ "regenerator-runtime": "^0.13.11"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+ "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+ "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+ "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+ "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+ "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+ "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+ "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+ "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+ "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+ "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+ "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+ "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+ "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+ "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+ "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+ "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+ "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+ "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+ "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+ "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@rollup/plugin-commonjs": {
+ "version": "24.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
+ "integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.2",
+ "glob": "^8.0.3",
+ "is-reference": "1.2.1",
+ "magic-string": "^0.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.68.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+ "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.13"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.2.tgz",
+ "integrity": "sha512-Y35fRGUjC3FaurG722uhUuG8YHOJRJQbI6/CkbRkdPotSpDj9NtIN85z1zrcyDcCQIW4qp5mgG72U+gJ0TAFEg==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-builtin-module": "^3.2.1",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.78.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve/node_modules/@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true
+ },
+ "node_modules/@rollup/plugin-replace": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz",
+ "integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "magic-string": "^0.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-replace/node_modules/magic-string": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+ "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.13"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@rollup/plugin-terser": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.0.tgz",
+ "integrity": "sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==",
+ "dev": true,
+ "dependencies": {
+ "serialize-javascript": "^6.0.0",
+ "smob": "^0.0.6",
+ "terser": "^5.15.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.x || ^3.x"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-typescript": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.0.tgz",
+ "integrity": "sha512-86flrfE+bSHB69znnTV6kVjkncs2LBMhcTCyxWgRxLyfXfQrxg4UwlAqENnjrrxnSNS/XKCDJCl8EkdFJVHOxw==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "resolve": "^1.22.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.14.0||^3.0.0",
+ "tslib": "*",
+ "typescript": ">=3.7.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ },
+ "tslib": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
+ "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/@types/estree": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
+ "dev": true
+ },
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/builtin-modules": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "node_modules/cross-fetch": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz",
+ "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==",
+ "dependencies": {
+ "node-fetch": "^2.6.11"
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+ "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "peer": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.19",
+ "@esbuild/android-arm64": "0.17.19",
+ "@esbuild/android-x64": "0.17.19",
+ "@esbuild/darwin-arm64": "0.17.19",
+ "@esbuild/darwin-x64": "0.17.19",
+ "@esbuild/freebsd-arm64": "0.17.19",
+ "@esbuild/freebsd-x64": "0.17.19",
+ "@esbuild/linux-arm": "0.17.19",
+ "@esbuild/linux-arm64": "0.17.19",
+ "@esbuild/linux-ia32": "0.17.19",
+ "@esbuild/linux-loong64": "0.17.19",
+ "@esbuild/linux-mips64el": "0.17.19",
+ "@esbuild/linux-ppc64": "0.17.19",
+ "@esbuild/linux-riscv64": "0.17.19",
+ "@esbuild/linux-s390x": "0.17.19",
+ "@esbuild/linux-x64": "0.17.19",
+ "@esbuild/netbsd-x64": "0.17.19",
+ "@esbuild/openbsd-x64": "0.17.19",
+ "@esbuild/sunos-x64": "0.17.19",
+ "@esbuild/win32-arm64": "0.17.19",
+ "@esbuild/win32-ia32": "0.17.19",
+ "@esbuild/win32-x64": "0.17.19"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
+ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+ "dev": true
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.0.tgz",
+ "integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
+ "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/i18next": {
+ "version": "22.4.15",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.15.tgz",
+ "integrity": "sha512-yYudtbFrrmWKLEhl6jvKUYyYunj4bTBCe2qIUYAxbXoPusY7YmdwPvOE6fx6UIfWvmlbCWDItr7wIs8KEBZ5Zg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "dependencies": {
+ "@babel/runtime": "^7.20.6"
+ }
+ },
+ "node_modules/i18next-http-backend": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.2.1.tgz",
+ "integrity": "sha512-ZXIdn/8NJIBJ0X4hzXfc3STYxKrCKh1fYjji9HPyIpEJfvTvy8/ZlTl8RuTizzCPj2ZcWrfaecyOMKs6bQ7u5A==",
+ "dependencies": {
+ "cross-fetch": "3.1.6"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-builtin-module": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+ "dev": true,
+ "dependencies": {
+ "builtin-modules": "^3.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+ "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
+ "dev": true
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+ "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+ "dev": true,
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "2.4.7",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz",
+ "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "peer": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
+ "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true,
+ "bin": {
+ "opener": "bin/opener-bin.js"
+ }
+ },
+ "node_modules/papaparse": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
+ "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/phaser": {
+ "version": "3.60.0",
+ "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.60.0.tgz",
+ "integrity": "sha512-IKUy35EnoEVcl2EmJ8WOyK4X8OoxHYdlhZLgRGpNrvD1fEagYffhVmwHcapE/tGiLgyrnezmXIo5RrH2NcrTHw==",
+ "dev": true,
+ "dependencies": {
+ "eventemitter3": "^5.0.0"
+ }
+ },
+ "node_modules/phaser3-rex-plugins": {
+ "version": "1.60.1",
+ "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.60.1.tgz",
+ "integrity": "sha512-tMHLwtNa18RH0U59lF+J5PSRPOwnJMv6Pw78TFOBgwXz2ezaceLgw85iE4MRV+I5KybfdU3hDTZsYOrfxOqRPQ==",
+ "dependencies": {
+ "eventemitter3": "^3.1.2",
+ "i18next": "^22.4.14",
+ "i18next-http-backend": "^2.2.0",
+ "js-yaml": "^4.1.0",
+ "papaparse": "^5.4.1",
+ "webfontloader": "^1.6.28"
+ }
+ },
+ "node_modules/phaser3-rex-plugins/node_modules/eventemitter3": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
+ "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "3.21.7",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.7.tgz",
+ "integrity": "sha512-KXPaEuR8FfUoK2uHwNjxTmJ18ApyvD6zJpYv9FOJSqLStmt6xOY84l1IjK2dSolQmoXknrhEFRaPRgOPdqCT5w==",
+ "dev": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rollup-plugin-commonjs": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz",
+ "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==",
+ "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-commonjs.",
+ "dev": true,
+ "dependencies": {
+ "estree-walker": "^0.6.1",
+ "is-reference": "^1.1.2",
+ "magic-string": "^0.25.2",
+ "resolve": "^1.11.0",
+ "rollup-pluginutils": "^2.8.1"
+ },
+ "peerDependencies": {
+ "rollup": ">=1.12.0"
+ }
+ },
+ "node_modules/rollup-plugin-serve": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-serve/-/rollup-plugin-serve-2.0.2.tgz",
+ "integrity": "sha512-ALqyTbPhlf7FZ5RzlbDvMYvbKuCHWginJkTo6dMsbgji/a78IbsXox+pC83HENdkTRz8OXrTj+aShp3+3ratpg==",
+ "dev": true,
+ "dependencies": {
+ "mime": ">=2.4.6",
+ "opener": "1"
+ }
+ },
+ "node_modules/rollup-pluginutils": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
+ "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
+ "dev": true,
+ "dependencies": {
+ "estree-walker": "^0.6.1"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/smob": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/smob/-/smob-0.0.6.tgz",
+ "integrity": "sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==",
+ "dev": true
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "deprecated": "Please use @jridgewell/sourcemap-codec instead",
+ "dev": true
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.16.8",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz",
+ "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/tslib": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
+ "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==",
+ "dev": true
+ },
+ "node_modules/typescript": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.3.tgz",
+ "integrity": "sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.5.tgz",
+ "integrity": "sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.17.5",
+ "postcss": "^8.4.23",
+ "rollup": "^3.21.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-static-copy": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.15.0.tgz",
+ "integrity": "sha512-Ww+/Ug9guV45oIfIi/lA2z8v3K+lLHV9zCJqTVO4FTdqrJoZBj68VgGBSH1fi0N4q/EHW32RsL3ympi4Wlsq5w==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": "^3.5.3",
+ "fast-glob": "^3.2.11",
+ "fs-extra": "^11.1.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "node_modules/webfontloader": {
+ "version": "1.6.28",
+ "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz",
+ "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ=="
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ }
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.21.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
+ "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
+ "requires": {
+ "regenerator-runtime": "^0.13.11"
+ }
+ },
+ "@esbuild/android-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+ "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+ "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+ "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+ "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+ "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+ "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+ "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+ "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+ "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+ "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+ "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+ "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+ "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+ "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+ "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+ "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+ "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+ "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+ "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+ "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@rollup/plugin-commonjs": {
+ "version": "24.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
+ "integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.2",
+ "glob": "^8.0.3",
+ "is-reference": "1.2.1",
+ "magic-string": "^0.27.0"
+ },
+ "dependencies": {
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "magic-string": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+ "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/sourcemap-codec": "^1.4.13"
+ }
+ }
+ }
+ },
+ "@rollup/plugin-node-resolve": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.2.tgz",
+ "integrity": "sha512-Y35fRGUjC3FaurG722uhUuG8YHOJRJQbI6/CkbRkdPotSpDj9NtIN85z1zrcyDcCQIW4qp5mgG72U+gJ0TAFEg==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-builtin-module": "^3.2.1",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ },
+ "dependencies": {
+ "@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true
+ }
+ }
+ },
+ "@rollup/plugin-replace": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz",
+ "integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "magic-string": "^0.27.0"
+ },
+ "dependencies": {
+ "magic-string": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
+ "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/sourcemap-codec": "^1.4.13"
+ }
+ }
+ }
+ },
+ "@rollup/plugin-terser": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.0.tgz",
+ "integrity": "sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==",
+ "dev": true,
+ "requires": {
+ "serialize-javascript": "^6.0.0",
+ "smob": "^0.0.6",
+ "terser": "^5.15.1"
+ }
+ },
+ "@rollup/plugin-typescript": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.0.tgz",
+ "integrity": "sha512-86flrfE+bSHB69znnTV6kVjkncs2LBMhcTCyxWgRxLyfXfQrxg4UwlAqENnjrrxnSNS/XKCDJCl8EkdFJVHOxw==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "resolve": "^1.22.1"
+ }
+ },
+ "@rollup/pluginutils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
+ "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "dependencies": {
+ "@types/estree": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ }
+ }
+ },
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "cross-fetch": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz",
+ "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==",
+ "requires": {
+ "node-fetch": "^2.6.11"
+ }
+ },
+ "deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+ "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@esbuild/android-arm": "0.17.19",
+ "@esbuild/android-arm64": "0.17.19",
+ "@esbuild/android-x64": "0.17.19",
+ "@esbuild/darwin-arm64": "0.17.19",
+ "@esbuild/darwin-x64": "0.17.19",
+ "@esbuild/freebsd-arm64": "0.17.19",
+ "@esbuild/freebsd-x64": "0.17.19",
+ "@esbuild/linux-arm": "0.17.19",
+ "@esbuild/linux-arm64": "0.17.19",
+ "@esbuild/linux-ia32": "0.17.19",
+ "@esbuild/linux-loong64": "0.17.19",
+ "@esbuild/linux-mips64el": "0.17.19",
+ "@esbuild/linux-ppc64": "0.17.19",
+ "@esbuild/linux-riscv64": "0.17.19",
+ "@esbuild/linux-s390x": "0.17.19",
+ "@esbuild/linux-x64": "0.17.19",
+ "@esbuild/netbsd-x64": "0.17.19",
+ "@esbuild/openbsd-x64": "0.17.19",
+ "@esbuild/sunos-x64": "0.17.19",
+ "@esbuild/win32-arm64": "0.17.19",
+ "@esbuild/win32-ia32": "0.17.19",
+ "@esbuild/win32-x64": "0.17.19"
+ }
+ },
+ "estree-walker": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
+ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.0.tgz",
+ "integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fs-extra": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
+ "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "i18next": {
+ "version": "22.4.15",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.15.tgz",
+ "integrity": "sha512-yYudtbFrrmWKLEhl6jvKUYyYunj4bTBCe2qIUYAxbXoPusY7YmdwPvOE6fx6UIfWvmlbCWDItr7wIs8KEBZ5Zg==",
+ "requires": {
+ "@babel/runtime": "^7.20.6"
+ }
+ },
+ "i18next-http-backend": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.2.1.tgz",
+ "integrity": "sha512-ZXIdn/8NJIBJ0X4hzXfc3STYxKrCKh1fYjji9HPyIpEJfvTvy8/ZlTl8RuTizzCPj2ZcWrfaecyOMKs6bQ7u5A==",
+ "requires": {
+ "cross-fetch": "3.1.6"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-builtin-module": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^3.3.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+ "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "*"
+ }
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "magic-string": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+ "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.7",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz",
+ "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "nanoid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "dev": true,
+ "peer": true
+ },
+ "node-fetch": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
+ "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true
+ },
+ "papaparse": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
+ "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "phaser": {
+ "version": "3.60.0",
+ "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.60.0.tgz",
+ "integrity": "sha512-IKUy35EnoEVcl2EmJ8WOyK4X8OoxHYdlhZLgRGpNrvD1fEagYffhVmwHcapE/tGiLgyrnezmXIo5RrH2NcrTHw==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^5.0.0"
+ }
+ },
+ "phaser3-rex-plugins": {
+ "version": "1.60.1",
+ "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.60.1.tgz",
+ "integrity": "sha512-tMHLwtNa18RH0U59lF+J5PSRPOwnJMv6Pw78TFOBgwXz2ezaceLgw85iE4MRV+I5KybfdU3hDTZsYOrfxOqRPQ==",
+ "requires": {
+ "eventemitter3": "^3.1.2",
+ "i18next": "^22.4.14",
+ "i18next-http-backend": "^2.2.0",
+ "js-yaml": "^4.1.0",
+ "papaparse": "^5.4.1",
+ "webfontloader": "^1.6.28"
+ },
+ "dependencies": {
+ "eventemitter3": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
+ }
+ }
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
+ "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ },
+ "resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rollup": {
+ "version": "3.21.7",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.7.tgz",
+ "integrity": "sha512-KXPaEuR8FfUoK2uHwNjxTmJ18ApyvD6zJpYv9FOJSqLStmt6xOY84l1IjK2dSolQmoXknrhEFRaPRgOPdqCT5w==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "rollup-plugin-commonjs": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz",
+ "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==",
+ "dev": true,
+ "requires": {
+ "estree-walker": "^0.6.1",
+ "is-reference": "^1.1.2",
+ "magic-string": "^0.25.2",
+ "resolve": "^1.11.0",
+ "rollup-pluginutils": "^2.8.1"
+ }
+ },
+ "rollup-plugin-serve": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-serve/-/rollup-plugin-serve-2.0.2.tgz",
+ "integrity": "sha512-ALqyTbPhlf7FZ5RzlbDvMYvbKuCHWginJkTo6dMsbgji/a78IbsXox+pC83HENdkTRz8OXrTj+aShp3+3ratpg==",
+ "dev": true,
+ "requires": {
+ "mime": ">=2.4.6",
+ "opener": "1"
+ }
+ },
+ "rollup-pluginutils": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
+ "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
+ "dev": true,
+ "requires": {
+ "estree-walker": "^0.6.1"
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "smob": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/smob/-/smob-0.0.6.tgz",
+ "integrity": "sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true,
+ "peer": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "terser": {
+ "version": "5.16.8",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz",
+ "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "tslib": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
+ "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.3.tgz",
+ "integrity": "sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==",
+ "dev": true
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ },
+ "vite": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.5.tgz",
+ "integrity": "sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "esbuild": "^0.17.5",
+ "fsevents": "~2.3.2",
+ "postcss": "^8.4.23",
+ "rollup": "^3.21.0"
+ }
+ },
+ "vite-plugin-static-copy": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.15.0.tgz",
+ "integrity": "sha512-Ww+/Ug9guV45oIfIi/lA2z8v3K+lLHV9zCJqTVO4FTdqrJoZBj68VgGBSH1fi0N4q/EHW32RsL3ympi4Wlsq5w==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^3.5.3",
+ "fast-glob": "^3.2.11",
+ "fs-extra": "^11.1.0",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "webfontloader": {
+ "version": "1.6.28",
+ "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz",
+ "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ=="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ }
+ }
+}
diff --git a/ui/package.json b/ui/package.json
new file mode 100644
index 000000000..c9c7ee281
--- /dev/null
+++ b/ui/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "phaser3-typescript-project-template",
+ "version": "1.2.0",
+ "description": "A Phaser 3 Project Template using Rollup and TypeScript",
+ "main": "src/index.ts",
+ "scripts": {
+ "dev": "rollup --config rollup.config.dev.mjs",
+ "build": "rollup --config rollup.config.dist.mjs",
+ "watch": "rollup --watch --config rollup.config.dev.mjs"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/photonstorm/phaser3-typescript-project-template.git"
+ },
+ "author": "Richard Davey (http://www.photonstorm.com)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/photonstorm/phaser3-typescript-project-template/issues"
+ },
+ "homepage": "https://github.com/photonstorm/phaser3-typescript-project-template#readme",
+ "devDependencies": {
+ "@rollup/plugin-commonjs": "^24.0.1",
+ "@rollup/plugin-node-resolve": "^15.0.2",
+ "@rollup/plugin-replace": "^5.0.2",
+ "@rollup/plugin-terser": "^0.4.0",
+ "@rollup/plugin-typescript": "^11.1.0",
+ "phaser": "^3.60.0",
+ "rollup": "^3.20.2",
+ "rollup-plugin-commonjs": "^10.1.0",
+ "rollup-plugin-serve": "^2.0.2",
+ "typescript": "^5.0.3",
+ "vite-plugin-static-copy": "^0.15.0",
+ "tslib": "^2.5.0"
+ },
+ "dependencies": {
+ "phaser3-rex-plugins": "^1.60.1"
+ }
+}
diff --git a/ui/rollup.config.dev.mjs b/ui/rollup.config.dev.mjs
new file mode 100644
index 000000000..9f54404f8
--- /dev/null
+++ b/ui/rollup.config.dev.mjs
@@ -0,0 +1,75 @@
+import commonjs from '@rollup/plugin-commonjs';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import replace from '@rollup/plugin-replace';
+import serve from 'rollup-plugin-serve';
+import typescript from '@rollup/plugin-typescript';
+
+
+export default {
+
+ // Our game entry point (edit as required)
+ input: [
+ './src/index.ts'
+ ],
+
+ // Where the build file is to be generated.
+ // Most games being built for distribution can use iife as the module type.
+ // You can also use 'umd' if you need to ingest your game into another system.
+ // If using Phaser 3.21 or **below**, add: `intro: 'var global = window;'` to the output object.
+ output: {
+ file: './dist/bundle.js',
+ name: 'MyGame',
+ format: 'iife',
+ sourcemap: true
+ },
+
+ plugins: [
+
+ // Toggle the booleans here to enable / disable Phaser 3 features:
+ replace({
+ preventAssignment: true,
+ 'typeof CANVAS_RENDERER': JSON.stringify(true),
+ 'typeof WEBGL_RENDERER': JSON.stringify(true),
+ 'typeof WEBGL_DEBUG': JSON.stringify(true),
+ 'typeof EXPERIMENTAL': JSON.stringify(true),
+ 'typeof PLUGIN_CAMERA3D': JSON.stringify(false),
+ 'typeof PLUGIN_FBINSTANT': JSON.stringify(false),
+ 'typeof FEATURE_SOUND': JSON.stringify(true)
+ }),
+
+ // Parse our .ts source files
+ nodeResolve({
+ browser: true,
+ extensions: [ '.ts', '.tsx' ]
+ }),
+
+ // We need to convert the Phaser 3 CJS modules into a format Rollup can use:
+ commonjs({
+ include: [
+ 'node_modules/eventemitter3/**',
+ 'node_modules/phaser/**'
+ ],
+ exclude: [
+ 'node_modules/phaser/src/polyfills/requestAnimationFrame.js',
+ 'node_modules/phaser/src/phaser-esm.js'
+ ],
+ sourceMap: true,
+ ignoreGlobal: true
+ }),
+
+ // See https://github.com/rollup/plugins/tree/master/packages/typescript for config options
+ typescript(),
+
+ // See https://www.npmjs.com/package/rollup-plugin-serve for config options
+ serve({
+ open: true,
+ contentBase: 'dist',
+ host: '127.0.0.1',
+ port: 10001,
+ headers: {
+ 'Access-Control-Allow-Origin': '*'
+ }
+ })
+
+ ]
+};
diff --git a/ui/rollup.config.dist.mjs b/ui/rollup.config.dist.mjs
new file mode 100644
index 000000000..39ac16327
--- /dev/null
+++ b/ui/rollup.config.dist.mjs
@@ -0,0 +1,65 @@
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
+import replace from '@rollup/plugin-replace';
+import terser from '@rollup/plugin-terser';
+import typescript from '@rollup/plugin-typescript';
+
+export default {
+
+ // Our games entry point (edit as required)
+ input: [
+ './src/index.ts'
+ ],
+
+ // Where the build file is to be generated.
+ // Most games being built for distribution can use iife as the module type.
+ // You can also use 'umd' if you need to ingest your game into another system.
+ // If using Phaser 3.21 or **below**, add: `intro: 'var global = window;'` to the output object.
+ output: {
+ file: './dist/index.js',
+ name: 'MyGame',
+ format: 'iife',
+ sourcemap: false
+ },
+
+ plugins: [
+
+ // Toggle the booleans here to enable / disable Phaser 3 features:
+ replace({
+ preventAssignment: true,
+ 'typeof CANVAS_RENDERER': JSON.stringify(true),
+ 'typeof WEBGL_RENDERER': JSON.stringify(true),
+ 'typeof WEBGL_DEBUG': JSON.stringify(false),
+ 'typeof EXPERIMENTAL': JSON.stringify(true),
+ 'typeof PLUGIN_CAMERA3D': JSON.stringify(false),
+ 'typeof PLUGIN_FBINSTANT': JSON.stringify(false),
+ 'typeof FEATURE_SOUND': JSON.stringify(true)
+ }),
+
+ // Parse our .ts source files
+ nodeResolve({
+ extensions: [ '.ts', '.tsx' ]
+ }),
+
+ // We need to convert the Phaser 3 CJS modules into a format Rollup can use:
+ commonjs({
+ include: [
+ 'node_modules/eventemitter3/**',
+ 'node_modules/phaser/**'
+ ],
+ exclude: [
+ 'node_modules/phaser/src/polyfills/requestAnimationFrame.js',
+ 'node_modules/phaser/src/phaser-esm.js'
+ ],
+ sourceMap: false,
+ ignoreGlobal: true
+ }),
+
+ // See https://github.com/rollup/plugins/tree/master/packages/typescript for config options
+ typescript(),
+
+ // See https://github.com/rollup/plugins/tree/master/packages/terser for config options
+ terser()
+
+ ]
+};
\ No newline at end of file
diff --git a/ui/run.sh b/ui/run.sh
new file mode 100644
index 000000000..2bb43d082
--- /dev/null
+++ b/ui/run.sh
@@ -0,0 +1,4 @@
+#npm install
+npm run watch
+#npm run dev
+#npm run build
diff --git a/ui/screenshot.png b/ui/screenshot.png
new file mode 100644
index 000000000..bbe056d60
Binary files /dev/null and b/ui/screenshot.png differ
diff --git a/ui/src/classes/actor.ts b/ui/src/classes/actor.ts
new file mode 100644
index 000000000..22c79a6a6
--- /dev/null
+++ b/ui/src/classes/actor.ts
@@ -0,0 +1,54 @@
+import { Physics } from "phaser";
+export class Actor extends Physics.Arcade.Sprite {
+ constructor(
+ scene: Phaser.Scene,
+ x: number,
+ y: number,
+ texture: string,
+ frame?: string | number
+ ) {
+ super(scene, x, y, texture, frame);
+ scene.add.existing(this);
+ scene.physics.add.existing(this);
+ this.getBody().setCollideWorldBounds(true);
+ }
+
+ protected getBody(): Physics.Arcade.Body {
+ return this.body as Physics.Arcade.Body;
+ }
+
+ initAnimations(): void {
+ this.scene.anims.create({
+ key: this.name + "-walk-down",
+ frames: this.scene.anims.generateFrameNumbers(this.name, {
+ start: 0,
+ end: 2,
+ }),
+ frameRate: 6,
+ });
+ this.scene.anims.create({
+ key: this.name + "-walk-up",
+ frames: this.scene.anims.generateFrameNumbers(this.name, {
+ start: 3,
+ end: 5,
+ }),
+ frameRate: 6,
+ });
+ this.scene.anims.create({
+ key: this.name + "-walk-left",
+ frames: this.scene.anims.generateFrameNumbers(this.name, {
+ start: 6,
+ end: 8,
+ }),
+ frameRate: 6,
+ });
+ this.scene.anims.create({
+ key: this.name + "-walk-right",
+ frames: this.scene.anims.generateFrameNumbers(this.name, {
+ start: 9,
+ end: 11,
+ }),
+ frameRate: 6,
+ });
+ }
+}
diff --git a/ui/src/classes/event_center.ts b/ui/src/classes/event_center.ts
new file mode 100644
index 000000000..ff4cbb288
--- /dev/null
+++ b/ui/src/classes/event_center.ts
@@ -0,0 +1,5 @@
+import { Events } from "phaser";
+
+const eventsCenter = new Events.EventEmitter();
+
+export default eventsCenter;
diff --git a/ui/src/classes/npc.ts b/ui/src/classes/npc.ts
new file mode 100644
index 000000000..05aec1a8c
--- /dev/null
+++ b/ui/src/classes/npc.ts
@@ -0,0 +1,49 @@
+import { Actor } from "./actor";
+import { DIRECTION } from "../utils";
+export class NPC extends Actor {
+ public id: number;
+ public direction: number = DIRECTION.DOWN;
+ constructor(
+ scene: Phaser.Scene,
+ x: number,
+ y: number,
+ name: string,
+ id: number
+ ) {
+ super(scene, x, y, name);
+
+ this.setName(name);
+ this.id = id;
+ // PHYSICS
+ this.getBody().setSize(14, 10);
+ this.getBody().setOffset(0, 0);
+ this.getBody().setImmovable(true);
+
+ this.initAnimations();
+ }
+
+ update(): void {
+ var text = "";
+ switch (this.direction) {
+ case DIRECTION.UP:
+ text = "up";
+ break;
+ case DIRECTION.DOWN:
+ text = "down";
+ break;
+ case DIRECTION.LEFT:
+ text = "left";
+ break;
+ case DIRECTION.RIGHT:
+ text = "right";
+ break;
+ }
+ this.anims.play(this.name + "-walk-" + text, true);
+ if (this.anims.isPlaying)
+ this.anims.setCurrentFrame(this.anims.currentAnim!.frames[0]);
+ }
+
+ public changeDirection(direction: number): void {
+ this.direction = direction;
+ }
+}
diff --git a/ui/src/classes/player.ts b/ui/src/classes/player.ts
new file mode 100644
index 000000000..2a208d998
--- /dev/null
+++ b/ui/src/classes/player.ts
@@ -0,0 +1,65 @@
+import { Actor } from "./actor";
+export class Player extends Actor {
+ private keyW: Phaser.Input.Keyboard.Key;
+ private keyA: Phaser.Input.Keyboard.Key;
+ private keyS: Phaser.Input.Keyboard.Key;
+ private keyD: Phaser.Input.Keyboard.Key;
+
+ constructor(scene: Phaser.Scene, x: number, y: number) {
+ super(scene, x, y, "Brendan");
+
+ this.setName("Brendan");
+
+ // Keys
+ this.initKeyboard();
+
+ // PHYSICS
+ this.getBody().setSize(14, 21);
+ this.getBody().setOffset(0, 0);
+
+ // ANIMATIONS
+ this.initAnimations();
+ }
+
+ update(): void {
+ this.getBody().setVelocity(0);
+
+ var pressed_flag = false;
+ if (this.keyW.enabled && this.keyW?.isDown) {
+ this.getBody().setVelocityY(-110);
+ this.anims.play(this.name + "-walk-up", true);
+ pressed_flag = true;
+ }
+
+ if (this.keyA.enabled && this.keyA?.isDown) {
+ // this.getBody().setOffset(48, 15);
+ this.getBody().setVelocityX(-110);
+ this.anims.play(this.name + "-walk-left", true);
+ pressed_flag = true;
+ }
+
+ if (this.keyS.enabled && this.keyS?.isDown) {
+ this.getBody().setVelocityY(110);
+ this.anims.play(this.name + "-walk-down", true);
+ pressed_flag = true;
+ }
+
+ if (this.keyD.enabled && this.keyD?.isDown) {
+ this.getBody().setVelocityX(110);
+ this.anims.play(this.name + "-walk-right", true);
+ // this.getBody().setOffset(15, 15);
+ pressed_flag = true;
+ }
+
+ if (!pressed_flag && this.anims.isPlaying) {
+ this.anims.setCurrentFrame(this.anims.currentAnim!.frames[0]);
+ }
+ }
+
+ initKeyboard(): void {
+ this.keyW = this.scene.input.keyboard!.addKey("W");
+ this.keyA = this.scene.input.keyboard!.addKey("A");
+ this.keyS = this.scene.input.keyboard!.addKey("S");
+ this.keyD = this.scene.input.keyboard!.addKey("D");
+ }
+}
diff --git a/ui/src/index.ts b/ui/src/index.ts
new file mode 100644
index 000000000..957e46157
--- /dev/null
+++ b/ui/src/index.ts
@@ -0,0 +1,73 @@
+import { Game, Scale, Types, WEBGL } from "phaser";
+
+import { TownScene, LoadingScene } from "./scenes";
+import UIPlugin from "./phaser3-rex-plugins/templates/ui/ui-plugin";
+
+declare global {
+ interface Window {
+ sizeChanged: () => void;
+ game: Game;
+ }
+}
+
+export const gameConfig: Types.Core.GameConfig = {
+ title: "Phaser game tutorial",
+ type: WEBGL,
+ parent: "game",
+ // backgroundColor: '#351f1b',
+ scale: {
+ mode: Scale.ScaleModes.NONE,
+ width: window.innerWidth,
+ height: window.innerHeight,
+ },
+ physics: {
+ default: "arcade",
+ arcade: {
+ debug: false,
+ },
+ },
+ render: {
+ antialiasGL: false,
+ pixelArt: true,
+ },
+ callbacks: {
+ postBoot: () => {
+ window.sizeChanged();
+ },
+ },
+ canvasStyle: `display: block; width: 100%; height: 100%;`,
+ autoFocus: true,
+ audio: {
+ disableWebAudio: false,
+ },
+ scene: [LoadingScene, TownScene],
+ dom: {
+ createContainer: true,
+ },
+ plugins: {
+ scene: [
+ {
+ key: "rexUI",
+ plugin: UIPlugin,
+ mapping: "rexUI",
+ },
+ ],
+ },
+};
+
+window.sizeChanged = () => {
+ if (window.game.isBooted) {
+ setTimeout(() => {
+ window.game.scale.resize(window.innerWidth, window.innerHeight);
+
+ window.game.canvas.setAttribute(
+ "style",
+ `display: block; width: ${window.innerWidth}px; height: ${window.innerHeight}px;`
+ );
+ }, 100);
+ }
+};
+
+window.onresize = () => window.sizeChanged();
+
+window.game = new Game(gameConfig);
diff --git a/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.d.ts
new file mode 100644
index 000000000..0f267e5ac
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.d.ts
@@ -0,0 +1,6 @@
+import Achievements from './achievements';
+
+export default class AchievementsPlugin extends Phaser.Plugins.BasePlugin {
+ add(): Achievements;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.js b/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.js
new file mode 100644
index 000000000..51fb3c289
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/achievements-plugin.js
@@ -0,0 +1,18 @@
+import Achievements from './achievements.js'
+
+class AchievementsPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add() {
+ return new Achievements();
+ }
+}
+
+export default AchievementsPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/achievements.d.ts b/ui/src/phaser3-rex-plugins/plugins/achievements.d.ts
new file mode 100644
index 000000000..d8841896b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/achievements.d.ts
@@ -0,0 +1,2 @@
+import Achievements from './logic/achievements/csvachievements/Achievements';
+export default Achievements;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/achievements.js b/ui/src/phaser3-rex-plugins/plugins/achievements.js
new file mode 100644
index 000000000..e586f235d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/achievements.js
@@ -0,0 +1,2 @@
+import Achievements from './logic/achievements/csvachievements/Achievements.js';
+export default Achievements;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.d.ts b/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.d.ts
new file mode 100644
index 000000000..84a0eb289
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.d.ts
@@ -0,0 +1,34 @@
+export default GridCutImage;
+
+declare namespace GridCutImage {
+ interface IConfig {
+ columns?: number,
+ rows?: number,
+
+ onCreateImage?: (
+ scene: Phaser.Scene,
+ key: Phaser.Textures.Texture,
+ frame: string
+ ) => T,
+ ImageClass?: T,
+
+ originX?: number,
+ originY?: number,
+ add?: boolean,
+ align?: boolean,
+
+ objectPool?: T[],
+ }
+}
+
+declare function GridCutImage(
+ gameObject: Phaser.GameObjects.GameObject,
+ columns: number,
+ rows: number,
+ config?: GridCutImage.IConfig
+): void;
+
+declare function GridCutImage(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: GridCutImage.IConfig
+): void;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.js b/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.js
new file mode 100644
index 000000000..e06c23c18
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/GridCutImage.js
@@ -0,0 +1,81 @@
+import GridCut from '../utils/texture/gridcut/GridCut.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const DefaultImageClass = Phaser.GameObjects.Image;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const RotateAround = Phaser.Math.RotateAround;
+
+var GridCutImage = function (gameObject, columns, rows, config) {
+ if (IsPlainObject(columns)) {
+ config = columns;
+ columns = GetValue(config, 'columns', 1);
+ rows = GetValue(config, 'rows', 1);
+ }
+
+ var createImageCallback = GetValue(config, 'onCreateImage');
+ if (!createImageCallback) {
+ var ImageClass = GetValue(config, 'ImageClass', DefaultImageClass);
+ createImageCallback = function (scene, key, frame) {
+ return new ImageClass(scene, 0, 0, key, frame);
+ }
+ }
+
+ var originX = GetValue(config, 'originX', 0.5);
+ var originY = GetValue(config, 'originY', 0.5);
+ var addToScene = GetValue(config, 'add', true);
+ var align = GetValue(config, 'align', addToScene);
+ var imageObjectPool = GetValue(config, 'objectPool', undefined);
+
+ var scene = gameObject.scene;
+ var texture = gameObject.texture;
+ var frame = gameObject.frame;
+
+ var result = GridCut(scene, texture, frame, columns, rows);
+ var getFrameNameCallback = result.getFrameNameCallback;
+ var scaleX = gameObject.scaleX,
+ scaleY = gameObject.scaleY;
+ var rotation = gameObject.rotation;
+ var topLeft = gameObject.getTopLeft(),
+ startX = topLeft.x,
+ startY = topLeft.y;
+
+ var cellGameObjects = [];
+ var cellWidth = result.cellWidth * scaleX,
+ cellHeight = result.cellHeight * scaleY;
+ for (var y = 0; y < rows; y++) {
+ for (var x = 0; x < columns; x++) {
+ var cellGameObject;
+
+ var frameName = getFrameNameCallback(x, y);
+ if (imageObjectPool && (imageObjectPool.length > 0)) {
+ cellGameObject = (imageObjectPool.pop()).setTexture(texture, frameName);
+ } else {
+ cellGameObject = createImageCallback(scene, texture, frameName);
+ }
+
+ if (addToScene) {
+ scene.add.existing(cellGameObject);
+ }
+
+ var cellTLX = startX + (cellWidth * x);
+ var cellTLY = startY + (cellHeight * y);
+ var cellX = cellTLX + (originX * cellWidth);
+ var cellY = cellTLY + (originY * cellHeight);
+
+ if (align) {
+ cellGameObject
+ .setOrigin(originX, originY)
+ .setPosition(cellX, cellY)
+ .setScale(scaleX, scaleY)
+ .setRotation(rotation);
+ RotateAround(cellGameObject, startX, startY, rotation);
+ }
+
+ cellGameObjects.push(cellGameObject);
+ }
+ }
+
+ return cellGameObjects;
+}
+
+export default GridCutImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/HexagonGridAlign.js b/ui/src/phaser3-rex-plugins/plugins/actions/HexagonGridAlign.js
new file mode 100644
index 000000000..f1baf19ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/HexagonGridAlign.js
@@ -0,0 +1,76 @@
+import HexagonGrid from '../utils/grid/hexagon/Hexagon.js';
+import GlobZone from '../utils/actions/GlobZone.js';
+import AlignIn from '../utils/align/align/in/QuickSet.js';
+
+const GetFastValue = Phaser.Utils.Objects.GetFastValue;
+
+var globHexagonGrid = new HexagonGrid();
+
+/**
+ * @typedef {object} GridAlignConfig
+ *
+ * @property {integer} [width=-1] - The width of the grid in items (not pixels). -1 means lay all items out horizontally, regardless of quantity.
+ * If both this value and height are set to -1 then this value overrides it and the `height` value is ignored.
+ * @property {integer} [height=-1] - The height of the grid in items (not pixels). -1 means lay all items out vertically, regardless of quantity.
+ * If both this value and `width` are set to -1 then `width` overrides it and this value is ignored.
+ * @property {integer} [cellWidth=1] - The width of the cell, in pixels, in which the item is positioned.
+ * @property {integer} [cellHeight=1] - The height of the cell, in pixels, in which the item is positioned.
+ * @property {integer} [position=6] - The alignment position. One of the Phaser.Display.Align consts such as `TOP_LEFT` or `RIGHT_CENTER`.
+ * @property {number} [x=0] - Optionally place the top-left of the final grid at this coordinate.
+ * @property {number} [y=0] - Optionally place the top-left of the final grid at this coordinate.
+ */
+
+var GridAlign = function (items, options) {
+ if (options === undefined) {
+ options = {};
+ }
+
+ var width = GetFastValue(options, 'width', -1);
+ var height = GetFastValue(options, 'height', -1);
+ var cellWidth = GetFastValue(options, 'cellWidth', 1);
+ var cellHeight = GetFastValue(options, 'cellHeight', cellWidth);
+ var staggeraxis = GetFastValue(options, 'staggeraxis', 1);
+ var staggerindex = GetFastValue(options, 'staggerindex', 1);
+ var position = GetFastValue(options, 'position', Phaser.Display.Align.CENTER);
+ var x = GetFastValue(options, 'x', 0);
+ var y = GetFastValue(options, 'y', 0);
+
+ globHexagonGrid
+ .setOriginPosition(x, y)
+ .setCellSize(cellWidth, cellHeight)
+ .setType(staggeraxis, staggerindex);
+
+ GlobZone.setSize(cellWidth, cellHeight);
+
+ var lastRowIdx = height - 1,
+ lastColIdx = width - 1,
+ rowIdx = 0,
+ colIdx = 0;
+
+ for (var i = 0, cnt = items.length; i < cnt; i++) {
+ globHexagonGrid.getWorldXY(colIdx, rowIdx, GlobZone);
+ AlignIn(items[i], GlobZone, position);
+
+ if (width === -1) {
+ rowIdx++;
+ } else if (height === -1) {
+ colIdx++;
+ } else {
+ if (colIdx === lastColIdx) {
+ if (rowIdx === lastRowIdx) {
+ break;
+ } else {
+ colIdx = 0;
+ rowIdx++;
+ }
+ } else {
+ colIdx++;
+ }
+ }
+ }
+
+ return items;
+};
+
+
+export default GridAlign;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/QuadGridAlign.js b/ui/src/phaser3-rex-plugins/plugins/actions/QuadGridAlign.js
new file mode 100644
index 000000000..fd63e07a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/QuadGridAlign.js
@@ -0,0 +1,75 @@
+import QuadGrid from '../utils/grid/quad/Quad.js';
+import GlobZone from '../utils/actions/GlobZone.js';
+import AlignIn from '../utils/align/align/in/QuickSet.js';
+
+const GetFastValue = Phaser.Utils.Objects.GetFastValue;
+
+var globQuadGrid = new QuadGrid();
+
+/**
+ * @typedef {object} GridAlignConfig
+ *
+ * @property {integer} [width=-1] - The width of the grid in items (not pixels). -1 means lay all items out horizontally, regardless of quantity.
+ * If both this value and height are set to -1 then this value overrides it and the `height` value is ignored.
+ * @property {integer} [height=-1] - The height of the grid in items (not pixels). -1 means lay all items out vertically, regardless of quantity.
+ * If both this value and `width` are set to -1 then `width` overrides it and this value is ignored.
+ * @property {integer} [cellWidth=1] - The width of the cell, in pixels, in which the item is positioned.
+ * @property {integer} [cellHeight=1] - The height of the cell, in pixels, in which the item is positioned.
+ * @property {integer} [position=6] - The alignment position. One of the Phaser.Display.Align consts such as `TOP_LEFT` or `RIGHT_CENTER`.
+ * @property {number} [x=0] - Optionally place the top-left of the final grid at this coordinate.
+ * @property {number} [y=0] - Optionally place the top-left of the final grid at this coordinate.
+ */
+
+var GridAlign = function (items, options) {
+ if (options === undefined) {
+ options = {};
+ }
+
+ var width = GetFastValue(options, 'width', -1);
+ var height = GetFastValue(options, 'height', -1);
+ var cellWidth = GetFastValue(options, 'cellWidth', 1);
+ var cellHeight = GetFastValue(options, 'cellHeight', cellWidth);
+ var type = GetFastValue(options, 'type', 0);
+ var position = GetFastValue(options, 'position', Phaser.Display.Align.CENTER);
+ var x = GetFastValue(options, 'x', 0);
+ var y = GetFastValue(options, 'y', 0);
+
+ globQuadGrid
+ .setOriginPosition(x, y)
+ .setCellSize(cellWidth, cellHeight)
+ .setType(type);
+
+ GlobZone.setSize(cellWidth, cellHeight);
+
+ var lastRowIdx = height - 1,
+ lastColIdx = width - 1,
+ rowIdx = 0,
+ colIdx = 0;
+
+ for (var i = 0, cnt = items.length; i < cnt; i++) {
+ globQuadGrid.getWorldXY(colIdx, rowIdx, GlobZone);
+ AlignIn(items[i], GlobZone, position);
+
+ if (width === -1) {
+ rowIdx++;
+ } else if (height === -1) {
+ colIdx++;
+ } else {
+ if (colIdx === lastColIdx) {
+ if (rowIdx === lastRowIdx) {
+ break;
+ } else {
+ colIdx = 0;
+ rowIdx++;
+ }
+ } else {
+ colIdx++;
+ }
+ }
+ }
+
+ return items;
+};
+
+
+export default GridAlign;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.d.ts b/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.d.ts
new file mode 100644
index 000000000..1bdb91e9f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.d.ts
@@ -0,0 +1,29 @@
+export default RandomPlace;
+
+declare namespace RandomPlace {
+
+ type Vec2Type = { x: number, y: number };
+ type GetPositionCallback = (out?: Vec2Type) => Vec2Type
+
+ type AreaType = {
+ getRandomPoint: GetPositionCallback
+ }
+
+ interface IConfig {
+ radius?: number,
+ getPositionCallback?: GetPositionCallback
+ area?: AreaType,
+ }
+}
+
+declare function RandomPlace(
+ gameObjects: Phaser.GameObjects.GameObject,
+ config: RandomPlace.IConfig
+): Phaser.GameObjects.GameObject;
+
+declare function RandomPlace(
+ config: {
+ gameObjects: Phaser.GameObjects.GameObject,
+ radius?: number,
+ }
+): Phaser.GameObjects.GameObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.js b/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.js
new file mode 100644
index 000000000..09ae313a3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/actions/RandomPlace.js
@@ -0,0 +1,69 @@
+import GetViewport from '../utils/system/GetViewport.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const Circle = Phaser.Geom.Circle;
+const CircleToCircle = Phaser.Geom.Intersects.CircleToCircle;
+
+var RandomPlace = function (items, options) {
+ if (items.length === 0) {
+ return items;
+ }
+
+ var getPositionCallback = GetValue(options, 'getPositionCallback', undefined);
+ if (getPositionCallback === undefined) {
+ var area = GetValue(options, 'area', undefined);
+ if (area === undefined) {
+ var item0 = items[0], gameObject;
+ if (IsPlainObject(item0)) {
+ gameObject = item0.gameObject;
+ } else {
+ gameObject = item0;
+ }
+ area = GetViewport(gameObject.scene);
+ }
+ getPositionCallback = area.getRandomPoint.bind(area);
+ }
+ var defaultRadius = GetValue(options, 'radius', 0);
+
+ var item, gameObject, radius;
+ var collisionCircles = [];
+ for (var i = 0, cnt = items.length; i < cnt; i++) {
+ item = items[i];
+ if (IsPlainObject(item)) {
+ gameObject = GetValue(item, 'gameObject', undefined);
+ radius = GetValue(item, 'radius', defaultRadius);
+ } else {
+ gameObject = item;
+ radius = defaultRadius;
+ }
+
+ if (!gameObject) {
+ continue;
+ }
+
+ if (radius <= 0) {
+ getPositionCallback(gameObject);
+ } else {
+ var circle = new Circle(0, 0, radius);
+ var isOverlapping;
+ do {
+ getPositionCallback(circle);
+ isOverlapping = false;
+ for (var ci = 0, ccnt = collisionCircles.length; ci < ccnt; ci++) {
+ isOverlapping = CircleToCircle(circle, collisionCircles[ci]);
+ if (isOverlapping) {
+ break;
+ }
+ }
+ } while (isOverlapping)
+
+ collisionCircles.push(circle);
+ gameObject.setPosition(circle.x, circle.y);
+ }
+ }
+
+ return items;
+}
+
+export default RandomPlace;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/alphamaskimage-plugin.js b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage-plugin.js
new file mode 100644
index 000000000..d5a43efb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/canvas/alphamaskimage/Factory.js';
+import Creator from './gameobjects/canvas/alphamaskimage/Creator.js';
+import AlphaMaskImage from './gameobjects/canvas/alphamaskimage/AlphaMaskImage.js';
+import SetValue from './utils/object/SetValue.js';
+
+class AlphaMaskImagePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexAlphaMaskImage', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.AlphaMaskImage', AlphaMaskImage);
+
+export default AlphaMaskImagePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.d.ts b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.d.ts
new file mode 100644
index 000000000..8ee0790c6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.d.ts
@@ -0,0 +1,2 @@
+import AlphaMaskImage from './gameobjects/canvas/alphamaskimage/AlphaMaskImage';
+export default AlphaMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.js b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.js
new file mode 100644
index 000000000..2fcf16ea1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/alphamaskimage.js
@@ -0,0 +1,2 @@
+import AlphaMaskImage from './gameobjects/canvas/alphamaskimage/AlphaMaskImage.js';
+export default AlphaMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.d.ts
new file mode 100644
index 000000000..5a908ac6d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.d.ts
@@ -0,0 +1,9 @@
+import Anchor from './anchor'
+
+export default class AnchorPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Anchor.IConfig
+ ): Anchor;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.js b/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.js
new file mode 100644
index 000000000..7b722c167
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/anchor-plugin.js
@@ -0,0 +1,18 @@
+import Anchor from './anchor.js'
+
+class AnchorPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Anchor(gameObject, config);
+ }
+}
+
+export default AnchorPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/anchor.d.ts b/ui/src/phaser3-rex-plugins/plugins/anchor.d.ts
new file mode 100644
index 000000000..207d995ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/anchor.d.ts
@@ -0,0 +1,2 @@
+import Anchor from './behaviors/anchor/Anchor';
+export default Anchor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/anchor.js b/ui/src/phaser3-rex-plugins/plugins/anchor.js
new file mode 100644
index 000000000..6800a82ad
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/anchor.js
@@ -0,0 +1,2 @@
+import Anchor from './behaviors/anchor/Anchor.js';
+export default Anchor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadestepclock-plugin.js b/ui/src/phaser3-rex-plugins/plugins/arcadestepclock-plugin.js
new file mode 100644
index 000000000..97523802e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadestepclock-plugin.js
@@ -0,0 +1,20 @@
+import ArcadeStepClock from './ArcadeStepClock.js';
+
+class ArcadeStepClockPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new ArcadeStepClock(scene, config);
+ }
+
+}
+
+export default ArcadeStepClockPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadestepclock.js b/ui/src/phaser3-rex-plugins/plugins/arcadestepclock.js
new file mode 100644
index 000000000..e882542ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadestepclock.js
@@ -0,0 +1,2 @@
+import ArcadeStepClock from './time/clock/ArcadeStepClock.js';
+export default ArcadeStepClock;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.d.ts
new file mode 100644
index 000000000..ebf6b5fc3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.d.ts
@@ -0,0 +1,23 @@
+import Recorder from './logic/runcommands/arcadetcrp/Recorder';
+import Player from './logic/runcommands/arcadetcrp/Player';
+import StepRunner from './logic/runcommands/arcadetcrp/StepRunner'
+import RunCommands from './logic/runcommands/RunCommands';
+
+
+export default class TCRPPlugin extends Phaser.Plugins.BasePlugin {
+ addRecorder(
+ parent: Phaser.Scene | Phaser.GameObjects.GameObject,
+ config?: Recorder.IConfig
+ ): Recorder;
+
+ addPlayer(
+ parent: Phaser.Scene | Phaser.GameObjects.GameObject,
+ config?: Player.IConfig
+ ): Player
+
+ addStepRunner(
+ parent: Phaser.Scene | Phaser.GameObjects.GameObject,
+ ): StepRunner;
+
+ runCommands: typeof RunCommands;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.js b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.js
new file mode 100644
index 000000000..31aeaa424
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp-plugin.js
@@ -0,0 +1,39 @@
+import TCRP from './arcadetcrp.js';
+
+const Recorder = TCRP.Recorder;
+const Player = TCRP.Player;
+const StepRunner = TCRP.StepRunner;
+
+class ArcadeTCRPPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ addRecorder(parent, config) {
+ return new Recorder(parent, config);
+ }
+
+ addPlayer(parent, config) {
+ return new Player(parent, config);
+ }
+
+ addStepRunner(parent) {
+ return new StepRunner(parent);
+ }
+}
+
+var methods = {
+ runCommands: TCRP.RunCommands
+}
+
+Object.assign(
+ ArcadeTCRPPlugin.prototype,
+ methods
+);
+
+export default ArcadeTCRPPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.d.ts b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.d.ts
new file mode 100644
index 000000000..ba2d30434
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.d.ts
@@ -0,0 +1,11 @@
+import Recorder from './logic/runcommands/arcadetcrp/Recorder';
+import Player from './logic/runcommands/arcadetcrp/Player';
+import StepRunner from './logic/runcommands/arcadetcrp/StepRunner';
+import RunCommands from './logic/runcommands/RunCommands';
+
+declare var ArcadeTCRP: {
+ Recorder: typeof Recorder,
+ Player: typeof Player,
+ StepRunner: typeof StepRunner,
+ RunCommands: typeof RunCommands,
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.js b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.js
new file mode 100644
index 000000000..18247f63b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/arcadetcrp.js
@@ -0,0 +1,11 @@
+import Recorder from './logic/runcommands/arcadetcrp/Recorder.js';
+import Player from './logic/runcommands/arcadetcrp/Player.js';
+import StepRunner from './logic/runcommands/arcadetcrp/StepRunner.js';
+import RunCommands from './logic/runcommands/RunCommands.js';
+
+export default {
+ Recorder: Recorder,
+ Player: Player,
+ StepRunner: StepRunner,
+ RunCommands: RunCommands
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/fade/Fade.js b/ui/src/phaser3-rex-plugins/plugins/audio/fade/Fade.js
new file mode 100644
index 000000000..881416183
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/fade/Fade.js
@@ -0,0 +1,93 @@
+import EaseValueTaskBase from '../../utils/componentbase/tweentask/EaseValueTaskBase.js';
+import IsSoundObject from '../../utils/system/IsSoundObject.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const Linear = Phaser.Math.Linear;
+
+class Fade extends EaseValueTaskBase {
+ constructor(scene, sound, config) {
+ if (IsSoundObject(scene)) {
+ config = sound;
+ sound = scene;
+ scene = undefined;
+ }
+
+ sound.active = true;
+ sound.scene = scene;
+ sound.game = sound.manager.game;
+
+ super(sound, config);
+ // this.parent = parent
+ // this.timer
+
+ this.volume = {};
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ super.resetFromJSON(o);
+ this.setMode(GetValue(o, 'mode', 0));
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setVolumeRange(
+ GetAdvancedValue(o, 'volume.start', this.parent.volume),
+ GetAdvancedValue(o, 'volume.end', 0)
+ );
+ return this;
+ }
+
+ setMode(m) {
+ if (typeof (m) === 'string') {
+ m = MODE[m];
+ }
+ this.mode = m;
+ return this;
+ }
+
+ setVolumeRange(start, end) {
+ this.volume.start = start;
+ this.volume.end = end;
+ return this;
+ }
+
+ start() {
+ if (this.timer.isRunning) {
+ return this;
+ }
+
+ this.parent.setVolume(this.volume.start);
+
+ this.timer
+ .setDelay(this.delay)
+ .setDuration(this.duration);
+
+ super.start();
+ return this;
+ }
+
+ updateGameObject(parent, timer) {
+ parent.volume = Linear(this.volume.start, this.volume.end, timer.t);
+ }
+
+ complete() {
+ super.complete();
+
+ switch (this.mode) {
+ case 1:
+ this.parent.stop();
+ break;
+ case 2:
+ this.parent.destroy();
+ break;
+ }
+
+ return this;
+ }
+}
+
+const MODE = {
+ stop: 1,
+ destroy: 2
+}
+
+export default Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.d.ts b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.d.ts
new file mode 100644
index 000000000..356ad1aeb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.d.ts
@@ -0,0 +1,14 @@
+export default function FadeIn(
+ sound: string | Phaser.Sound.BaseSound,
+ duration: number,
+ endVolume?: number,
+ startVolume?: number
+): Phaser.Sound.BaseSound;
+
+export default function FadeIn(
+ scene: Phaser.Scene,
+ sound: string | Phaser.Sound.BaseSound,
+ duration: number,
+ endVolume?: number,
+ startVolume?: number
+): Phaser.Sound.BaseSound;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.js b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.js
new file mode 100644
index 000000000..0c3821740
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeIn.js
@@ -0,0 +1,50 @@
+import Fade from './Fade.js';
+import IsSoundObject from '../../utils/system/IsSoundObject.js';
+
+var FadeIn = function (scene, sound, duration, endVolume, startVolume) {
+ if (IsSoundObject(scene)) {
+ startVolume = endVolume;
+ endVolume = duration;
+ duration = sound;
+ sound = scene;
+ scene = undefined;
+ }
+
+ if (endVolume === undefined) {
+ endVolume = 1;
+ }
+ if (startVolume === undefined) {
+ startVolume = 0;
+ }
+
+ var config = {
+ mode: 0,
+ volume: {
+ start: startVolume,
+ end: endVolume
+ },
+ duration: duration
+ }
+
+ // create sound instance by key
+ if (typeof (sound) === 'string') {
+ sound = scene.sys.sound.add(sound);
+ }
+
+ var fade;
+ if (sound.hasOwnProperty('_fade')) {
+ fade = sound._fade;
+ fade.stop().resetFromJSON(config);
+ } else {
+ fade = new Fade(scene, sound, config);
+ sound._fade = fade;
+ }
+
+ fade.start();
+ if (!sound.isPlaying) {
+ sound.setVolume(startVolume).play();
+ }
+ return sound;
+};
+
+export default FadeIn;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.d.ts b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.d.ts
new file mode 100644
index 000000000..451268571
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.d.ts
@@ -0,0 +1,12 @@
+export default function FadeOut(
+ sound: Phaser.Sound.BaseSound,
+ duration: number,
+ destroy?: boolean
+): Phaser.Sound.BaseSound;
+
+export default function FadeOut(
+ scene: Phaser.Scene,
+ sound: Phaser.Sound.BaseSound,
+ duration: number,
+ destroy?: boolean
+): Phaser.Sound.BaseSound;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.js b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.js
new file mode 100644
index 000000000..4e83c9750
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/fade/FadeOut.js
@@ -0,0 +1,41 @@
+import Fade from './Fade.js';
+import IsSoundObject from '../../utils/system/IsSoundObject.js';
+
+var FadeOut = function (scene, sound, duration, destroy) {
+ if (IsSoundObject(scene)) {
+ destroy = duration;
+ duration = sound;
+ sound = scene;
+ scene = undefined;
+ }
+
+ if (destroy === undefined) {
+ destroy = true;
+ }
+
+ var config = {
+ mode: ((destroy) ? 2 : 1), // 1: stop, 2: destroy
+ volume: {
+ start: sound.volume,
+ end: 0
+ },
+ duration: duration
+ }
+
+ var fade;
+ if (sound.hasOwnProperty('_fade')) {
+ fade = sound._fade;
+ fade.stop().resetFromJSON(config);
+ } else {
+ fade = new Fade(scene, sound, config);
+ sound._fade = fade;
+ }
+
+ fade.start();
+ if (!sound.isPlaying) {
+ sound.play();
+ }
+ return sound;
+};
+
+export default FadeOut;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/midiplayer/MidiPlayer.js b/ui/src/phaser3-rex-plugins/plugins/audio/midiplayer/MidiPlayer.js
new file mode 100644
index 000000000..71d5b5953
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/audio/midiplayer/MidiPlayer.js
@@ -0,0 +1,93 @@
+import midiParser from '../../utils/midi-parser/midi-parser.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+
+class MidiPlayer {
+ constructor(config) {
+ this.resetFromJSON(config);
+ }
+
+ /**
+ * Reset status by JSON object
+ * @param {object} o JSON object
+ * @returns {object} this object
+ */
+ resetFromJSON(o) {
+ this.tracks = [];
+ this.tickPeriod = 0;
+ this.beatPeriod = 0;
+
+ // -status-
+ this.IsPlaying = false;
+ this.playingTrackCnt = 0;
+ return this;
+ }
+
+ /**
+ * Return status in JSON object
+ * @returns JSON object
+ */
+ toJSON() {
+ return {
+
+ };
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ load(arrayBuffer) {
+ var midi = midiParser.parse(new Uint8Array(arrayBuffer));
+ if (!midi) {
+ return this;
+ }
+ this.clear();
+
+ this.setTickPeriod(midi);
+
+ // load tracks
+ this.tracks.length = 0;
+ var tracks = midi.track;
+ var i, cnt = tracks.length,
+ t;
+ for (var i = 0, len = tracks.length; i < len; i++) {
+ t = new TrackKlass(this, i);
+ t.Load(tracks[i]);
+ this.tracks.push(t);
+ }
+ }
+
+ clear() {};
+
+ setTickPeriod(midi) {
+ var timeDivision = midi.timeDivision;
+ if (typeof (timeDivision) === "number") // Pulses per quarter note
+ this.tickPeriod = this.beatPeriod / timeDivision;
+ else // Frames per second
+ this.tickPeriod = 1 / (timeDivision[0] * timeDivision[1]);
+ };
+
+ eachNote(callback, scope) {
+
+ }
+
+ play() {
+
+ }
+
+ stop() {
+
+ }
+
+ pause() {
+
+ }
+
+ resume() {
+
+ }
+}
+
+export default MidiPlayer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/audio/midiplayer/Track.js b/ui/src/phaser3-rex-plugins/plugins/audio/midiplayer/Track.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaitloader-plugin.js b/ui/src/phaser3-rex-plugins/plugins/awaitloader-plugin.js
new file mode 100644
index 000000000..4ec10cc26
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaitloader-plugin.js
@@ -0,0 +1,15 @@
+import LoaderCallback from './loader/awaitloader/AwaitLoaderCallback.js';
+
+class AwaitLoaderPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ pluginManager.registerFileType('rexAwait', LoaderCallback);
+ }
+
+ addToScene(scene) {
+ scene.sys.load.rexAwait = LoaderCallback;
+ }
+}
+
+export default AwaitLoaderPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaitloader.d.ts b/ui/src/phaser3-rex-plugins/plugins/awaitloader.d.ts
new file mode 100644
index 000000000..039dae0b3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaitloader.d.ts
@@ -0,0 +1,3 @@
+import LoaderCallback from './loader/awaitloader/AwaitLoaderCallback.js';
+
+export default LoaderCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaitloader.js b/ui/src/phaser3-rex-plugins/plugins/awaitloader.js
new file mode 100644
index 000000000..538b2559c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaitloader.js
@@ -0,0 +1,5 @@
+import LoaderCallback from './loader/awaitloader/AwaitLoaderCallback.js';
+
+Phaser.Loader.FileTypesManager.register('rexAwait', LoaderCallback);
+
+export default LoaderCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.d.ts
new file mode 100644
index 000000000..87fb02767
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.d.ts
@@ -0,0 +1,11 @@
+import AwayTime from './awaytime'
+
+export default class AwayTimePlugin extends Phaser.Plugins.BasePlugin {
+ add(config?: AwayTime.IConfig): AwayTime;
+
+ readonly awayTime: number;
+
+ setKey(key: string): this;
+ setPeriod(time: number): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.js b/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.js
new file mode 100644
index 000000000..a43533be5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaytime-plugin.js
@@ -0,0 +1,46 @@
+import AwayTime from './awaytime.js'
+
+class AwayTimePlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ destroy() {
+ if (this._defaultAwayTimer) {
+ this._defaultAwayTimer.destroy();
+ }
+ super.destroy();
+ }
+
+ add(config) {
+ return new AwayTime(config);
+ }
+
+ get defaultAwayTimer() {
+ if (!this._defaultAwayTimer) {
+ this._defaultAwayTimer = this.add();
+ }
+ return this._defaultAwayTimer;
+ }
+
+ get awayTime() {
+ return this.defaultAwayTimer.awayTime;
+ }
+
+ setKey(key) {
+ this.defaultAwayTimer.setKey(key);
+ return this;
+ }
+
+ setPeriod(time) {
+ this.defaultAwayTimer.setPeriod(time);
+ return this;
+ }
+}
+
+export default AwayTimePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaytime.d.ts b/ui/src/phaser3-rex-plugins/plugins/awaytime.d.ts
new file mode 100644
index 000000000..5921f1e15
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaytime.d.ts
@@ -0,0 +1,2 @@
+import AwayTime from './time/awaytime/AwayTime';
+export default AwayTime;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/awaytime.js b/ui/src/phaser3-rex-plugins/plugins/awaytime.js
new file mode 100644
index 000000000..3b4a99159
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/awaytime.js
@@ -0,0 +1,2 @@
+import AwayTime from './time/awaytime/AwayTime.js';
+export default AwayTime;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bank-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bank-plugin.js
new file mode 100644
index 000000000..a656e3c02
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bank-plugin.js
@@ -0,0 +1,20 @@
+import Bank from './bank.js';
+
+class BankPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new Bank(config);
+ }
+
+}
+
+export default BankPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bank.js b/ui/src/phaser3-rex-plugins/plugins/bank.js
new file mode 100644
index 000000000..7bc321a4a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bank.js
@@ -0,0 +1,2 @@
+import Bank from './data/bank/Bank.js';
+export default Bank;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.d.ts
new file mode 100644
index 000000000..bb529fe3e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.d.ts
@@ -0,0 +1,29 @@
+// import * as Phaser from 'phaser';
+import BarrelPostFxPipeline from './barrelpipeline';
+
+export default BarrelPipelinePlugin;
+
+declare namespace BarrelPipelinePlugin {
+
+ interface IConfig extends BarrelPostFxPipeline.IConfig {
+ name?: string
+ }
+
+}
+
+declare class BarrelPipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: BarrelPipelinePlugin.IConfig
+ ): BarrelPostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): BarrelPostFxPipeline | BarrelPostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.js
new file mode 100644
index 000000000..b882e3f44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline-plugin.js
@@ -0,0 +1,14 @@
+import BarrelPostFxPipeline from './barrelpipeline';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class BarrelPipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(BarrelPostFxPipeline, 'rexBarrelPostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.BarrelPostFx', BarrelPostFxPipeline);
+
+export default BarrelPipelinePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.d.ts
new file mode 100644
index 000000000..af8116cc8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.d.ts
@@ -0,0 +1,2 @@
+import BarrelPostFxPipeline from './shaders/barrel/BarrelPostFxPipeline';
+export default BarrelPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.js b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.js
new file mode 100644
index 000000000..af8116cc8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/barrelpipeline.js
@@ -0,0 +1,2 @@
+import BarrelPostFxPipeline from './shaders/barrel/BarrelPostFxPipeline';
+export default BarrelPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bbcodetext-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bbcodetext-plugin.js
new file mode 100644
index 000000000..5a24446b4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bbcodetext-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/tagtext/bbcodetext/Factory.js';
+import Creator from './gameobjects/tagtext/bbcodetext/Creator.js';
+import BBCodeText from './gameobjects/tagtext/bbcodetext/BBCodeText.js';
+import SetValue from './utils/object/SetValue.js';
+
+class BBCodeTextPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexBBCodeText', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.BBCodeText', BBCodeText);
+
+export default BBCodeTextPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bbcodetext.d.ts b/ui/src/phaser3-rex-plugins/plugins/bbcodetext.d.ts
new file mode 100644
index 000000000..6b9c63733
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bbcodetext.d.ts
@@ -0,0 +1,2 @@
+import BBCodeText from './gameobjects/tagtext/bbcodetext/BBCodeText'
+export default BBCodeText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bbcodetext.js b/ui/src/phaser3-rex-plugins/plugins/bbcodetext.js
new file mode 100644
index 000000000..9fe534474
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bbcodetext.js
@@ -0,0 +1,2 @@
+import BBCodeText from './gameobjects/tagtext/bbcodetext/BBCodeText.js'
+export default BBCodeText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.d.ts
new file mode 100644
index 000000000..73e8e1423
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.d.ts
@@ -0,0 +1,51 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default Anchor;
+
+declare namespace Anchor {
+ type OnResizeCallbackType = (
+ width: number,
+ height: number,
+ gameObject: Phaser.GameObjects.GameObject,
+ anchor: Anchor
+ ) => void;
+
+ type OnUpdateViewportCallbackType = (
+ viewport: Phaser.Geom.Rectangle,
+ gameObject: Phaser.GameObjects.GameObject,
+ anchor: Anchor
+ ) => void;
+
+ interface IConfig {
+ left?: string, right?: string, centerX?: string, x?: string,
+ top?: string, bottom?: string, centerY?: string, y?: string,
+
+ width?: string, height?: string,
+
+ onResizeCallback?: OnResizeCallbackType,
+ onResizeCallbackScope?: unknown,
+
+ onUpdateViewportCallback?: OnUpdateViewportCallbackType,
+ onUpdateViewportCallbackScope?: unknown,
+
+ enable?: boolean
+ }
+}
+
+declare class Anchor extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Anchor.IConfig
+ );
+
+ resetFromJSON(config: Anchor.IConfig): this;
+
+ setUpdateViewportCallback(
+ callback?: Anchor.OnUpdateViewportCallbackType,
+ scope?: object
+ ): this;
+
+ anchor(): this;
+
+ autoAnchor(enable?: boolean): this;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.js
new file mode 100644
index 000000000..ac433ad7c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/anchor/Anchor.js
@@ -0,0 +1,276 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import GetViewport from '../../utils/system/GetViewport.js';
+
+class Anchor extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this.viewport = undefined;
+ this.resetFromJSON(config);
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.autoAnchor(false);
+
+ this.viewport = undefined;
+ this.onUpdateViewportCallback = undefined;
+ this.onUpdateViewportCallbackScope = undefined;
+ this.onResizeCallback = undefined;
+ this.onResizeCallbackScope = undefined;
+
+ super.shutdown(fromScene);
+ }
+
+ resetFromJSON(o) {
+ if (o === undefined) {
+ o = {};
+ }
+
+ // Position
+ var alignX, configX;
+ if (o.x !== undefined) {
+ alignX = null;
+ configX = o.x;
+ } else if (o.left !== undefined) {
+ alignX = 0;
+ configX = o.left;
+ } else if (o.right !== undefined) {
+ alignX = 1;
+ configX = o.right;
+ } else if (o.centerX !== undefined) {
+ alignX = 0.5;
+ configX = o.centerX;
+ }
+
+ var alignY, configY;
+ if (o.y !== undefined) {
+ alignY = null;
+ configY = o.y;
+ } else if (o.top !== undefined) {
+ alignY = 0;
+ configY = o.top;
+ } else if (o.bottom !== undefined) {
+ alignY = 1;
+ configY = o.bottom;
+ } else if (o.centerY !== undefined) {
+ alignY = 0.5;
+ configY = o.centerY;
+ }
+
+ var percentageX, offsetX;
+ if (configX !== undefined) {
+ configX = configX.replace('left', '0%').replace('right', '100%').replace('center', '50%').split('%');
+ percentageX = parseFloat(configX[0]) / 100;
+ offsetX = (configX[1] === '') ? 0 : parseFloat(configX[1]);
+ }
+ var percentageY, offsetY;
+ if (configY !== undefined) {
+ configY = configY.replace('top', '0%').replace('bottom', '100%').replace('center', '50%').split('%');
+ percentageY = parseFloat(configY[0]) / 100;
+ offsetY = (configY[1] === '') ? 0 : parseFloat(configY[1]);
+ }
+
+ // Size
+ var configWidth = o.width;
+ var percentageWidth, paddingWidth;
+ if (configWidth !== undefined) {
+ configWidth = configWidth.split('%');
+ percentageWidth = parseFloat(configWidth[0]) / 100;
+ paddingWidth = (configWidth[1] === '') ? 0 : parseFloat(configWidth[1]);
+ }
+
+ var configHeight = o.height;
+ var percentageHeight, paddingHeight;
+ if (configHeight !== undefined) {
+ configHeight = configHeight.split('%');
+ percentageHeight = parseFloat(configHeight[0]) / 100;
+ paddingHeight = (configHeight[1] === '') ? 0 : parseFloat(configHeight[1]);
+ }
+
+ // Position
+ this.setAlign(alignX, alignY);
+ this.setPercentage(percentageX, percentageY);
+ this.setOffset(offsetX, offsetY);
+ // Size
+ this.setSizePercentage(percentageWidth, percentageHeight);
+ this.setSizePadding(paddingWidth, paddingHeight);
+
+ var onResizeCallback = o.onResizeCallback;
+ var onResizeCallbackScope = o.onResizeCallbackScope;
+ if (onResizeCallback !== undefined) {
+ this.setResizeCallback(onResizeCallback, onResizeCallbackScope);
+ }
+
+ var onUpdateViewportCallback = o.onUpdateViewportCallback;
+ var onUpdateViewportCallbackScope = o.onUpdateViewportCallbackScope;
+ if (onUpdateViewportCallback !== undefined) {
+ this.setUpdateViewportCallback(onUpdateViewportCallback, onUpdateViewportCallbackScope);
+ }
+
+ this.autoAnchor(o.enable);
+
+ return this;
+ }
+
+ autoAnchor(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ enable = !!enable;
+ if (this.autoAnchorEnable === enable) {
+ return this;
+ }
+
+ if (enable) {
+ this.scene.sys.scale.on('resize', this.anchor, this);
+ this.anchor();
+ } else {
+ this.scene.sys.scale.off('resize', this.anchor, this);
+ }
+
+ this.autoAnchorEnable = enable;
+
+ return this;
+ }
+
+ // Position
+ setAlign(x, y) {
+ this.alignX = x;
+ this.alignY = y;
+ return this;
+ }
+
+ setPercentage(x, y) {
+ this.percentageX = x;
+ this.percentageY = y;
+ return this;
+ }
+
+ setOffset(x, y) {
+ this.offsetX = x;
+ this.offsetY = y;
+ return this;
+ }
+
+ // Size
+ setSizePercentage(width, height) {
+ this.percentageWidth = width;
+ this.percentageHeight = height;
+ return this;
+ }
+
+ setSizePadding(width, height) {
+ this.paddingWidth = width;
+ this.paddingHeight = height;
+ return this;
+ }
+
+ setResizeCallback(callback, scope) {
+ this.onResizeCallback = callback;
+ this.onResizeCallbackScope = scope;
+ return this;
+ }
+
+ setUpdateViewportCallback(callback, scope) {
+ this.onUpdateViewportCallback = callback;
+ this.onUpdateViewportCallbackScope = scope;
+ return this;
+ }
+
+ anchor() {
+ this.updateViewport();
+ this.updateSize();
+ this.updatePosition();
+ return this;
+ }
+
+ updateSize() {
+ var callback = this.onResizeCallback,
+ scope = this.onResizeCallbackScope;
+ var newWidth = this.anchorWidth,
+ newHeight = this.anchorHeight;
+ if (((newWidth === undefined) && (newHeight === undefined)) || !callback) {
+ return;
+ }
+
+ var gameObject = this.parent;
+ if (newWidth === undefined) {
+ newWidth = gameObject.width;
+ }
+ if (newHeight === undefined) {
+ newHeight = gameObject.height;
+ }
+
+ if (scope) {
+ callback.call(scope, newWidth, newHeight, gameObject, this);
+ } else {
+ callback(newWidth, newHeight, gameObject, this);
+ }
+ }
+
+ updatePosition() {
+ var gameObject = this.parent;
+
+ if (this.alignX === null) {
+ gameObject.x = this.anchorX;
+ } else if (this.alignX !== undefined) {
+ gameObject.x = this.anchorX + (gameObject.displayWidth * (gameObject.originX - this.alignX));
+ }
+
+ if (this.alignY === null) {
+ gameObject.y = this.anchorY;
+ } else if (this.alignY !== undefined) {
+ gameObject.y = this.anchorY + (gameObject.displayHeight * (gameObject.originY - this.alignY));
+ }
+
+ return this;
+ }
+
+ get anchorX() {
+ return this.viewport.x + (this.viewport.width * this.percentageX) + this.offsetX;
+ }
+
+ get anchorY() {
+ return this.viewport.y + (this.viewport.height * this.percentageY) + this.offsetY;
+ }
+
+ get anchorWidth() {
+ if (this.percentageWidth === undefined) {
+ return undefined;
+ }
+ return (this.viewport.width * this.percentageWidth) + this.paddingWidth;
+ }
+
+ get anchorHeight() {
+ if (this.percentageHeight === undefined) {
+ return undefined;
+ }
+ return (this.viewport.height * this.percentageHeight) + this.paddingHeight;
+ }
+
+ updateViewport() {
+ var camera = this.parent.scene.cameras.main;
+ this.viewport = GetViewport(this.scene, camera, this.viewport);
+
+ var viewport = this.viewport;
+ var callback = this.onUpdateViewportCallback,
+ scope = this.onUpdateViewportCallbackScope;
+ if (callback) {
+ if (scope) {
+ callback.call(scope, viewport, this.parent, this);
+ } else {
+ callback(viewport, this.parent, this);
+ }
+ }
+ }
+}
+
+export default Anchor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.d.ts
new file mode 100644
index 000000000..d690c63b9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.d.ts
@@ -0,0 +1,28 @@
+export default BitmapZone;
+
+declare namespace BitmapZone {
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+
+ scaleX?: number, scaleY?: number,
+ offsetX?: number, offsetY?: number,
+ }
+}
+
+declare class BitmapZone {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: BitmapZone.IConfig
+ );
+
+ getRandomPoint: Phaser.Types.GameObjects.Particles.RandomZoneSourceCallback;
+
+ setOffset(offsetX?: number, offsetY?: number): this;
+ offsetX: number;
+ offsetY: number;
+
+ setScale(scaleX?: number, scaleY?: number): this;
+ scaleX: number;
+ scaleY: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.js
new file mode 100644
index 000000000..be73cd18f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bitmapzone/BitmapZone.js
@@ -0,0 +1,87 @@
+const GetRandom = Phaser.Utils.Array.GetRandom;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class BitmapZone {
+ constructor(canvasObject, config) {
+ this.data = [];
+ this.setSource(canvasObject, config);
+ }
+
+ setSource(canvasObject, config) {
+ var canvas = canvasObject.canvas;
+
+ var x = GetValue(config, 'x', 0);
+ var y = GetValue(config, 'y', 0);
+ var width = GetValue(config, 'width', canvas.width - x);
+ var height = GetValue(config, 'height', canvas.height - y);
+
+ var context = canvas.getContext('2d', { willReadFrequently: true });
+ var imgData = context.getImageData(x, y, width, height).data;
+ var data = this.data;
+ data.length = 0;
+ for (var i = 0, cnt = (imgData.length / 4); i < cnt; i++) {
+ if (imgData[(i * 4) + 3] > 0) {
+ data.push(i);
+ }
+ }
+
+ this.width = width;
+ this.height = height;
+
+ var scaleX = GetValue(config, 'scaleX', canvasObject);
+ var scaleY = GetValue(config, 'scaleY', undefined);
+ this.setScale(scaleX, scaleY);
+
+ var offsetX = GetValue(config, 'offsetX', canvasObject);
+ var offsetY = GetValue(config, 'offsetY', undefined);
+ this.setOffset(offsetX, offsetY);
+
+ return this;
+ }
+
+ setOffset(offsetX, offsetY) {
+ if (typeof (offsetX) !== 'number') {
+ var canvasObject = offsetX;
+ offsetX = -(canvasObject.originX * canvasObject.displayWidth);
+ offsetY = -(canvasObject.originY * canvasObject.displayHeight);
+ }
+ this.offsetX = offsetX;
+ this.offsetY = offsetY;
+ return this;
+ }
+
+ setScale(scaleX, scaleY) {
+ if (typeof (scaleX) !== 'number') {
+ var canvasObject = scaleX;
+ scaleX = canvasObject.scaleX;
+ scaleY = canvasObject.scaleY;
+ }
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ getRandomPoint(out) {
+ if (out === undefined) {
+ out = {};
+ }
+ if (this.data.length > 0) {
+ var index = GetRandom(this.data);
+ var x = index % this.width;
+ var y = (index - x) / this.width;
+ out.x = x * this.scaleX;
+ out.y = y * this.scaleY;
+ } else {
+ out.x = 0;
+ out.y = 0;
+ }
+ out.x += this.offsetX;
+ out.y += this.offsetY;
+ return out;
+ }
+}
+
+export default BitmapZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddAlignmentForce.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddAlignmentForce.js
new file mode 100644
index 000000000..7e742ef52
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddAlignmentForce.js
@@ -0,0 +1,43 @@
+const Vector2 = Phaser.Math.Vector2;
+const Distance = Phaser.Math.Distance.Between;
+
+var AddAlignmentForce = function (myAgent, neighbors, weight, distanceThreshold, out) {
+ // Steer towards average heading of neighbors
+ if (out === undefined) {
+ out = new Vector2();
+ }
+ if (weight <= 0) {
+ return out;
+ }
+ if ((neighbors.length == 0) ||
+ ((neighbors.length === 1) && (neighbors[0] === myAgent))) {
+ return out;
+ }
+
+ var sum = 0, validNeighborsCount = 0;
+ var agent;
+ for (var i = 0, cnt = neighbors.length; i < cnt; i++) {
+ agent = neighbors[i];
+ if (agent === myAgent) {
+ continue;
+ }
+ if (Distance(agent.x, agent.y, myAgent.x, myAgent.y) > distanceThreshold) {
+ continue;
+ }
+
+ sum += agent.rotation;
+ validNeighborsCount++;
+ }
+
+ if (validNeighborsCount === 0) {
+ return out;
+ }
+ var angle = sum / validNeighborsCount;
+ var p = weight;
+ out.x += (Math.cos(angle) * p);
+ out.y += (Math.sin(angle) * p);
+
+ return out;
+}
+
+export default AddAlignmentForce;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddCohesionForce.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddCohesionForce.js
new file mode 100644
index 000000000..363538233
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddCohesionForce.js
@@ -0,0 +1,54 @@
+const Vector2 = Phaser.Math.Vector2;
+const Distance = Phaser.Math.Distance.Between;
+
+var AddCohesionForce = function (myAgent, neighbors, weight, distanceThreshold, out) {
+ // Steer towards average position of neighbors (long range attraction)
+ if (out === undefined) {
+ out = new Vector2();
+ }
+ if (weight <= 0) {
+ return out;
+ }
+ if ((neighbors.length == 0) ||
+ ((neighbors.length === 1) && (neighbors[0] === myAgent))) {
+ return out;
+ }
+
+ centerPosition.reset();
+ var agent, validNeighborsCount = 0;
+ for (var i = 0, cnt = neighbors.length; i < cnt; i++) {
+ agent = neighbors[i];
+ if (agent === myAgent) {
+ continue;
+ }
+ if (Distance(agent.x, agent.y, myAgent.x, myAgent.y) > distanceThreshold) {
+ continue;
+ }
+
+ centerPosition.add(agent);
+ validNeighborsCount++;
+ }
+ if (validNeighborsCount === 0) {
+ return out;
+ }
+ centerPosition.scale(1 / validNeighborsCount);
+
+ var dx = centerPosition.x - myAgent.x;
+ var dy = centerPosition.y - myAgent.y;
+ var d = Math.sqrt((dx * dx) + (dy * dy));
+
+ var p = weight;
+ if (distanceThreshold !== Infinity) {
+ p *= (d / distanceThreshold);
+ }
+
+ var angle = Math.atan2(dy, dx);
+ out.x += (Math.cos(angle) * p);
+ out.y += (Math.sin(angle) * p);
+
+ return out;
+}
+
+var centerPosition = new Vector2();
+
+export default AddCohesionForce;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddSeparationForce.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddSeparationForce.js
new file mode 100644
index 000000000..9010a350d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/AddSeparationForce.js
@@ -0,0 +1,43 @@
+const Vector2 = Phaser.Math.Vector2;
+
+var AddSeparationForce = function (myAgent, neighbors, weight, distanceThreshold, out) {
+ // Steer to avoid crowding neighbors
+ if (out === undefined) {
+ out = new Vector2();
+ }
+ if (weight <= 0) {
+ return out;
+ }
+ if ((neighbors.length == 0) ||
+ ((neighbors.length === 1) && (neighbors[0] === myAgent))) {
+ return out;
+ }
+
+ var agent;
+ var dx, dy, angle, d, p;
+ for (var i = 0, cnt = neighbors.length; i < cnt; i++) {
+ agent = neighbors[i];
+ if (agent === myAgent) {
+ continue;
+ }
+
+ dx = myAgent.x - agent.x;
+ dy = myAgent.y - agent.y;
+ d = Math.sqrt((dx * dx) + (dy * dy));
+ if (d > distanceThreshold) { // out-of-range
+ continue;
+ }
+
+ p = weight;
+ if (distanceThreshold !== Infinity) {
+ p *= (distanceThreshold - d) / distanceThreshold;
+ }
+ angle = Math.atan2(dy, dx);
+ out.x += (Math.cos(angle) * p);
+ out.y += (Math.sin(angle) * p);
+ }
+
+ return out;
+}
+
+export default AddSeparationForce;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.d.ts
new file mode 100644
index 000000000..b61b5e6ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.d.ts
@@ -0,0 +1,38 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default Boids;
+
+declare namespace Boids {
+
+ interface IConfig {
+ separation?: {
+ weight?: number,
+ distance?: number,
+ },
+
+ cohesion?: {
+ weight?: number,
+ distance?: number,
+ },
+
+ alignment?: {
+ weight?: number,
+ distance?: number,
+ },
+ }
+}
+
+declare class Boids extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Boids.IConfig
+ );
+
+ readonly output: Phaser.Math.Vector2;
+
+ setSeparationParameters(weight: number, distance: number): this;
+ setCohesionParameters(weight: number, distance: number): this;
+ setAlignmentParameters(weight: number, distance: number): this;
+
+ update(neighbors: Phaser.GameObjects.GameObject[]): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.js
new file mode 100644
index 000000000..3b8d45792
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/boids/Boids.js
@@ -0,0 +1,83 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import AddSeparationForce from './AddSeparationForce.js';
+import AddAlignmentForce from './AddAlignmentForce.js';
+import AddCohesionForce from './AddCohesionForce.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Vector2 = Phaser.Math.Vector2;
+
+class Boids extends ComponentBase {
+ constructor(parent, config) {
+ super(parent, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this.output = new Vector2();
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.setSeparationParameters(GetValue(o, 'separation.weight', 0), GetValue(o, 'separation.distance', Infinity));
+ this.setCohesionParameters(GetValue(o, 'cohesion.weight', 0), GetValue(o, 'cohesion.distance', Infinity));
+ this.setAlignmentParameters(GetValue(o, 'alignment.weight', 0), GetValue(o, 'alignment.distance', Infinity));
+ return this;
+ }
+
+ setSeparationParameters(weight, distance) {
+ this.separationWeight = weight;
+ this.separationDistance = distance;
+ return this;
+ }
+
+ setCohesionParameters(weight, distance) {
+ this.cohesionWeight = weight;
+ this.cohesionDistance = distance;
+ return this;
+ }
+
+ setAlignmentParameters(weight, distance) {
+ this.alignmentWeight = weight;
+ this.alignmentDistance = distance;
+ return this;
+ }
+
+ update(neighbors) {
+ this.output.reset();
+ AddSeparationForce(this.parent, neighbors, this.separationWeight, this.separationDistance, this.output);
+ AddCohesionForce(this.parent, neighbors, this.cohesionWeight, this.cohesionDistance, this.output);
+ AddAlignmentForce(this.parent, neighbors, this.alignmentWeight, this.alignmentDistance, this.output);
+ return this;
+ }
+
+ addSeparationForce(neighbors, separationWeight, separationDistance, output) {
+ if (separationWeight === undefined) {
+ separationWeight = this.separationWeight;
+ }
+ if (separationDistance === undefined) {
+ separationDistance = this.separationDistance;
+ }
+ AddSeparationForce(this.parent, neighbors, separationWeight, separationDistance, output);
+ return this;
+ }
+
+ addAlignmentForce(neighbors, alignmentWeight, output) {
+ if (alignmentWeight === undefined) {
+ alignmentWeight = this.alignmentWeight;
+ }
+ AddAlignmentForce(this.parent, neighbors, alignmentWeight, output);
+ return this;
+ }
+
+ addCohesionForce(neighbors, cohesionWeight, cohesionDistance, output) {
+ if (cohesionWeight === undefined) {
+ cohesionWeight = this.cohesionWeight;
+ }
+ if (cohesionDistance === undefined) {
+ cohesionDistance = this.cohesionDistance;
+ }
+ AddCohesionForce(this.parent, neighbors, cohesionWeight, cohesionDistance, output);
+ return this;
+ }
+}
+
+export default Boids;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.d.ts
new file mode 100644
index 000000000..360a15aba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.d.ts
@@ -0,0 +1,53 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Bounds;
+
+declare namespace Bounds {
+
+ type RectangleLikeType = {
+ x?: number, y?: number,
+ centerX?: number, centerY?: number,
+ width?: number, height?: number,
+ }
+
+ type BoundsEnableType = {
+ left?: boolean, right?: boolean,
+ top?: boolean, bottom?: boolean,
+ }
+
+ type AlignModeType = 0 | 1 | 'bounds' | 'origin';
+
+ interface IConfig {
+ target?: Phaser.GameObjects.GameObject;
+ bounds?: Phaser.Geom.Rectangle | RectangleLikeType;
+ enable?: boolean | BoundsEnableType;
+ alignMode?: AlignModeType;
+ }
+}
+
+declare class Bounds extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Bounds.IConfig
+ )
+
+ setBoundsTarget(gameObject?: Phaser.GameObjects.GameObject): this;
+ boundsTarget: Phaser.GameObjects.GameObject;
+
+ setBounds(bounds: Phaser.Geom.Rectangle | Bounds.RectangleLikeType): this;
+ bounds: Phaser.Geom.Rectangle;
+
+ setEnable(enable?: boolean | Bounds.BoundsEnableType): this;
+ enable: boolean;
+ boundsEnable: { left: boolean, right: boolean, top: boolean, bottom: boolean };
+
+ setAlignMode(mode: Bounds.AlignModeType): this;
+ alignMode: number;
+
+ readonly isHitAny: boolean;
+ readonly isHitLeft: boolean;
+ readonly isHitRight: boolean;
+ readonly isHitTop: boolean;
+ readonly isHitBottom: boolean;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.js
new file mode 100644
index 000000000..1a94a8465
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bounds/Bounds.js
@@ -0,0 +1,195 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import { GetBounds } from '../../utils/bounds/GetBounds.js';
+
+const Rectangle = Phaser.Geom.Rectangle;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Bounds extends TickTask {
+ constructor(gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ config.tickEventName = 'postupdate';
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.bounds = new Rectangle();
+ this.boundsTarget = undefined;
+ this.boundsEnable = {};
+ this.clearHitResult();
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ var target = GetValue(o, 'target');
+ if (target) {
+ this.setBoundsTarget(target);
+ } else {
+ this.setBoundsTarget();
+ this.setBounds(GetValue(o, 'bounds'));
+ }
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setAlignMode(GetValue(o, 'alignMode', 0));
+
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ super.shutdown(fromScene);
+ }
+
+ setBoundsTarget(gameObject) {
+ this.boundsTarget = gameObject;
+ return this;
+ }
+
+ setBounds(boundsConfig) {
+ if (!boundsConfig) {
+ return this;
+ }
+
+ var bounds = this.bounds;
+
+ bounds.setSize(
+ GetValue(boundsConfig, 'width', 0),
+ GetValue(boundsConfig, 'height', 0)
+ )
+ if (boundsConfig.hasOwnProperty('centerX')) {
+ bounds.centerX = boundsConfig.centerX;
+ } else {
+ bounds.x = GetValue(boundsConfig, 'x', 0);
+ }
+ if (boundsConfig.hasOwnProperty('centerY')) {
+ bounds.centerY = boundsConfig.centerY;
+ } else {
+ bounds.y = GetValue(boundsConfig, 'y', 0);
+ }
+
+ return this;
+ }
+
+ setEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ var boundsEnable = this.boundsEnable;
+ if (typeof (enable) === 'boolean') {
+ boundsEnable.left = enable;
+ boundsEnable.right = enable;
+ boundsEnable.top = enable;
+ boundsEnable.bottom = enable;
+ } else {
+ boundsEnable.left = GetValue(enable, 'left', false);
+ boundsEnable.right = GetValue(enable, 'right', false);
+ boundsEnable.top = GetValue(enable, 'top', false);
+ boundsEnable.bottom = GetValue(enable, 'bottom', false);
+ }
+
+ this.isRunning = this.enable;
+
+ return this;
+ }
+
+ setAlignMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = AlignMode[mode];
+ }
+ this.alignMode = mode;
+ return this;
+ }
+
+ get enable() {
+ var boundsEnable = this.boundsEnable;
+ return boundsEnable.left || boundsEnable.right || boundsEnable.top || boundsEnable.bottom;
+ }
+
+ set enable(value) {
+ this.setEnable(value);
+ }
+
+ update(time, delta) {
+ var gameObject = this.parent;
+ this.clearHitResult();
+ if (!this.enable) {
+ return this;
+ }
+
+ var target = this.boundsTarget;
+ if (target) {
+ GetBounds(target, this.bounds);
+ }
+
+ var bounds = this.bounds;
+ var boundsEnable = this.boundsEnable;
+
+ var alignToGOBound = (this.alignMode === 0);
+ var gameObjectBounds = (alignToGOBound) ? GetBounds(gameObject, true) : undefined;
+
+ if (boundsEnable.left) {
+ var left = (alignToGOBound) ? gameObjectBounds.left : gameObject.x;
+ var dx = bounds.left - left;
+ if (dx > 0) {
+ gameObject.x += dx;
+ this.isHitAny = true;
+ this.isHitLeft = true;
+ this.emit('hitleft', this.parent, this);
+ }
+ }
+ if (boundsEnable.right) {
+ var right = (alignToGOBound) ? gameObjectBounds.right : gameObject.x;
+ var dx = bounds.right - right;
+ if (dx < 0) {
+ gameObject.x += dx;
+ this.isHitAny = true;
+ this.isHitRight = true;
+ this.emit('hitright', this.parent, this);
+ }
+ }
+ if (boundsEnable.top) {
+ var top = (alignToGOBound) ? gameObjectBounds.top : gameObject.y;
+ var dy = bounds.top - top;
+ if (dy > 0) {
+ gameObject.y += dy;
+ this.isHitAny = true;
+ this.isHitTop = true;
+ this.emit('hittop', this.parent, this);
+ }
+ }
+ if (boundsEnable.bottom) {
+ var bottom = (alignToGOBound) ? gameObjectBounds.bottom : gameObject.y;
+ var dy = bounds.bottom - bottom;
+ if (dy < 0) {
+ gameObject.y += dy;
+ this.isHitAny = true;
+ this.isHitBottom = true;
+ this.emit('hitbottom', this.parent, this);
+ }
+ }
+
+ if (this.isHitAny) {
+ this.emit('hitany', this.parent, this);
+ }
+ }
+
+ clearHitResult() {
+ this.isHitAny = false;
+ this.isHitLeft = false;
+ this.isHitRight = false;
+ this.isHitTop = false;
+ this.isHitBottom = false;
+ return this;
+ }
+}
+
+const AlignMode = {
+ bounds: 0,
+ origin: 1
+}
+
+export default Bounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.d.ts
new file mode 100644
index 000000000..621f20311
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.d.ts
@@ -0,0 +1,35 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Bullet;
+
+declare namespace Bullet {
+
+ interface IConfig {
+ speed?: number,
+ enable?: boolean,
+ wrap?: boolean,
+ padding?: number,
+
+ angle?: number,
+ rotation?: number,
+ }
+}
+
+declare class Bullet extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Bullet.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setSpeed(speed: number): this;
+ speed: number;
+
+ setAngle(angle?: number): this;
+ angle: number | undefined;
+
+ setRotation(rotation?: number): this;
+ rotation: number | undefined;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.js
new file mode 100644
index 000000000..1e8d0f439
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/bullet/Bullet.js
@@ -0,0 +1,122 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import { SetVelocity } from '../../utils/arcade/Helpers.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const DegToRad = Phaser.Math.DegToRad;
+const RadToDeg = Phaser.Math.RadToDeg;
+
+class Bullet extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ if (!this.parent.body) {
+ this.scene.physics.add.existing(this.parent, false);
+ }
+ this.setWrapMode(GetValue(o, 'wrap', false), GetValue(o, 'padding', 0));
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setSpeed(GetValue(o, 'speed', 200));
+
+ var angle = GetValue(o, 'angle');
+ if (angle !== undefined) {
+ this.setAngle(angle);
+ var rotation = GetValue(o, 'rotation');
+ if (rotation !== undefined) {
+ this.setRotation(rotation);
+ }
+ }
+
+ return this;
+ }
+
+ get enable() {
+ return this.isRunning;
+ }
+
+ set enable(value) {
+ this.isRunning = value;
+ if (!value) {
+ SetVelocity(this.parent, 0, 0);
+ }
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ setWrapMode(wrap, padding) {
+ if (wrap === undefined) {
+ wrap = true;
+ }
+ this.wrap = wrap;
+ this.padding = padding;
+ return this;
+ }
+
+ setAngle(angle) {
+ this.angle = angle;
+ return this;
+ }
+
+ setRotation(rotation) {
+ this.rotation = rotation;
+ return this;
+ }
+
+ set angle(value) {
+ if (typeof (value) === 'number') {
+ value = DegToRad(value);
+ }
+ this.rotation = value;
+ }
+
+ get angle() {
+ var value = this.rotation;
+ if (typeof (value) === 'number') {
+ value = RadToDeg(value);
+ }
+ return value;
+ }
+
+ update(time, delta) {
+ var gameObject = this.parent;
+ if (!this.enable) {
+ SetVelocity(gameObject, 0, 0);
+ return this;
+ }
+
+ if (!gameObject.active) {
+ return this;
+ }
+
+ var rotation = this.rotation;
+ if (rotation == null) {
+ rotation = gameObject.rotation;
+ }
+ var vx = this.speed * Math.cos(rotation);
+ var vy = this.speed * Math.sin(rotation);
+ SetVelocity(gameObject, vx, vy);
+
+ if (this.wrap) {
+ gameObject.body.world.wrap(gameObject, this.padding);
+ }
+
+ return this;
+ }
+}
+
+export default Bullet;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.d.ts
new file mode 100644
index 000000000..1c2c8c6d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.d.ts
@@ -0,0 +1,23 @@
+import RenderTexture from '../../gameobjects/mesh/perspective/rendertexture/RenderTexture';
+import ContainerLite from '../../gameobjects/container/containerlite/ContainerLite';
+
+export default ContainerPerspective;
+
+declare namespace ContainerPerspective {
+
+ interface IConfig {
+ useParentBounds?: boolean,
+ }
+
+}
+
+declare class ContainerPerspective extends RenderTexture {
+ constructor(
+ parentContainer: ContainerLite,
+ config?: ContainerPerspective.IConfig
+ );
+
+ enter(): this;
+ exit(): this;
+ readonly perspectiveState: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.js
new file mode 100644
index 000000000..7b2271700
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerperspective/ContainerPerspective.js
@@ -0,0 +1,10 @@
+import MeshRenderTextureBase from '../../gameobjects/container/containerlite/rendertexture/MeshRenderTextureBase.js';
+import RenderTexture from '../../gameobjects/mesh/perspective/rendertexture/RenderTexture.js';
+
+class ContainerPerspective extends MeshRenderTextureBase(RenderTexture) {
+ get perspectiveState() {
+ return this.isRunning;
+ }
+}
+
+export default ContainerPerspective;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.d.ts
new file mode 100644
index 000000000..c2317927c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.d.ts
@@ -0,0 +1,23 @@
+import RenderTexture from '../../gameobjects/mesh/quad/skewrendertexture/SkewRenderTexture';
+import ContainerLite from '../../gameobjects/container/containerlite/ContainerLite';
+
+export default ContainerSkew;
+
+declare namespace ContainerSkew {
+
+ interface IConfig {
+ useParentBounds?: boolean,
+ }
+
+}
+
+declare class ContainerSkew extends RenderTexture {
+ constructor(
+ parentContainer: ContainerLite,
+ config?: ContainerSkew.IConfig
+ );
+
+ enter(): this;
+ exit(): this;
+ readonly skewState: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.js
new file mode 100644
index 000000000..5d86618c6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/containerskew/ContainerSkew.js
@@ -0,0 +1,10 @@
+import MeshRenderTextureBase from '../../gameobjects/container/containerlite/rendertexture/MeshRenderTextureBase.js';
+import RenderTexture from '../../gameobjects/mesh/quad/skewrendertexture/SkewRenderTexture.js';
+
+class ContainerSkew extends MeshRenderTextureBase(RenderTexture) {
+ get skewState() {
+ return this.isRunning;
+ }
+}
+
+export default ContainerSkew;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/DropDown.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/DropDown.js
new file mode 100644
index 000000000..2f9db45d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/DropDown.js
@@ -0,0 +1,99 @@
+import OpenCloseTransition from '../openclosetransition/OpenCloseTransition.js';
+import PopUp from '../../popup.js';
+import ScaleDown from '../scale/ScaleDown.js';
+import SetPosition from './SetPosition.js';
+import IsPointInBounds from '../../utils/bounds/IsPointInBounds.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class DropDown extends OpenCloseTransition {
+ constructor(gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ if (config.transitIn == null) {
+ config.transitIn = function (gameObject, duration) {
+ PopUp(gameObject, duration, 'y', 'Cubic')
+ };
+ }
+ if (config.transitOut == null) {
+ config.transitOut = function (gameObject, duration) {
+ // Don't destroy here
+ ScaleDown(gameObject, duration, 'y', 'Linear')
+ };
+ }
+ config.manualClose = true;
+ config.clickOutsideClose = true;
+ config.destroy = true;
+
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.scene
+
+ SetPosition(gameObject, config);
+
+ if (gameObject.isRexSizer) {
+ gameObject.layout();
+ }
+
+ // Close conditions:
+ var touchOutsideClose = GetValue(config, 'touchOutsideClose', false);
+ var anyTouchClose = GetValue(config, 'anyTouchClose', false);
+
+ if (anyTouchClose) {
+ touchOutsideClose = false;
+ }
+
+ // Registet touch-close event after opened
+ if (anyTouchClose) {
+ this.once('open', this.anyTouchClose, this);
+ } else if (touchOutsideClose) {
+ this.once('open', this.touchOutsideClose, this);
+ }
+
+ this.requestOpen();
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ // Registered in touchOutsideClose()
+ this.scene.input.off('pointerup', this.touchCloseCallback, this);
+
+ super.shutdown(fromScene);
+ }
+
+ touchOutsideClose() {
+ this.scene.input.on('pointerup', this.touchCloseCallback, this);
+ this.clickOutsideTest = true;
+ return this;
+ }
+
+ anyTouchClose() {
+ this.scene.input.once('pointerup', this.touchCloseCallback, this);
+ return this;
+ }
+
+ touchCloseCallback(pointer) {
+ if (this.clickOutsideTest && IsPointInBounds(this.parent, pointer.worldX, pointer.worldY)) {
+ return;
+ }
+ this.requestClose();
+ }
+
+ onOpen() {
+ this.emit('open', this.parent, this);
+ super.onOpen();
+ }
+
+ onClose() {
+ this.emit('close', this.parent, this);
+ super.onClose();
+ }
+
+}
+
+export default DropDown;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/Dropdown.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/Dropdown.d.ts
new file mode 100644
index 000000000..9a95236ef
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/Dropdown.d.ts
@@ -0,0 +1,45 @@
+export default DropDown;
+
+declare namespace DropDown {
+ type TransitCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number
+ ) => void;
+
+ interface IConfig {
+
+ duration?: {
+ in?: number,
+ out?: number,
+ },
+
+ transitIn?: TransitCallbackType,
+ transitOut?: TransitCallbackType,
+
+ expandDirection?: 0 | 1 | 'down' | 'up',
+
+ alignTarget?: Phaser.GameObjects.GameObject,
+ alignTargetX?: Phaser.GameObjects.GameObject,
+ alignTargetY?: Phaser.GameObjects.GameObject,
+ alignOffsetX?: number,
+ alignOffsetY?: number,
+ alignSide?: string,
+
+ bounds?: Phaser.Geom.Rectangle,
+
+ touchOutsideClose?: boolean,
+
+ anyTouchClose?: boolean,
+
+ destroy?: boolean,
+ }
+}
+
+declare class DropDown extends Phaser.Events.EventEmitter {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: DropDown.IConfig,
+ )
+
+ requestClose(closeEventData: any): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/SetPosition.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/SetPosition.js
new file mode 100644
index 000000000..7a3e23b22
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/dropdown/SetPosition.js
@@ -0,0 +1,65 @@
+import GetValueFromAlias from '../../utils/object/GetValueFromAliasKeys.js';
+import GetViewport from '../../utils/system/GetViewport.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var SetPosition = function (gameObject, config) {
+ var expandDirection = GetValue(config, 'expandDirection', undefined);
+ if (typeof (expandDirection) === 'string') {
+ expandDirection = ExpandDirections[expandDirection];
+ }
+ var alignTargetX = GetValueFromAlias(config, 'alignTarget', 'alignTargetX');
+ var alignTargetY = GetValue(config, 'alignTargetY', alignTargetX);
+ var alignOffsetX = GetValue(config, 'alignOffsetX', 0);
+ var alignOffsetY = GetValue(config, 'alignOffsetY', 0);
+ var alignSide = GetValue(config, 'alignSide', '');
+ var alignRight = alignSide.includes('right');
+
+ var positionBounds = GetValue(config, 'bounds');
+
+ // Expand direction
+ var isExpandDown = (expandDirection === 0);
+ var isExpandUp = (expandDirection === 1);
+ var flexExpand = !isExpandDown && !isExpandUp;
+
+ var originX = (alignRight) ? 1 : 0;
+ var originY = (isExpandDown || flexExpand) ? 0 : 1;
+ gameObject.setOrigin(originX, originY);
+
+ var x, y;
+ if (alignRight) {
+ x = alignTargetX.getTopRight().x;
+ } else {
+ x = alignTargetX.getTopLeft().x;
+ }
+
+ y = alignTargetY.getBottomLeft().y;
+ gameObject.setPosition(
+ x + alignOffsetX,
+ y + alignOffsetY
+ );
+
+ var bounds = positionBounds;
+ if (!bounds) {
+ bounds = GetViewport(gameObject.scene);
+ }
+
+ if (flexExpand && (gameObject.getBottomLeft().y > bounds.bottom)) {
+ // Out of bounds, can't put list-panel below parent
+ y = alignTargetY.getTopLeft().y;
+ gameObject
+ .setOrigin(0, 1)
+ .setPosition(
+ x + alignOffsetX,
+ y + alignOffsetY
+ );
+ }
+
+}
+
+const ExpandDirections = {
+ down: 0,
+ up: 1
+}
+
+export default SetPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.d.ts
new file mode 100644
index 000000000..1cda512bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.d.ts
@@ -0,0 +1,53 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default EaseData;
+
+declare namespace EaseData {
+ interface IConfig extends ComponentBase.IConfig {
+ }
+}
+
+declare class EaseData extends ComponentBase {
+ easeTo(
+ key: string,
+ value: number,
+ duration?: number,
+ ease?: string
+ ): this;
+
+ easeTo(
+ config: {
+ key: string,
+ value: number,
+ duration?: number,
+ ease?: string,
+ speed?: number
+ }
+ ): this;
+
+ easeFrom(
+ key: string,
+ value: number,
+ duration?: number,
+ ease?: string
+ ): this;
+
+ easeFrom(
+ config: {
+ key: string,
+ value: number,
+ duration?: number,
+ ease?: string,
+ speed?: number
+ }
+ ): this;
+
+ stopEase(
+ key: string,
+ toEnd?: boolean
+ ): this;
+
+ stopAll(
+ toEnd?: boolean
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.js
new file mode 100644
index 000000000..a0fd8c48a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easedata/EaseData.js
@@ -0,0 +1,123 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import EaseValueTask from '../../utils/ease/EaseValueTask.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+
+class EaseData extends ComponentBase {
+ constructor(parent, config) {
+ super(parent, config);
+
+ this.parent.setDataEnabled();
+ this.easeTasks = {};
+ }
+
+ complete(key) {
+ this.emit(`complete-${key}`, this.parent, this);
+ this.emit('complete', key, this.parent, this);
+ }
+
+ getEaseTask(key) {
+ var easeTask = this.easeTasks[key];
+ if (easeTask === undefined) {
+ easeTask = new EaseValueTask(this.parent);
+ this.easeTasks[key] = easeTask;
+
+ easeTask
+ .setTarget(this.parent.data.values)
+ .on('complete', function () {
+ this.complete(key);
+ }, this);
+ }
+ return easeTask;
+ }
+
+ easeTo(key, value, duration, ease) {
+ if (IsPlainObject(key)) {
+ var config = key;
+ key = config.key;
+ value = config.value;
+ duration = config.duration;
+ ease = config.ease;
+
+ var speed = config.speed;
+ if ((duration === undefined) && (speed !== undefined)) {
+ duration = (Math.abs(value - this.parent.data.values[key]) / speed) * 1000;
+ }
+ }
+
+ if (duration === undefined) {
+ duration = 1000;
+ }
+ if (ease === undefined) {
+ ease = 'Linear';
+ }
+
+ var easeTask = this.getEaseTask(key);
+ easeTask.restart({
+ key: key,
+ to: value,
+ duration: duration,
+ ease: ease
+ });
+
+ return this;
+ }
+
+ easeFrom(key, value, duration, ease) {
+ if (IsPlainObject(key)) {
+ var config = key;
+ key = config.key;
+ value = config.value;
+ duration = config.duration;
+ ease = config.ease;
+
+ var speed = config.speed;
+ if ((duration === undefined) && (speed !== undefined)) {
+ duration = (Math.abs(value - this.parent.data.values[key]) / speed) * 1000;
+ }
+ }
+
+ if (duration === undefined) {
+ duration = 1000;
+ }
+ if (ease === undefined) {
+ ease = 'Linear';
+ }
+
+ var easeTask = this.getEaseTask(key);
+ easeTask.restart({
+ key: key,
+ from: value,
+ duration: duration,
+ ease: ease
+ });
+
+ return this;
+ }
+
+ stopEase(key, toEnd) {
+ if (toEnd === undefined) {
+ toEnd = true;
+ }
+
+ var easeTask = this.easeTasks[key];
+ if (easeTask) {
+ easeTask.stop(toEnd);
+ }
+
+ return this;
+ }
+
+ stopAll(toEnd) {
+ if (toEnd === undefined) {
+ toEnd = true;
+ }
+
+ for (var key in this.easeTasks) {
+ this.stopEase(key, toEnd);
+ }
+ return this;
+ }
+}
+
+export default EaseData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.d.ts
new file mode 100644
index 000000000..846172719
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.d.ts
@@ -0,0 +1,48 @@
+import EaseValueTaskBase from "../../utils/componentbase/tweentask/EaseValueTaskBase";
+
+export default EaseMove;
+
+declare namespace EaseMove {
+ type ModeType = 0 | 1 | 2 | 'stop' | 'destroy' | 'yoyo';
+
+ interface IConfig {
+ mode?: ModeType,
+
+ x?: number, y?: number,
+ startX?: number, startY?: number,
+ endX?: number, endY?: number,
+
+ duration?: number,
+ delay?: number,
+ ease?: string
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ easeMove: EaseMove
+ ) => void;
+ }
+}
+
+declare class EaseMove extends EaseValueTaskBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: EaseMove.IConfig
+ )
+
+ setMode(mode: EaseMove.ModeType): this;
+ mode: number;
+
+ setTargetPosition(x: number, y: number): this;
+ setTargetPosition(
+ config?: {
+ startX?: number, startY?: number,
+ endX?: number, endY?: number,
+ }
+ ): this;
+ startX: number;
+ startY: number;
+ endX: number;
+ endY: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.js
new file mode 100644
index 000000000..36c0f63e5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMove.js
@@ -0,0 +1,116 @@
+import EaseValueTaskBase from '../../utils/componentbase/tweentask/EaseValueTaskBase.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const Linear = Phaser.Math.Linear;
+
+class EaseMove extends EaseValueTaskBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.timer
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ super.resetFromJSON(o);
+
+ this.setMode(GetValue(o, 'mode', 0));
+
+ if (o && (o.hasOwnProperty('x') || o.hasOwnProperty('y'))) {
+ var endX = GetAdvancedValue(o, 'x', undefined);
+ var endY = GetAdvancedValue(o, 'y', undefined);
+ this.setTargetPosition(endX, endY);
+ } else {
+ this.setTargetPosition(o);
+ }
+
+ return this;
+ }
+
+ setMode(m) {
+ if (typeof (m) === 'string') {
+ m = MODE[m];
+ }
+ this.mode = m;
+ return this;
+ }
+
+ setTargetPosition(x, y) {
+ if ((typeof (x) === 'number') || (typeof (y) === 'number')) {
+ // endX, endY
+ // x,y : a number, or undefined
+ this.startX = this.parent.x;
+ this.startY = this.parent.y;
+ this.endX = x;
+ this.endY = y;
+ } else {
+ var config = x;
+ this.startX = GetAdvancedValue(config, 'startX', undefined);
+ this.startY = GetAdvancedValue(config, 'startY', undefined);
+ this.endX = GetAdvancedValue(config, 'endX', undefined);
+ this.endY = GetAdvancedValue(config, 'endY', undefined);
+ }
+
+ this.hasMoveX = (this.startX !== undefined) && (this.endX !== undefined);
+ this.hasMoveY = (this.startY !== undefined) && (this.endY !== undefined);
+ return this;
+ }
+
+ start() {
+ if (this.timer.isRunning) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (this.hasMoveX) {
+ gameObject.x = this.startX;
+ }
+ if (this.hasMoveY) {
+ gameObject.y = this.startY;
+ }
+
+ this.timer
+ .setDelay(this.delay)
+ .setDuration(this.duration)
+ .setRepeat((this.mode === 2) ? -1 : 0);
+
+ super.start();
+ return this;
+ }
+
+ updateGameObject(gameObject, timer) {
+ var t = timer.t;
+ if (timer.isOddIteration) { // Yoyo
+ t = 1 - t;
+ }
+ t = this.easeFn(t);
+
+ if (this.hasMoveX) {
+ gameObject.x = Linear(this.startX, this.endX, t);
+ }
+ if (this.hasMoveY) {
+ gameObject.y = Linear(this.startY, this.endY, t);
+ }
+ }
+
+ complete() {
+ super.complete();
+
+ if (this.mode === 1) {
+ this.parent.destroy();
+ // Will also destroy this behavior
+ }
+ return this;
+ }
+}
+
+const MODE = {
+ stop: 0,
+ destroy: 1,
+ yoyo: 2
+}
+
+export default EaseMove;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.d.ts
new file mode 100644
index 000000000..801400d93
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.d.ts
@@ -0,0 +1,22 @@
+import EaseMove from './EaseMove';
+
+declare function EaseMoveFrom(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ startX: number | string | undefined,
+ startY: number | string | undefined,
+ ease?: string,
+ destroyMode?: boolean,
+ easeMove?: EaseMove
+): EaseMove;
+
+declare function EaseMoveFrom(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ startX: number | string | undefined,
+ startY: number | string | undefined,
+ ease?: string,
+ easeMove?: EaseMove
+): EaseMove;
+
+export default EaseMoveFrom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.js
new file mode 100644
index 000000000..6ebe2439c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveFrom.js
@@ -0,0 +1,37 @@
+import EaseMove from './EaseMove.js';
+import ParseValue from './ParseValue.js';
+
+var EaseMoveFrom = function (gameObject, duration, startX, startY, ease, destroyMode, easeMove) {
+ if (destroyMode instanceof EaseMove) {
+ easeMove = destroyMode;
+ destroyMode = undefined;
+ }
+
+ if (destroyMode === undefined) {
+ destroyMode = false;
+ }
+
+ var config = {};
+ config.mode = (destroyMode) ? 1 : 0;
+ if (startX !== undefined) {
+ config.startX = ParseValue(startX, gameObject.x);
+ config.endX = gameObject.x;
+ }
+ if (startY !== undefined) {
+ config.startY = ParseValue(startY, gameObject.y);
+ config.endY = gameObject.y;
+ }
+ config.duration = duration;
+ config.ease = (ease === undefined) ? 'Linear' : ease;
+
+ if (easeMove === undefined) {
+ easeMove = new EaseMove(gameObject, config);
+ } else {
+ easeMove.resetFromJSON(config);
+ }
+ easeMove.restart();
+
+ return easeMove;
+}
+
+export default EaseMoveFrom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.d.ts
new file mode 100644
index 000000000..e36748437
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.d.ts
@@ -0,0 +1,22 @@
+import EaseMove from './EaseMove';
+
+declare function EaseMoveTo(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ endX: number | string | undefined,
+ endY: number | string | undefined,
+ ease?: string,
+ destroyMode?: boolean,
+ easeMove?: EaseMove
+): EaseMove;
+
+declare function EaseMoveTo(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ endX: number | string | undefined,
+ endY: number | string | undefined,
+ ease?: string,
+ easeMove?: EaseMove
+): EaseMove;
+
+export default EaseMoveTo;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.js
new file mode 100644
index 000000000..29622dad8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/EaseMoveTo.js
@@ -0,0 +1,37 @@
+import EaseMove from './EaseMove.js';
+import ParseValue from './ParseValue.js';
+
+var EaseMoveTo = function (gameObject, duration, endX, endY, ease, destroyMode, easeMove) {
+ if (destroyMode instanceof EaseMove) {
+ easeMove = destroyMode;
+ destroyMode = undefined;
+ }
+
+ if (destroyMode === undefined) {
+ destroyMode = false;
+ }
+
+ var config = {};
+ config.mode = (destroyMode) ? 1 : 0;
+ if (endX !== undefined) {
+ config.startX = gameObject.x;
+ config.endX = ParseValue(endX, gameObject.x);
+ }
+ if (endY !== undefined) {
+ config.startY = gameObject.y;
+ config.endY = ParseValue(endY, gameObject.y);
+ }
+ config.duration = duration;
+ config.ease = (ease === undefined) ? 'Linear' : ease;
+
+ if (easeMove === undefined) {
+ easeMove = new EaseMove(gameObject, config);
+ } else {
+ easeMove.resetFromJSON(config);
+ }
+ easeMove.restart();
+
+ return easeMove;
+};
+
+export default EaseMoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/ParseValue.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/ParseValue.js
new file mode 100644
index 000000000..afd7f7491
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/easemove/ParseValue.js
@@ -0,0 +1,17 @@
+var ParseValue = function (propertyValue, startValue) {
+ // propertyValue : number or string
+ if (typeof (propertyValue) === 'number') {
+ return propertyValue;
+ } else {
+ var op = propertyValue[0];
+ var num = parseFloat(propertyValue.substr(2));
+ switch (op) {
+ case '+': return startValue + num;
+ case '-': return startValue - num;
+ case '*': return startValue * num;
+ case '/': return startValue / num;
+ }
+ }
+}
+
+export default ParseValue;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.d.ts
new file mode 100644
index 000000000..24c8c89f0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.d.ts
@@ -0,0 +1,60 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default EightDirection;
+
+declare namespace EightDirection {
+
+ type DirectionModeType = 0 | 1 | 2 | 3 | 'up&down' | 'left&right' | '4dir' | '8dir';
+ type CursorKeys = {
+ up: Phaser.Input.Keyboard.Key,
+ down: Phaser.Input.Keyboard.Key,
+ left: Phaser.Input.Keyboard.Key,
+ right: Phaser.Input.Keyboard.Key
+ }
+
+ interface IConfig {
+ speed?: number,
+ dir?: DirectionModeType,
+ rotateToDirection?: boolean,
+ enable?: boolean,
+ wrap?: boolean,
+ padding?: number,
+ cursorKeys?: CursorKeys
+ }
+}
+
+declare class EightDirection extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: EightDirection.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setCursorKeys(
+ cursorKeys: EightDirection.CursorKeys
+ ): this;
+ cursorKeys: EightDirection.CursorKeys;
+
+ setSpeed(speed: number): this;
+ speed: number;
+
+ setRotateToTarget(enable?: boolean): this;
+ rotateToTarget: boolean;
+
+ setDirMode(dir: EightDirection.DirectionModeType): this;
+ dirMode: number;
+
+ setWrapMode(
+ wrap?: boolean,
+ padding?: number
+ ): this;
+ wrap: boolean;
+ padding: number;
+
+ readonly isLeft: boolean;
+ readonly isRight: boolean;
+ readonly isUp: boolean;
+ readonly isDown: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.js
new file mode 100644
index 000000000..56085779a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/eightdirection/EightDirection.js
@@ -0,0 +1,172 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import {
+ SetVelocity
+} from '../../utils/arcade/Helpers.js';
+import DegToRad from '../../utils/math/DegToRad.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class EightDirection extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ if (!this.parent.body) {
+ this.scene.physics.add.existing(this.parent, false);
+ }
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setDirMode(GetValue(o, 'dir', '8dir'));
+ this.setSpeed(GetValue(o, 'speed', 200));
+ this.setRotateToDirection(GetValue(o, 'rotateToDirection', false));
+ this.setWrapMode(GetValue(o, 'wrap', false), GetValue(o, 'padding', 0));
+ this.setCursorKeys(GetValue(o, 'cursorKeys', undefined));
+ return this;
+ }
+
+ get enable() {
+ return this.isRunning;
+ }
+
+ set enable(value) {
+ this.isRunning = value;
+ if (!value) {
+ SetVelocity(this, 0, 0);
+ }
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ if (e && (this.body === undefined)) {
+ this.scene.physics.add.existing(this.parent, false);
+ }
+ return this;
+ }
+
+ setDirMode(m) {
+ if (typeof (m) === 'string') {
+ m = DIRMODE[m];
+ }
+ this.dirMode = m;
+ return this;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ setRotateToDirection(rotateToDirection) {
+ this.rotateToDirection = rotateToDirection;
+ return this;
+ }
+
+ setWrapMode(wrap, padding) {
+ if (wrap === undefined) {
+ wrap = true;
+ }
+ this.wrap = wrap;
+ this.padding = padding;
+ return this;
+ }
+
+ setCursorKeys(cursorKeys) {
+ if (cursorKeys === undefined) {
+ cursorKeys = this.scene.input.keyboard.createCursorKeys();
+ }
+ this.cursorKeys = cursorKeys;
+ return this;
+ }
+
+ get isLeft() {
+ return (this.enable) ? this.cursorKeys.left.isDown : false;
+ }
+
+ get isRight() {
+ return (this.enable) ? this.cursorKeys.right.isDown : false;
+ }
+
+ get isUp() {
+ return (this.enable) ? this.cursorKeys.up.isDown : false;
+ }
+
+ get isDown() {
+ return (this.enable) ? this.cursorKeys.down.isDown : false;
+ }
+
+ update(time, delta) {
+ var gameObject = this.parent;
+ if (!this.enable) {
+ SetVelocity(gameObject, 0, 0);
+ return this;
+ }
+
+ if (!gameObject.active) {
+ return this;
+ }
+
+ var dy = ((this.isUp) ? -1 : 0) + ((this.isDown) ? 1 : 0),
+ dx = ((this.isLeft) ? -1 : 0) + ((this.isRight) ? 1 : 0);
+ if ((dx === 0) && (dy === 0)) {
+ SetVelocity(gameObject, 0, 0);
+ return this;
+ }
+ switch (this.dirMode) {
+ case 0: // up&down
+ dx = 0;
+ break;
+ case 1: // left&right
+ dy = 0;
+ break;
+ case 2: // 4dir
+ if (dy !== 0) {
+ dx = 0;
+ }
+ break;
+ }
+
+ var rotation, vx, vy;
+ if (dy === 0) { // dx !== 0
+ vx = this.speed * dx;
+ vy = 0;
+ rotation = (dx === 1) ? RAD0 : RAD180;
+ } else if (dx === 0) { // dy !== 0
+ vx = 0;
+ vy = this.speed * dy;
+ rotation = (dy === 1) ? RAD90 : RAD270;
+ } else { // (dx !== 0) && (dy !== 0)
+ rotation = Math.atan2(dy, dx);
+ vx = this.speed * Math.cos(rotation);
+ vy = this.speed * Math.sin(rotation);
+ }
+ SetVelocity(gameObject, vx, vy);
+ if (this.rotateToDirection && (rotation !== undefined)) {
+ gameObject.rotation = rotation;
+ }
+
+ if (this.wrap) {
+ gameObject.body.world.wrap(gameObject, this.padding);
+ }
+ return this;
+ }
+}
+
+const DIRMODE = {
+ 'up&down': 0,
+ 'left&right': 1,
+ '4dir': 2,
+ '8dir': 3
+};
+const RAD0 = DegToRad(0);
+const RAD90 = DegToRad(90);
+const RAD180 = DegToRad(180);
+const RAD270 = DegToRad(270);
+
+export default EightDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.d.ts
new file mode 100644
index 000000000..b61b06fb3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.d.ts
@@ -0,0 +1,36 @@
+import EaseValueTaskBase from "../../utils/componentbase/tweentask/EaseValueTaskBase";
+
+export default Fade;
+
+declare namespace Fade {
+ type ModeType = 0 | 1 | 2 | 'stop' | 'destroy' | 'yoyo';
+
+ interface IConfig {
+ mode?: ModeType,
+ start?: number,
+ end?: number,
+ duration?: number,
+ delay?: number
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ fade: Fade
+ ) => void;
+ }
+}
+
+declare class Fade extends EaseValueTaskBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Fade.IConfig
+ )
+
+ setMode(mode: Fade.ModeType): this;
+ mode: number;
+
+ setAlphaRange(start: number, end: number): this;
+ alphaStart: number;
+ alphaEnd: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.js
new file mode 100644
index 000000000..27972541f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/fade/Fade.js
@@ -0,0 +1,85 @@
+import EaseValueTaskBase from '../../utils/componentbase/tweentask/EaseValueTaskBase.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const Linear = Phaser.Math.Linear;
+
+class Fade extends EaseValueTaskBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.timer
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ super.resetFromJSON(o);
+
+ this.setMode(GetValue(o, 'mode', 0));
+ this.setAlphaRange(
+ GetAdvancedValue(o, 'start', this.parent.alpha),
+ GetAdvancedValue(o, 'end', 0)
+ );
+ return this;
+ }
+
+ setMode(m) {
+ if (typeof (m) === 'string') {
+ m = MODE[m];
+ }
+ this.mode = m;
+ return this;
+ }
+
+ setAlphaRange(start, end) {
+ this.alphaStart = start;
+ this.alphaEnd = end;
+ return this;
+ }
+
+ start() {
+ if (this.timer.isRunning) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ gameObject.setAlpha(this.alphaStart);
+
+ this.timer
+ .setDelay(this.delay)
+ .setDuration(this.duration)
+ .setRepeat((this.mode === 2) ? -1 : 0);
+
+ super.start();
+ return this;
+ }
+
+ updateGameObject(gameObject, timer) {
+ var t = timer.t;
+ if (timer.isOddIteration) { // Yoyo
+ t = 1 - t;
+ }
+
+ gameObject.alpha = Linear(this.alphaStart, this.alphaEnd, t);
+ }
+
+ complete() {
+ super.complete();
+ if (this.mode === 1) {
+ this.parent.destroy();
+ // Will also destroy this behavior
+ }
+ return this;
+ }
+
+}
+
+const MODE = {
+ stop: 0,
+ destroy: 1,
+ yoyo: 2
+}
+
+export default Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/CreateFileInput.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/CreateFileInput.js
new file mode 100644
index 000000000..3c4e35416
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/CreateFileInput.js
@@ -0,0 +1,20 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var CreateFileInput = function (config) {
+ var fileInput = document.createElement('input');
+ fileInput.type = 'file';
+
+ var accept = GetValue(config, 'accept', '');
+ var multiple = GetValue(config, 'multiple', false);
+
+ fileInput.setAttribute('accept', accept);
+ if (multiple) {
+ fileInput.setAttribute('multiple', '');
+ } else {
+ fileInput.removeAttribute('multiple');
+ }
+
+ return fileInput;
+}
+
+export default CreateFileInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.d.ts
new file mode 100644
index 000000000..7bc43551f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.d.ts
@@ -0,0 +1,19 @@
+export default Open;
+
+declare namespace Open {
+
+ interface IConfig {
+ accept?: string,
+ multiple?: boolean
+ closeDelay?: number
+ }
+
+ interface IResult {
+ files: File[]
+ }
+}
+
+declare function Open(
+ game: Phaser.Game | Phaser.Scene | Phaser.GameObjects.GameObject,
+ config?: Open.IConfig
+): Promise;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.js
new file mode 100644
index 000000000..1e7e0e776
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/filechooser/Open.js
@@ -0,0 +1,20 @@
+// Note: Not working in iOS9+
+
+import CreateFileInput from './CreateFileInput.js';
+import ClickPromise from '../../gameobjects/dom/filechooser/ClickPromise.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var Open = function (game, config) {
+ // game: game, scene, or game object
+ var closeDelay = GetValue(config, 'closeDelay', 200);
+ var fileInput = CreateFileInput(config);
+ fileInput.click();
+ return ClickPromise({ game, fileInput, closeDelay })
+ .then(function (result) {
+ fileInput.remove();
+ return Promise.resolve(result);
+ })
+}
+
+export default Open;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.d.ts
new file mode 100644
index 000000000..39c8de0ea
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.d.ts
@@ -0,0 +1,40 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Flash;
+
+declare namespace Flash {
+
+ interface IConfig {
+ duration?: number,
+ repeat?: number,
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ flash: Flash
+ ) => void;
+ }
+}
+
+declare class Flash extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Flash.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ flash(duration?: number, repeat?: number): this;
+ flash(config: {
+ duration?: number,
+ repeat?: number,
+ }): this;
+
+ setDuration(duration: number): this;
+ duration: number;
+
+ setRepeat(repeat: number): this;
+ repeat: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.js
new file mode 100644
index 000000000..d01cfb5c5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/flash/Flash.js
@@ -0,0 +1,109 @@
+import TickTask from '../../utils/componentbase/timerticktask/TimerTask';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Flash extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.timer
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.timer.resetFromJSON(GetValue(o, 'timer'));
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setDuration(GetValue(o, 'duration', 500));
+ this.setRepeat(GetValue(o, 'repeat', 2));
+ return this;
+ }
+
+ toJSON() {
+ return {
+ timer: this.timer.toJSON(),
+ enable: this.enable,
+ isRunning: this.isRunning,
+ duration: this.duration,
+ repeat: this.repeat,
+ };
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ setDuration(duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ setRepeat(repeat) {
+ this.repeat = repeat;
+ return this;
+ }
+
+ start(duration, repeat) {
+ if (typeof (duration) !== 'number') {
+ var config = duration;
+ duration = GetValue(config, 'duration', undefined);
+ repeat = GetValue(config, 'repeat', undefined);
+ }
+ if (duration !== undefined) {
+ this.setDuration(duration);
+ }
+ if (repeat !== undefined) {
+ this.setRepeat(repeat);
+ }
+
+ this.timer
+ .setDuration(this.duration)
+ .setRepeat(this.repeat);
+
+ if (this.isRunning) {
+ // pend task
+ this.timer.repeatCounter = -1;
+ } else {
+ super.start();
+ }
+ return this;
+ }
+
+ flash(duration, repeat) {
+ this.start(duration, repeat);
+ return this;
+ }
+
+ stop() {
+ this.parent.setVisible(true);
+ super.stop();
+ return this;
+ }
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ this.timer.update(time, delta);
+ gameObject.setVisible((this.timer.t > 0.5));
+
+ if (this.timer.isDone) {
+ this.complete();
+ }
+ return this;
+ }
+}
+
+export default Flash;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.d.ts
new file mode 100644
index 000000000..0ec7d3332
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.d.ts
@@ -0,0 +1,62 @@
+// import * as Phaser from 'phaser';
+import EaseValueTaskBase from "../../utils/componentbase/tweentask/EaseValueTaskBase";
+
+export default Flip;
+
+declare namespace Flip {
+
+ type FaceTypes = 0 | 1 | 'front' | 'back';
+
+ type FaceDefTypes = string |
+ { key?: string, frame?: string } |
+ ((gameObject: Phaser.GameObjects.GameObject) => void);
+
+ type OrientationTypes = 0 | 1 | 'x' | 'y' | 'horizontal' | 'vertical';
+
+ interface IConfig {
+ face?: FaceTypes,
+ front?: FaceDefTypes,
+ back?: FaceDefTypes,
+
+ orientation?: OrientationTypes,
+ duration?: number,
+ delay?: number,
+ ease?: string,
+
+ eventEmitter?: boolean | Phaser.Events.EventEmitter
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ flip: Flip
+ ) => void;
+ }
+
+}
+
+declare class Flip extends EaseValueTaskBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Flip.IConfig
+ )
+
+ flip(duration?: number): this;
+
+ setFace(
+ face: 0 | 1 | 'front' | 'back'
+ ): this;
+ toggleFace(): this;
+ face: number;
+
+ setFrontFace(
+ key: string |
+ ((gameObject: Phaser.GameObjects.GameObject) => void),
+ frame?: string
+ ): this;
+ setBackFace(
+ key: string |
+ ((gameObject: Phaser.GameObjects.GameObject) => void),
+ frame?: string
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.js
new file mode 100644
index 000000000..10e30e749
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/Flip.js
@@ -0,0 +1,139 @@
+import EaseValueTaskBase from '../../utils/componentbase/tweentask/EaseValueTaskBase.js';
+import GetFaceUpdatingCallback from './GetFaceUpdatingCallback.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const Linear = Phaser.Math.Linear;
+
+class Flip extends EaseValueTaskBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.timer
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ super.resetFromJSON(o);
+ this.setDuration(GetAdvancedValue(o, 'duration', 500));
+ this.setEase(GetValue(o, 'ease', 'Sine'));
+
+ this.setOrientation(GetValue(o, 'orientation', 0));
+ this.setFrontFace(GetValue(o, 'front', undefined));
+ this.setBackFace(GetValue(o, 'back', undefined));
+ this.setFace(GetValue(o, 'face', 0));
+ return this;
+ }
+
+ setOrientation(orientation) {
+ if (typeof (orientation) === 'string') {
+ orientation = ORIENTATIONMODE[orientation];
+ }
+ this.orientation = orientation;
+ return this;
+ }
+
+ get face() {
+ return this._face;
+ }
+
+ set face(face) {
+ if (typeof (face) === 'string') {
+ face = FACEMODE[face];
+ }
+ this._face = face;
+ if ((face === 0) && this.frontFaceCallback) {
+ this.frontFaceCallback(this.parent);
+ } else if ((face === 1) && this.backFaceCallback) {
+ this.backFaceCallback(this.parent);
+ }
+ }
+
+ setFace(face) {
+ this.face = face;
+ return this;
+ }
+
+ toggleFace() {
+ var newFace = (this.face === 0) ? 1 : 0;
+ this.setFace(newFace);
+ return this;
+ }
+
+ setFrontFace(key, frame) {
+ this.frontFaceCallback = GetFaceUpdatingCallback(key, frame, this.parent);
+ return this;
+ }
+
+ setBackFace(key, frame) {
+ this.backFaceCallback = GetFaceUpdatingCallback(key, frame, this.parent);
+ return this;
+ }
+
+ start() {
+ if (this.timer.isRunning) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (this.orientation === 0) {
+ this.scale0 = gameObject.scaleX;
+ } else {
+ this.scale0 = gameObject.scaleY;
+ }
+
+ this.timer
+ .setDelay(this.delay)
+ .setDuration(this.duration / 2)
+ .setRepeat(1); // 2 times
+
+ super.start();
+ return this;
+ }
+
+ flip(duration) {
+ if (this.isRunning) {
+ return this;
+ }
+ if (duration !== undefined) {
+ this.setDuration(duration);
+ }
+ this.start();
+ return this;
+ }
+
+ updateGameObject(gameObject, timer) {
+ if (timer.justRestart) {
+ this.toggleFace();
+ }
+
+ var t = timer.t;
+ if (timer.isOddIteration) { // Yoyo
+ t = 1 - t;
+ }
+ t = this.easeFn(t);
+
+ var value = Linear(this.scale0, 0, t);
+ if (this.orientation === 0) {
+ gameObject.scaleX = value;
+ } else {
+ gameObject.scaleY = value;
+ }
+ }
+}
+
+const ORIENTATIONMODE = {
+ x: 0,
+ horizontal: 0,
+ y: 1,
+ vertical: 1,
+}
+
+const FACEMODE = {
+ front: 0,
+ back: 1,
+}
+
+export default Flip;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/GetFaceUpdatingCallback.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/GetFaceUpdatingCallback.js
new file mode 100644
index 000000000..fefe2ee67
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/flip/GetFaceUpdatingCallback.js
@@ -0,0 +1,26 @@
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var GetFrameUpdatingCallback = function (key, frame, gameObject) {
+ var callback;
+ if (key === undefined) {
+ key = gameObject.texture.key;
+ frame = gameObject.frame.name;
+ } else if (IsPlainObject(key)) {
+ var config = key;
+ key = GetValue(config, 'key', gameObject.texture.key);
+ frame = GetValue(config, 'frame', gameObject.frame.name);
+ } else if (typeof (key) === 'string') {
+ } else {
+ callback = key;
+ }
+
+ if (callback === undefined) {
+ callback = function (gameObject) {
+ gameObject.setTexture(key, frame);
+ }
+ }
+ return callback;
+}
+
+export default GetFrameUpdatingCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.d.ts
new file mode 100644
index 000000000..13e730f79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.d.ts
@@ -0,0 +1,31 @@
+import HiddenTextEditBase from './HiddenTextEditBase';
+
+export default HiddenTextEdit;
+
+declare namespace HiddenTextEdit {
+ interface IConfig extends HiddenTextEditBase.IConfig {
+ cursor?: string;
+ cursorFlashDuration?: number;
+ }
+
+ type UpdateTextCallbackType = (
+ newText: string,
+ hiddenInputText: HiddenTextEdit,
+ ) => string;
+}
+
+declare class HiddenTextEdit extends HiddenTextEditBase {
+ constructor(
+ textObject: Phaser.GameObjects.GameObject,
+ config?: HiddenTextEdit.IConfig
+ );
+
+ setCursor(
+ s: string
+ ): this;
+ readonly cursor: string;
+
+ setCursorFlashDuration(
+ duration: number
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.js
new file mode 100644
index 000000000..3cdad8e9b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEdit.js
@@ -0,0 +1,97 @@
+import HiddenTextEditBase from './HiddenTextEditBase.js';
+import NumberInputUpdateCallback from './defaultcallbacks/NumberInputUpdateCallback.js';
+import GetTickDelta from '../../utils/system/GetTickDelta.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Wrap = Phaser.Math.Wrap;
+
+class HiddenTextEdit extends HiddenTextEditBase {
+ constructor(gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ if (config.onUpdate === 'number') {
+ config.onUpdate = NumberInputUpdateCallback;
+ }
+
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.setCursor(GetValue(config, 'cursor', '|'));
+ this.setCursorFlashDuration(GetValue(config, 'cursorFlashDuration', 1000));
+ this.cursorFlashTimer = 0;
+
+ }
+
+ initText() {
+ this.cursorFlashTimer = 0;
+ this.prevCursorPosition = undefined;
+ this.setText(this.parent.text);
+ this.setCursorPosition();
+
+ return this;
+ }
+
+ updateText() {
+ var textObject = this.parent;
+ var text = this.text;
+
+ if (this.onUpdateCallback) {
+ var newText = this.onUpdateCallback(text, textObject, this);
+ if (newText != null) {
+ text = newText;
+ }
+ }
+
+ if (this.isOpened && this.hasCursor) {
+ // Insert Cursor
+ var cursorPosition = this.cursorPosition;
+ text = text.substring(0, cursorPosition) + this.cursor + text.substring(cursorPosition);
+
+ if (this.prevCursorPosition !== cursorPosition) {
+ // console.log(cursorPosition);
+ this.prevCursorPosition = cursorPosition;
+ }
+ }
+
+ if (textObject.text !== text) {
+ textObject.setText(text);
+ this.emit('textchange', text, textObject, this);
+ }
+
+ return this;
+ }
+
+ setCursor(s) {
+ this._cursor = s;
+ this.hasCursor = s && (s !== '');
+ return s;
+ }
+
+ setCursorFlashDuration(duration) {
+ this.cursorFlashDuration = duration;
+ return this;
+ }
+
+ get cursor() {
+ if (!this._isFocused) {
+ return this._cursor;
+ }
+
+ // Flash Cursor
+ var cursor;
+ if (this.cursorFlashTimer < (this.cursorFlashDuration / 2)) {
+ cursor = this._cursor;
+ } else {
+ cursor = ' ';
+ }
+
+ var timerValue = this.cursorFlashTimer + GetTickDelta(this.scene);
+ this.cursorFlashTimer = Wrap(timerValue, 0, this.cursorFlashDuration);
+ return cursor;
+ }
+
+}
+
+export default HiddenTextEdit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.d.ts
new file mode 100644
index 000000000..467e4810e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.d.ts
@@ -0,0 +1,123 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default HiddenTextEditBase;
+
+declare namespace HiddenTextEditBase {
+ type OnOpenCallbackType = (
+ textObject: Phaser.GameObjects.GameObject,
+ hiddenInputText: HiddenTextEditBase,
+ ) => void;
+
+ type OnCloseCallbackType = (
+ textObject: Phaser.GameObjects.GameObject,
+ hiddenInputText: HiddenTextEditBase,
+ ) => void;
+
+ type OnUpdateCallbackType = (
+ text: string,
+ textObject: Phaser.GameObjects.GameObject,
+ hiddenInputText: HiddenTextEditBase,
+ ) => void | string;
+
+
+ interface IConfig {
+ enterClose?: boolean;
+
+ onOpen: OnOpenCallbackType;
+
+ onClose: OnCloseCallbackType;
+
+ onUpdate: OnUpdateCallbackType | 'number';
+
+ // Copy from InputText
+ type?: string,
+
+ // Element properties
+ id?: string,
+ text?: string,
+ maxLength?: number,
+ minLength?: number,
+ placeholder?: string,
+ tooltip?: string,
+ readOnly?: boolean,
+ spellCheck?: boolean,
+ autoComplete?: 'on' | 'off',
+
+ // Style properties
+ align?: string,
+ paddingLeft?: string,
+ paddingRight?: string,
+ paddingTop?: string,
+ paddingBottom?: string,
+ fontFamily?: string,
+ fontSize?: string,
+ color?: string,
+ border?: number,
+ backgroundColor?: string,
+ borderColor?: string,
+ outline?: string,
+
+ selectAll?: boolean,
+ }
+
+ type UpdateTextCallbackType = (
+ newText: string,
+ hiddenInputText: HiddenTextEditBase,
+ ) => string;
+}
+
+declare class HiddenTextEditBase extends ComponentBase {
+ constructor(
+ textObject: Phaser.GameObjects.GameObject,
+ config?: HiddenTextEditBase.IConfig
+ );
+
+ setEnterClose(
+ value?: boolean
+ ): this;
+
+ open(): this;
+ close(): this;
+ readonly isOpened: boolean;
+
+ // Copy from InputText
+ setText(text: string): this;
+ text: string;
+
+ selectText(
+ selectionStart?: number,
+ selectionEnd?: number
+ ): this;
+ selectAll(): this;
+ readonly selectionStart: number;
+ readonly selectionEnd: number;
+ readonly selectedText: string;
+
+ setCursorPosition(value: number): this;
+ cursorPosition: number;
+
+ scrollToBottom(): this;
+
+ getStyle(key: string): string;
+
+ setStyle(key: string, value?: number | string): this;
+
+ setFocus(): this;
+ setBlur(): this;
+ readonly isFocused: boolean;
+
+ setFontColor(color: string): this;
+ fontColor: string;
+
+ setMaxLength(value: number): this;
+ maxLength: number;
+
+ setMinLength(value: number): this;
+ minLength: number;
+
+ setPlaceholder(value: string): this;
+ placeholder: string;
+
+ setTooltip(value: string): this;
+ tooltip: string;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.js
new file mode 100644
index 000000000..5aeb06ea5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/HiddenTextEditBase.js
@@ -0,0 +1,353 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import CopyElementConfig from './methods/CopyElementConfig.js';
+import IsPointerInHitArea from '../../utils/input/IsPointerInHitArea.js';
+import Methods from './methods/Methods.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class HiddenTextEditBase extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject);
+ // this.parent = gameObject;
+
+ var textType = GetValue(config, 'inputType', undefined);
+ if (textType === undefined) {
+ textType = GetValue(config, 'type', 'text');
+ }
+
+ this.setEnterCloseEnable(GetValue(config, 'enterClose', (textType !== 'textarea')));
+
+ var onOpen = GetValue(config, 'onOpen', undefined);
+ if (!onOpen) {
+ onOpen = GetValue(config, 'onFocus', undefined);
+ }
+ this.onOpenCallback = onOpen;
+
+ var onClose = GetValue(config, 'onClose', undefined);
+ if (!onClose) {
+ onClose = GetValue(config, 'onBlur', undefined);
+ }
+ this.onCloseCallback = onClose;
+
+ this.onUpdateCallback = GetValue(config, 'onUpdate', undefined);
+
+ this.isOpened = false;
+
+ gameObject
+ .on('pointerdown', function () {
+ this.open();
+ }, this)
+ .setInteractive()
+
+ this.nodeConfig = CopyElementConfig(config);
+ // Create/remove input text element when opening/closing editor
+ this.node = undefined;
+ }
+
+ destroy() {
+ // this.parent.off('pointerdown', this.open, this);
+
+ this.close();
+
+ super.destroy();
+ }
+
+ onClickOutside(pointer) {
+ if (!IsPointerInHitArea(this.parent, pointer)) {
+ this.close();
+ }
+ }
+
+ setEnterCloseEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.enterCloseEnable = enable;
+ return this;
+ }
+
+ // Override
+ initText() {
+ }
+
+ // Override
+ updateText() {
+ }
+
+ // Copy from InputText class
+ get text() {
+ if (!this.node) {
+ return '';
+ }
+ return this.node.value;
+ }
+
+ set text(value) {
+ if (!this.node) {
+ return;
+ }
+ this.node.value = value;
+ }
+
+ setText(value) { // Override
+ this.text = value;
+ return this;
+ }
+
+ get maxLength() {
+ return this.nodeConfig.maxLength;
+ }
+
+ set maxLength(value) {
+ this.nodeConfig.maxLength = value;
+
+ if (this.node) {
+ this.node.maxLength = value;
+ }
+ }
+
+ setMaxLength(value) {
+ this.maxLength = value;
+ return this;
+ }
+
+ get minLength() {
+ return this.nodeConfig.minLength;
+ }
+
+ set minLength(value) {
+ this.nodeConfig.minLength = value;
+
+ if (this.node) {
+ this.node.minLength = value;
+ }
+ }
+
+ setMinLength(value) {
+ this.minLength = value;
+ return this;
+ }
+
+ get placeholder() {
+ return this.node.placeholder;
+ }
+
+ set placeholder(value) {
+ if (!this.node) {
+ return;
+ }
+ this.node.placeholder = value;
+ }
+
+ setPlaceholder(value) {
+ this.placeholder = value;
+ return this;
+ }
+
+ selectText(selectionStart, selectionEnd) {
+ if (!this.node) {
+ return this;
+ }
+ if (selectionStart === undefined) {
+ this.node.select();
+ } else {
+ this.node.setSelectionRange(selectionStart, selectionEnd);
+ }
+ return this;
+ }
+
+ selectAll() {
+ this.selectText();
+ return this;
+ }
+
+ get selectionStart() {
+ if (!this.node) {
+ return 0;
+ }
+ return this.node.selectionStart;
+ }
+
+ get selectionEnd() {
+ if (!this.node) {
+ return 0;
+ }
+ return this.node.selectionEnd;
+ }
+
+ get selectedText() {
+ if (!this.node) {
+ return '';
+ }
+ var node = this.node;
+ return node.value.substring(node.selectionStart, node.selectionEnd);
+ }
+
+ get cursorPosition() {
+ if (!this.node) {
+ return 0;
+ }
+ return this.node.selectionStart;
+ }
+
+ set cursorPosition(value) {
+ if (!this.node) {
+ return;
+ }
+ this.node.setSelectionRange(value, value);
+ }
+
+ setCursorPosition(value) {
+ if (value === undefined) {
+ value = this.text.length;
+ } else if (value < 0) {
+ value = this.text.length + value;
+ }
+
+ this.cursorPosition = value;
+ return this;
+ }
+
+ get tooltip() {
+ if (!this.node) {
+ return '';
+ }
+ return this.node.title;
+ }
+
+ set tooltip(value) {
+ if (!this.node) {
+ return this;
+ }
+ this.node.title = value;
+ }
+
+ setTooltip(value) {
+ this.tooltip = value;
+ return this;
+ }
+
+ setTextChangedCallback(callback) {
+ this.onTextChanged = callback;
+ return this;
+ }
+
+ get readOnly() {
+ return this.nodeConfig.readOnly;
+ }
+
+ set readOnly(value) {
+ this.nodeConfig.readOnly = value;
+
+ if (this.node) {
+ this.node.readOnly = value;
+ }
+ }
+
+ setReadOnly(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.readOnly = value;
+ return this;
+ }
+
+ get spellCheck() {
+ if (!this.node) {
+ return '';
+ }
+ return this.node.spellcheck;
+ }
+
+ set spellCheck(value) {
+ if (!this.node) {
+ return;
+ }
+ this.node.spellcheck = value;
+ }
+
+ setSpellCheck(value) {
+ this.spellCheck = value;
+ return this;
+ }
+
+ get fontColor() {
+ if (!this.node) {
+ return undefined;
+ }
+ return this.node.style.color;
+ }
+
+ set fontColor(value) {
+ if (!this.node) {
+ return;
+ }
+ this.node.style.color = value;
+ }
+
+ setFontColor(value) {
+ this.fontColor = value;
+ return this;
+ }
+
+ setStyle(key, value) {
+ if (!this.node) {
+ return this;
+ }
+ this.node.style[key] = value;
+ return this;
+ }
+
+ getStyle(key) {
+ if (!this.node) {
+ return undefined;
+ }
+ return this.node.style[key];
+ }
+
+ scrollToBottom() {
+ if (!this.node) {
+ return this;
+ }
+ this.node.scrollTop = this.node.scrollHeight;
+ return this;
+ }
+
+ setEnabled(enabled) {
+ if (!this.node) {
+ return this;
+ }
+ if (enabled === undefined) {
+ enabled = true;
+ }
+ this.node.disabled = !enabled;
+ return this;
+ }
+
+ setBlur() {
+ if (!this.node) {
+ return this;
+ }
+ this.node.blur();
+ return this;
+ }
+
+ setFocus() {
+ if (!this.node) {
+ return this;
+ }
+ this.node.focus();
+ return this;
+ }
+
+ get isFocused() {
+ return this.isOpened;
+ }
+}
+
+Object.assign(
+ HiddenTextEditBase.prototype,
+ Methods,
+)
+
+export default HiddenTextEditBase;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/defaultcallbacks/NumberInputUpdateCallback.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/defaultcallbacks/NumberInputUpdateCallback.js
new file mode 100644
index 000000000..a626b8288
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/defaultcallbacks/NumberInputUpdateCallback.js
@@ -0,0 +1,23 @@
+var NumberInputUpdateCallback = function (text, textObject, hiddenInputText) {
+ text = text.replace(' ', '');
+ var previousText = hiddenInputText.previousText;
+ if (text === previousText) {
+ return text;
+ }
+
+ if (isNaN(text)) {
+ // Enter a NaN character, back to previous text
+ hiddenInputText.emit('nan', text, hiddenInputText);
+
+ text = previousText;
+ var cursorPosition = hiddenInputText.cursorPosition - 1;
+ hiddenInputText.setText(text);
+ hiddenInputText.setCursorPosition(cursorPosition);
+ } else {
+ // New number text, update previous texr
+ hiddenInputText.previousText = text;
+ }
+
+ return text;
+}
+export default NumberInputUpdateCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Close.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Close.js
new file mode 100644
index 000000000..82fe9a001
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Close.js
@@ -0,0 +1,35 @@
+import { CloseLastOpenEditor } from './LastOpenedEditor.js';
+import RemoveElement from './RemoveElement.js';
+
+var Close = function () {
+ // Already closed
+ if (!this.isOpened) {
+ return this;
+ }
+
+ CloseLastOpenEditor(this);
+
+ this.setBlur();
+
+ this.isOpened = false;
+
+ this.updateText();
+
+ this.scene.sys.events.off('postupdate', this.updateText, this);
+
+ this.scene.input.off('pointerdown', this.onClickOutside, this);
+
+ if (this.onCloseCallback) {
+ this.onCloseCallback(this.parent, this);
+ }
+
+ // Remove input text element when closing editor
+ RemoveElement(this.node);
+ this.node = undefined;
+
+ this.emit('close', this);
+
+ return this;
+}
+
+export default Close;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CopyElementConfig.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CopyElementConfig.js
new file mode 100644
index 000000000..e50c6d15e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CopyElementConfig.js
@@ -0,0 +1,22 @@
+import {
+ ElementProperties,
+ StyleProperties,
+} from './InputTextProperties.js';
+import CopyProperty from '../../../utils/object/CopyProperty.js';
+
+var CopyElementConfig = function (from) {
+ if (from === undefined) {
+ from = {};
+ }
+ var to = {};
+
+ CopyProperty(from, to, 'inputType');
+ CopyProperty(from, to, 'type');
+ CopyProperty(from, to, 'style');
+ CopyProperty(from, to, StyleProperties);
+ CopyProperty(from, to, ElementProperties);
+
+ return to;
+}
+
+export default CopyElementConfig;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CreateElement.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CreateElement.js
new file mode 100644
index 000000000..54717b4bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/CreateElement.js
@@ -0,0 +1,49 @@
+import {
+ ElementProperties,
+ StyleProperties,
+} from './InputTextProperties.js';
+import SetPrpoerties from '../../../gameobjects/dom/utils/SetProperties.js';
+import StopPropagationTouchEvents from '../../../gameobjects/dom/utils/StopPropagationTouchEvents.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var CreateElement = function (parent, config) {
+ var element;
+ var textType = GetValue(config, 'inputType', undefined);
+ if (textType === undefined) {
+ textType = GetValue(config, 'type', 'text');
+ }
+ if (textType === 'textarea') {
+ element = document.createElement('textarea');
+ element.style.resize = 'none';
+ } else {
+ element = document.createElement('input');
+ element.type = textType;
+ }
+
+ var style = GetValue(config, 'style', undefined);
+ // Apply other style properties
+ var elementStyle = element.style;
+ SetPrpoerties(StyleProperties, style, elementStyle);
+ // Set style
+ elementStyle.position = 'absolute';
+ elementStyle.opacity = 0;
+ elementStyle.pointerEvents = 'none';
+ elementStyle.zIndex = 0;
+ // hide native blue text cursor on iOS
+ elementStyle.transform = 'scale(0)';
+
+ SetPrpoerties(ElementProperties, config, element);
+
+ // Don't propagate touch/mouse events to parent(game canvas)
+ StopPropagationTouchEvents(element);
+
+ // Attach element to fullscreenTarget in full screen mode
+ var scaleManager = parent.scene.sys.scale;
+ var parentElement = (scaleManager.isFullscreen) ? scaleManager.fullscreenTarget : document.body;
+ parentElement.appendChild(element);
+
+ return element;
+}
+
+export default CreateElement;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/InputTextProperties.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/InputTextProperties.js
new file mode 100644
index 000000000..c6dff76f3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/InputTextProperties.js
@@ -0,0 +1,14 @@
+const ElementProperties = {
+ maxLength: ['maxLength', undefined],
+ minLength: ['minLength', undefined],
+ readOnly: ['readOnly', false],
+};
+
+const StyleProperties = {
+ direction: ['direction', undefined]
+};
+
+export {
+ ElementProperties,
+ StyleProperties,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/LastOpenedEditor.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/LastOpenedEditor.js
new file mode 100644
index 000000000..ee0f6f509
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/LastOpenedEditor.js
@@ -0,0 +1,27 @@
+var LastOpenedEditor = undefined;
+
+var SetLastOpenedEditor = function (editor) {
+ if (editor === LastOpenedEditor) {
+ return;
+ }
+
+ if (LastOpenedEditor !== undefined) {
+ LastOpenedEditor.close();
+ }
+
+ LastOpenedEditor = editor;
+}
+
+var CloseLastOpenEditor = function (editor) {
+ if (editor !== LastOpenedEditor) {
+ return;
+ }
+
+ // Don't call `LastOpenedEditor.close()`
+ LastOpenedEditor = undefined;
+}
+
+export {
+ SetLastOpenedEditor,
+ CloseLastOpenEditor
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Methods.js
new file mode 100644
index 000000000..f5647826d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Methods.js
@@ -0,0 +1,9 @@
+import Open from './Open.js';
+import Close from './Close.js';
+
+var Methods = {
+ open: Open,
+ close: Close,
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Open.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Open.js
new file mode 100644
index 000000000..ffcd4d7c4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/Open.js
@@ -0,0 +1,46 @@
+import { SetLastOpenedEditor } from './LastOpenedEditor.js';
+import CreateElement from './CreateElement.js';
+
+var Open = function () {
+ // Already opened
+ if (this.isOpened) {
+ return this;
+ }
+ // Read only
+ if (this.readOnly) {
+ return this;
+ }
+
+ SetLastOpenedEditor(this);
+
+ this.isOpened = true;
+
+ if (!this.node) {
+ // Create input text element when opening editor
+ this.node = CreateElement(this, this.nodeConfig);
+ }
+
+ this.setFocus();
+
+ this.initText();
+
+ if (this.enterCloseEnable) {
+ this.scene.input.keyboard.once('keydown-ENTER', this.close, this);
+ }
+
+ // There is no cursor-position-change event,
+ // so updating cursor position every tick
+ this.scene.sys.events.on('postupdate', this.updateText, this);
+
+ this.scene.input.on('pointerdown', this.onClickOutside, this);
+
+ if (this.onOpenCallback) {
+ this.onOpenCallback(this.parent, this);
+ }
+
+ this.emit('open', this);
+
+ return this;
+}
+
+export default Open;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/RemoveElement.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/RemoveElement.js
new file mode 100644
index 000000000..cb9f2e91e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/hiddentextedit/methods/RemoveElement.js
@@ -0,0 +1,12 @@
+var RemoveElement = function (element) {
+ if (!element) {
+ return;
+ }
+
+ var parentElement = element.parentElement;
+ if (parentElement) {
+ parentElement.removeChild(element);
+ }
+}
+
+export default RemoveElement;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.d.ts
new file mode 100644
index 000000000..77b05c40d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.d.ts
@@ -0,0 +1,29 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Interception;
+
+declare namespace Interception {
+
+ interface IConfig {
+ target?: Phaser.GameObjects.GameObject,
+ enable?: boolean
+ }
+}
+
+declare class Interception extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Interception.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setTarget(
+ gameObject?: Phaser.GameObjects.GameObject
+ ): this;
+ target: Phaser.GameObjects.GameObject | undefined;
+
+ readonly predictedPosition: { x: number, y: number };
+ readonly predictedAngle: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.js
new file mode 100644
index 000000000..3e0d931d1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/interception/Interception.js
@@ -0,0 +1,144 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import SpeedMonitor from '../../utils/speedmonitor/SpeedMonitor.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Vector2 = Phaser.Math.Vector2;
+const Distance = Phaser.Math.Distance.Between;
+const AngleBetweenPoint = Phaser.Math.Angle.BetweenPoints;
+
+class Interception extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this._target = undefined;
+ this.mySpeedMonitor = new SpeedMonitor();
+ this.targetSpeedMonitor = new SpeedMonitor();
+ this.predictedPosition = new Vector2();
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setTarget(GetValue(o, 'target', undefined));
+ return this;
+ }
+
+ toJSON() {
+ return {
+ isRunning: this.isRunning,
+ duration: this.duration,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.setTarget();
+
+ super.shutdown(fromScene);
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ get target() {
+ return this._target;
+ }
+
+ set target(target) {
+ if (target == null) {
+ target = undefined;
+ }
+
+ if (this.target === target) {
+ // Do nothing
+ } else {
+ // Remove previous target
+ if (this.target) {
+ this.target.off('destroy', this.onTargetDestroy, this);
+ }
+
+ // Add new target
+ if (target) {
+ target.once('destroy', this.onTargetDestroy, this);
+ }
+ this._target = target;
+ }
+
+ if (this.isRunning) {
+ if (this.target === undefined) {
+ super.stop();
+ }
+ } else { // !this.isRunning
+ if (this.target !== undefined) {
+ // Start speed monitor
+ this.mySpeedMonitor.init(this.parent.x, this.parent.y);
+ this.targetSpeedMonitor.init(this.target.x, this.target.y);
+ super.start();
+ }
+ }
+ return this;
+ }
+
+ setTarget(target) {
+ this.target = target;
+ return this;
+ }
+
+ onTargetDestroy(target, fromScene) {
+ this.setTarget();
+ return this;
+ }
+
+ update(time, delta) {
+ if (!this.isRunning) {
+ return this;
+ }
+
+ if (this.target === undefined) {
+ return this;
+ } else if (!this.enable) {
+ this.predictedPosition.copy(this.target);
+ return this;
+ }
+
+ var gameObject = this.parent;
+ delta /= 1000; // delta in sec
+ this.mySpeedMonitor.update(gameObject.x, gameObject.y, delta);
+ this.targetSpeedMonitor.update(this.target.x, this.target.y, delta);
+
+ var relatedVelocityX = this.targetSpeedMonitor.velocity.x - this.mySpeedMonitor.velocity.x;
+ var relatedVelocityY = this.targetSpeedMonitor.velocity.y - this.mySpeedMonitor.velocity.y;
+ if ((relatedVelocityX === 0) && (relatedVelocityY === 0)) {
+ // Target and mine is moving parallelly
+ this.predictedPosition.copy(this.target);
+ } else {
+ var relatedSpeed = Distance(0, 0, relatedVelocityX, relatedVelocityY);
+ var distanceToTarget = Distance(this.target.x, this.target.y, gameObject.x, gameObject.y);
+ var time = distanceToTarget / relatedSpeed;
+ this.predictedPosition.set(
+ this.target.x + (this.targetSpeedMonitor.velocity.x * time),
+ this.target.y + (this.targetSpeedMonitor.velocity.y * time),
+ )
+ }
+ return this;
+ }
+
+ get predictedAngle() {
+ return AngleBetweenPoint(this.parent, this.predictedPosition);
+ }
+}
+
+export default Interception;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/GetProgress.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/GetProgress.js
new file mode 100644
index 000000000..cb9e95af5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/GetProgress.js
@@ -0,0 +1,9 @@
+var GetProgress = function (scene) {
+ var loader = scene.load;
+ var total = loader.totalToLoad - 1;
+ var remainder = loader.list.size + loader.inflight.size - 1;
+ var progress = 1 - (remainder / total);
+ return progress;
+}
+
+export default GetProgress;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.d.ts
new file mode 100644
index 000000000..5e75c87a8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.d.ts
@@ -0,0 +1,33 @@
+export default LoadingProgress;
+
+declare namespace LoadingProgress {
+ type ProgressCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ progress: number
+ ) => void;
+
+ type TransitCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number
+ ) => void;
+
+ interface IConfig {
+ duration?: {
+ in?: number,
+ out?: number,
+ },
+
+ progress?: ProgressCallbackType,
+
+ transitIn?: TransitCallbackType,
+
+ transitOut?: TransitCallbackType,
+ }
+}
+
+declare class LoadingProgress extends Phaser.Events.EventEmitter {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: LoadingProgress.IConfig
+ );
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.js
new file mode 100644
index 000000000..5f5895269
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/loadingprogress/LoadingProgress.js
@@ -0,0 +1,74 @@
+import OpenCloseTransition from '../openclosetransition/OpenCloseTransition.js';
+import PopUp from '../../popup.js';
+import ScaleDown from '../scale/ScaleDown.js';
+import NOOP from '../../utils/object/NOOP.js';
+import AwaitLoader from '../../awaitloader.js';
+import GetProgress from './GetProgress.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class LoadingProgress extends OpenCloseTransition {
+ constructor(gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ if (!config.hasOwnProperty('transitIn')) {
+ config.transitIn = PopUp;
+ }
+ if (!config.hasOwnProperty('transitOut')) {
+ config.transitOut = ScaleDown;
+ }
+
+ config.destroy = true;
+
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.scene
+
+ this.setProgressCallback(GetValue(config, 'progress'));
+
+ this.start();
+ }
+
+ setProgressCallback(callback) {
+ if (!callback) {
+ callback = NOOP;
+ }
+
+ this.progressCallback = callback;
+ return this;
+ }
+
+ start() {
+ var self = this;
+ AwaitLoader.call(this.scene.load, function (successCallback, failureCallback) {
+ self.once('close', successCallback);
+ })
+
+ this.requestOpen();
+ }
+
+ onOpen() {
+ this.scene.load.on('progress', this.onProgress, this);
+ this.emit('open', this.parent, this);
+ super.onOpen();
+ this.onProgress(); // Might requestClose if progress === 1
+ }
+
+ onClose() {
+ this.scene.load.off('progress', this.onProgress, this);
+ this.emit('close', this.closeEventData);
+ super.onClose();
+ }
+
+ onProgress() {
+ var progress = GetProgress(this.scene);
+ this.progressCallback(this.parent, progress);
+ this.emit('progress', progress);
+ if (progress === 1) {
+ this.requestClose();
+ }
+ }
+}
+
+export default LoadingProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/CreateCover.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/CreateCover.js
new file mode 100644
index 000000000..c8732dac3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/CreateCover.js
@@ -0,0 +1,24 @@
+import Cover from '../../gameobjects/shape/cover/Cover.js';
+
+var CreateCover = function (gameObject, config) {
+ var scene = gameObject.scene;
+ var cover = new Cover(scene, config);
+ scene.add.existing(cover);
+
+ // Put cover behind game object
+ if (gameObject.isRexContainerLite) {
+ gameObject.moveDepthBelow(cover);
+ gameObject.pin(cover, {
+ syncPosition: false,
+ syncRotation: false,
+ syncScale: false,
+ syncAlpha: false,
+ syncScrollFactor: false
+ });
+ } else {
+ scene.children.moveBelow(cover, gameObject);
+ }
+ return cover;
+}
+
+export default CreateCover;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultCoverTransitCallbacks.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultCoverTransitCallbacks.js
new file mode 100644
index 000000000..f1b9b750c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultCoverTransitCallbacks.js
@@ -0,0 +1,21 @@
+import FadeIn from '../../fade-in.js';
+import FadeOutDestroy from '../../fade-out-destroy.js';
+
+var DefaultCoverTransitInCallback = function (cover, duration) {
+ if (cover._modalAlphaSave !== undefined) {
+ cover.alpha = cover._modalAlphaSave;
+ } else {
+ cover._modalAlphaSave = cover.alpha;
+ }
+
+ FadeIn(cover, duration, cover.alpha);
+}
+
+var DefaultCoverTransitOutCallback = function (cover, duration) {
+ FadeOutDestroy(cover, duration, false);
+}
+
+export {
+ DefaultCoverTransitInCallback,
+ DefaultCoverTransitOutCallback
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultTransitCallbacks.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultTransitCallbacks.js
new file mode 100644
index 000000000..7b8d1c95f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/DefaultTransitCallbacks.js
@@ -0,0 +1,37 @@
+import PopUp from '../../popup.js';
+import ScaleDownDestroy from '../../scale-down-destroy.js';
+import FadeIn from '../../fade-in.js';
+import FadeOutDestroy from '../../fade-out-destroy.js';
+
+export default {
+ popUp(gameObject, duration) {
+ if (gameObject._modalScaleSave !== undefined) {
+ gameObject.scaleX = gameObject._modalScaleSave;
+ gameObject.scaleY = gameObject._modalScaleSave;
+ } else {
+ gameObject._modalScaleSave = gameObject.scaleX;
+ }
+
+ PopUp(gameObject, duration);
+ },
+
+ scaleDown(gameObject, duration) {
+ // Don't destroy here
+ ScaleDownDestroy(gameObject, duration, undefined, undefined, false);
+ },
+
+ fadeIn(gameObject, duration) {
+ if (gameObject._modalAlphaSave !== undefined) {
+ gameObject.alpha = gameObject._modalAlphaSave;
+ } else {
+ gameObject._modalAlphaSave = gameObject.alpha;
+ }
+
+ FadeIn(gameObject, duration);
+ },
+
+ fadeOut(gameObject, duration) {
+ // Don't destroy here
+ FadeOutDestroy(gameObject, duration, false);
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.d.ts
new file mode 100644
index 000000000..b450e020a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.d.ts
@@ -0,0 +1,51 @@
+export default Modal;
+
+declare namespace Modal {
+ type TransitCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number
+ ) => void;
+
+ interface IConfig {
+ cover?: {
+ color?: number,
+ alpha?: number,
+ transitIn?: TransitCallbackType | null,
+ transitOut?: TransitCallbackType | null,
+ },
+
+ manualClose?: boolean,
+
+ clickOutsideClose?: boolean,
+
+ anyTouchClose?: boolean,
+
+ duration?: {
+ in?: number,
+ hold?: number,
+ out?: number,
+ },
+
+ transitIn?: 0 | 1 | 'popUp' | 'fadeIn' | TransitCallbackType,
+
+ transitOut?: 0 | 1 | 'scaleDown' | 'fadeOut' | TransitCallbackType,
+
+ destroy?: boolean,
+
+ openOnStart?: boolean,
+ }
+}
+
+declare class Modal extends Phaser.Events.EventEmitter {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Modal.IConfig
+ );
+
+ requestClose(
+ closeEventData?: unknown
+ ): this;
+
+ setCoverTransitInCallback(callback?: Modal.TransitCallbackType): this;
+ setCoverTransitOutCallback(callback?: Modal.TransitCallbackType): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.js
new file mode 100644
index 000000000..9e7dcabf9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/Modal.js
@@ -0,0 +1,225 @@
+import OpenCloseTransition from '../openclosetransition/OpenCloseTransition.js';
+import CreateCover from './CreateCover.js';
+import DefaultTransitCallbacks from './DefaultTransitCallbacks.js';
+import {
+ DefaultCoverTransitInCallback,
+ DefaultCoverTransitOutCallback
+} from './DefaultCoverTransitCallbacks.js';
+import IsPointInBounds from '../../utils/bounds/IsPointInBounds.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Modal extends OpenCloseTransition {
+ constructor(gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ if (config.transitIn == null) {
+ config.transitIn = TransitionMode.popUp;
+ }
+ if (config.transitOut == null) {
+ config.transitOut = TransitionMode.scaleDown;
+ }
+
+ config.destroy = GetValue(config, 'destroy', true);
+
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.scene
+
+ // Cover : key of modal, to block touch input
+ var coverConfig = GetValue(config, 'cover');
+ this.cover = (coverConfig !== false) ? CreateCover(gameObject, coverConfig) : undefined;
+ if (this.cover) {
+ this.setCoverTransitInCallback(GetValue(coverConfig, 'transitIn', DefaultCoverTransitInCallback));
+ this.setCoverTransitOutCallback(GetValue(coverConfig, 'transitOut', DefaultCoverTransitOutCallback));
+ }
+
+ // Close conditions:
+ var touchOutsideClose = GetValue(config, 'touchOutsideClose', false);
+ var timeOutDuration = GetValue(config, 'duration.hold', -1);
+ var timeOutClose = GetValue(config, 'timeOutClose', (timeOutDuration >= 0));
+ var anyTouchClose = GetValue(config, 'anyTouchClose', false);
+ var manualClose = GetValue(config, 'manualClose', false);
+
+ if (manualClose) {
+ touchOutsideClose = false;
+ anyTouchClose = false;
+ timeOutClose = false;
+ }
+
+ if (anyTouchClose) {
+ touchOutsideClose = false;
+ }
+
+ if (timeOutClose) {
+ this.setDisplayTime(timeOutDuration);
+ } else {
+ this.setDisplayTime(-1);
+ }
+
+ // Registet touch-close event after opened
+ if (anyTouchClose) {
+ this.once('open', this.anyTouchClose, this);
+ } else if (touchOutsideClose) {
+ this.once('open', this.touchOutsideClose, this);
+ }
+
+ if (GetValue(config, 'openOnStart', true)) {
+ // Run this.requestOpen() next tick
+ // User can register events before this.requestOpen()
+ this.delayCall(0, this.requestOpen, this);
+ }
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ // Registered in touchOutsideClose(), or anyTouchClose()
+ if (!this.cover) {
+ this.scene.input.off('pointerup', this.touchCloseCallback, this);
+ }
+
+ if (this.cover && !fromScene) {
+ this.cover.destroy();
+ this.cover = undefined;
+ }
+
+ super.shutdown(fromScene);
+ }
+
+ touchOutsideClose() {
+ if (this.cover) {
+ this.cover.on('pointerup', this.touchCloseCallback, this);
+ } else {
+ this.scene.input.on('pointerup', this.touchCloseCallback, this);
+ }
+ this.clickOutsideTest = true;
+ return this;
+ }
+
+ anyTouchClose() {
+ if (this.cover) {
+ this.cover.once('pointerup', this.touchCloseCallback, this);
+ } else {
+ this.scene.input.once('pointerup', this.touchCloseCallback, this);
+ }
+ return this;
+ }
+
+ touchCloseCallback(pointer) {
+ if (this.clickOutsideTest && IsPointInBounds(this.parent, pointer.worldX, pointer.worldY)) {
+ return;
+ }
+ this.requestClose();
+ }
+
+ runTransitionInCallback() {
+ var duration = super.runTransitionInCallback();
+
+ var cover = this.cover;
+ if (cover && this.coverTransitInCallback) {
+ this.coverTransitInCallback(cover, duration);
+ }
+
+ return duration;
+ }
+
+ runTransitionOutCallback() {
+ var duration = super.runTransitionOutCallback();
+
+ var cover = this.cover;
+ if (cover && this.coverTransitOutCallback) {
+ this.coverTransitOutCallback(cover, duration);
+ }
+
+ return duration;
+ }
+
+ onOpen() {
+ var duration = this.displayTime;
+ if (duration >= 0) {
+ this.delayCall(
+ duration,
+ this.requestClose, // callback
+ this // scope
+ );
+ }
+
+ this.emit('open', this.parent, this);
+
+ super.onOpen();
+ }
+
+ onClose() {
+ this.emit('close', this.closeEventData);
+
+ super.onClose();
+ }
+
+ setDisplayTime(time) {
+ this.displayTime = time;
+ return this;
+ }
+
+ setTransitInCallback(callback) {
+ if (typeof (callback) === 'string') {
+ callback = TransitionMode[callback];
+ }
+
+ switch (callback) {
+ case TransitionMode.popUp:
+ callback = DefaultTransitCallbacks.popUp;
+ break;
+ case TransitionMode.fadeIn:
+ callback = DefaultTransitCallbacks.fadeIn;
+ break;
+ }
+
+ super.setTransitInCallback(callback);
+ // callback = function(gameObject, duration) {}
+ return this;
+ }
+
+ setTransitOutCallback(callback) {
+ if (typeof (callback) === 'string') {
+ callback = TransitionMode[callback];
+ }
+
+ switch (callback) {
+ case TransitionMode.scaleDown:
+ callback = DefaultTransitCallbacks.scaleDown;
+ break;
+ case TransitionMode.fadeOut:
+ callback = DefaultTransitCallbacks.fadeOut;
+ break;
+ }
+
+ super.setTransitOutCallback(callback);
+ // callback = function(gameObject, duration) {}
+ return this;
+ }
+
+ setCoverTransitInCallback(callback) {
+ this.coverTransitInCallback = callback;
+ return this;
+ }
+
+ setCoverTransitOutCallback(callback) {
+ this.coverTransitOutCallback = callback;
+ return this;
+ }
+
+}
+
+const TransitionMode = {
+ popUp: 0,
+ fadeIn: 1,
+ scaleDown: 0,
+ fadeOut: 1,
+}
+
+export default Modal;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.d.ts
new file mode 100644
index 000000000..6b36dda9e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.d.ts
@@ -0,0 +1,18 @@
+import ModalBehavior from './Modal';
+
+export function Modal(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ModalBehavior.IConfig
+): ModalBehavior;
+
+export function ModalPromise(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ModalBehavior.IConfig
+): Promise
+
+export function ModalClose(
+ gameObject: Phaser.GameObjects.GameObject,
+ closeEventData?: unknown
+): void;
+
+export { ModalBehavior };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.js
new file mode 100644
index 000000000..7e12f1f43
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/modal/ModalPromise.js
@@ -0,0 +1,39 @@
+import ModalBehavior from './Modal.js';
+
+var Modal = function (gameObject, config) {
+ var modalBehavior = new ModalBehavior(gameObject, config);
+
+ // Route modal's 'open', 'close' event
+ modalBehavior.on('open', function () {
+ gameObject.emit('modal.open', modalBehavior);
+ })
+ modalBehavior.on('close', function (closeEventData) {
+ gameObject.emit('modal.close', closeEventData, modalBehavior);
+ })
+
+ // Reigster 'modal.requestClose' event for invoking modalBehavior.requestClose() method
+ gameObject.on('modal.requestClose', modalBehavior.requestClose, modalBehavior);
+ /*
+ It is not necessary to turn off gameObject's 'modal.requestClose' event because that :
+
+ - If `config.destroy` is `undefined` (or `true), gameObject and modalBehavior will be destroyed
+ - If `config.destroy` is `false` (for reusing dialog), keeping gameObject and modalBehavior
+ */
+
+ return modalBehavior;
+}
+
+var ModalPromise = function (gameObject, config) {
+ var modalBehavior = Modal(gameObject, config);
+ return new Promise(function (resolve, reject) {
+ modalBehavior.once('close', function (closeEventData) {
+ resolve(closeEventData);
+ });
+ });
+}
+
+var ModalClose = function (gameObject, closeEventData) {
+ gameObject.emit('modal.requestClose', closeEventData);
+}
+
+export { Modal, ModalPromise, ModalClose };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.d.ts
new file mode 100644
index 000000000..a66249ba9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.d.ts
@@ -0,0 +1,48 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default MoveTo;
+
+declare namespace MoveTo {
+
+ interface IConfig {
+ speed?: number,
+ rotateToTarget?: boolean
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ moveTo: MoveTo
+ ) => void;
+ }
+}
+
+declare class MoveTo extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: MoveTo.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ moveTo(x: number, y: number): this;
+ moveTo(config: {
+ x: number,
+ y: number,
+ speed?: number
+ }): this;
+ moveFrom(x: number, y: number): this;
+ moveFrom(config: {
+ x: number,
+ y: number,
+ speed?: number
+ }): this;
+ moveToward(angle: number, distance: number): this;
+
+ setSpeed(speed: number): this;
+ speed: number;
+
+ setRotateToTarget(enable?: boolean): this;
+ rotateToTarget: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.js
new file mode 100644
index 000000000..c01f48a9a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/moveto/MoveTo.js
@@ -0,0 +1,144 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const DistanceBetween = Phaser.Math.Distance.Between;
+const Lerp = Phaser.Math.Linear;
+const AngleBetween = Phaser.Math.Angle.Between;
+
+
+class MoveTo extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.timeScale = GetValue(o, 'timeScale', 1);
+ this.setSpeed(GetValue(o, 'speed', 400));
+ this.setRotateToTarget(GetValue(o, 'rotateToTarget', false));
+ this.targetX = GetValue(o, 'targetX', 0);
+ this.targetY = GetValue(o, 'targetY', 0);
+ return this;
+ }
+
+ toJSON() {
+ return {
+ isRunning: this.isRunning,
+ enable: this.enable,
+ timeScale: this.timeScale,
+ speed: this.speed,
+ rotateToTarget: this.rotateToTarget,
+ targetX: this.targetX,
+ targetY: this.targetY,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ setRotateToTarget(rotateToTarget) {
+ this.rotateToTarget = rotateToTarget;
+ return this;
+ }
+
+ moveTo(x, y) {
+ if (typeof (x) !== 'number') {
+ var config = x;
+ x = config.x;
+ y = config.y;
+ }
+
+ this.targetX = x;
+ this.targetY = y;
+ super.start();
+ this.emit('start', this.parent, this);
+ return this;
+ }
+
+ moveFrom(x, y) {
+ if (typeof (x) !== 'number') {
+ var config = x;
+ x = config.x;
+ y = config.y;
+ }
+
+ var gameObject = this.parent;
+ var targetX = gameObject.x;
+ var targetY = gameObject.y;
+
+ gameObject.setPosition(x, y);
+
+ this.moveTo(targetX, targetY);
+
+ return this;
+ }
+
+ moveToward(angle, distance) {
+ var gameObject = this.parent;
+ var targetX = gameObject.x + Math.cos(angle) * distance;
+ var targetY = gameObject.y + Math.sin(angle) * distance;
+ this.moveTo(targetX, targetY);
+ return this;
+ }
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ var curX = gameObject.x,
+ curY = gameObject.y;
+ var targetX = this.targetX,
+ targetY = this.targetY;
+ if ((curX === targetX) && (curY === targetY)) {
+ this.complete();
+ return this;
+ }
+
+ if ((this.speed === 0) || (delta === 0) || (this.timeScale === 0)) {
+ return this;
+ }
+
+ var dt = (delta * this.timeScale) / 1000;
+ var movingDist = this.speed * dt;
+ var distToTarget = DistanceBetween(curX, curY, targetX, targetY);
+ var newX, newY;
+ if (movingDist < distToTarget) {
+ var t = movingDist / distToTarget;
+ newX = Lerp(curX, targetX, t);
+ newY = Lerp(curY, targetY, t);
+ } else {
+ newX = targetX;
+ newY = targetY;
+ }
+
+ gameObject.setPosition(newX, newY);
+ if (this.rotateToTarget) {
+ gameObject.rotation = AngleBetween(curX, curY, newX, newY);
+ }
+ return this;
+ }
+}
+
+export default MoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.d.ts
new file mode 100644
index 000000000..f488d27d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.d.ts
@@ -0,0 +1,55 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default OpenCloseTransition;
+
+declare namespace OpenCloseTransition {
+ type TransitCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number
+ ) => void;
+
+ interface IConfig {
+
+ duration?: {
+ in?: number,
+ out?: number,
+ },
+
+ transitIn?: TransitCallbackType,
+
+ transitOut?: TransitCallbackType,
+
+ oneShot?: boolean,
+ destroy?: boolean,
+ opened?: boolean,
+ }
+}
+
+declare class OpenCloseTransition extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: OpenCloseTransition.IConfig
+ );
+
+ setTransitInTime(duration: number): this;
+ transitInTime: number;
+
+ setTransitOutTime(duration: number): this;
+ transitOutTime: number;
+
+ setTransitInCallback(
+ callback?: OpenCloseTransition.TransitCallbackType
+ ): this;
+ transitInCallback: OpenCloseTransition.TransitCallbackType;
+
+ setTransitOutCallback(
+ callback?: OpenCloseTransition.TransitCallbackType
+ ): this;
+ transitOutCallback: OpenCloseTransition.TransitCallbackType;
+
+ requestOpen(openEventData?: any, duration?: number): this;
+ onOpen(): void;
+
+ requestClose(closeEventData?: any, duration?: number): this;
+ onClose(): void;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.js
new file mode 100644
index 000000000..4cdcfcc9a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/OpenCloseTransition.js
@@ -0,0 +1,55 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import State from './State.js';
+import Methods from './methods/Methods.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class OpenCloseTransition extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.scene
+
+ this.setTransitInTime(GetValue(config, 'duration.in', 200));
+ this.setTransitOutTime(GetValue(config, 'duration.out', 200));
+ this.setTransitInCallback(GetValue(config, 'transitIn'));
+ this.setTransitOutCallback(GetValue(config, 'transitOut'));
+
+ this.oneShotMode = GetValue(config, 'destroy', false);
+
+ this.delayCallTimer = undefined;
+ this._state = new State(this, {
+ eventEmitter: false,
+ initState: GetValue(config, 'initState', 'IDLE')
+ });
+ this.openEventData = undefined;
+ this.closeEventData = undefined;
+ }
+
+ get state() {
+ return this._state.state;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.transitInCallback = undefined;
+ this.transitOutCallback = undefined;
+ this.openEventData = undefined;
+ this.closeEventData = undefined;
+
+ this.removeDelayCall();
+
+ super.shutdown(fromScene);
+ }
+}
+
+Object.assign(
+ OpenCloseTransition.prototype,
+ Methods,
+)
+
+export default OpenCloseTransition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/State.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/State.js
new file mode 100644
index 000000000..957a91198
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/State.js
@@ -0,0 +1,105 @@
+import FSM from '../../fsm.js';
+
+/*
+graph TD
+
+IDLE --> |"requestOpen()"| TRANS_OPNE["TRAN_OPEN
runTransitionInCallback()"]
+TRANS_OPNE --> |transitInTime| OPEN
+OPEN --> |"requestClose()"| TRANS_CLOSE["TRANS_CLOSE
runTransitionOutCallback()"]
+TRANS_CLOSE --> |transitOutTime| CLOSE
+CLOSE --> |"requestOpen()"| TRANS_OPNE
+*/
+
+class State extends FSM {
+ constructor(parent, config) {
+ super(config);
+ this.parent = parent;
+
+ var initState = config.initState || 'IDLE';
+ this.start(initState);
+ }
+
+ init() {
+ this.start('IDLE');
+ }
+
+ // IDLE -> TRANS_OPNE
+ next_IDLE() {
+ return 'TRANS_OPNE';
+ }
+ // IDLE
+
+ // TRANS_OPNE -> OPEN
+ next_TRANS_OPNE() {
+ return 'OPEN';
+ }
+ enter_TRANS_OPNE() {
+ var transitionBehavior = this.parent;
+ if (transitionBehavior.transitInTime > 0) {
+ var delay = transitionBehavior.runTransitionInCallback();
+ transitionBehavior.delayCall(delay, this.next, this);
+ } else {
+ this.next();
+ }
+ }
+ exit_TRANS_OPNE() {
+ var transitionBehavior = this.parent;
+ transitionBehavior.removeDelayCall();
+ }
+ // TRANS_OPNE
+
+ // OPEN -> TRANS_CLOSE
+ next_OPEN() {
+ return 'TRANS_CLOSE';
+ }
+ enter_OPEN() {
+ var transitionBehavior = this.parent;
+ transitionBehavior.onOpen();
+ }
+ exit_OPEN() {
+ var transitionBehavior = this.parent;
+ transitionBehavior.removeDelayCall();
+ }
+ // OPEN
+
+ // TRANS_CLOSE -> CLOSE
+ next_TRANS_CLOSE() {
+ return 'CLOSE';
+ }
+ enter_TRANS_CLOSE() {
+ var transitionBehavior = this.parent;
+ if (transitionBehavior.transitOutTime > 0) {
+ var delay = transitionBehavior.runTransitionOutCallback();
+ transitionBehavior.delayCall(delay, this.next, this);
+ } else {
+ this.next();
+ }
+ }
+ exit_TRANS_CLOSE() {
+ var transitionBehavior = this.parent;
+ transitionBehavior.removeDelayCall();
+ }
+ // TRANS_CLOSE
+
+ // CLOSE -> TRANS_OPNE
+ next_CLOSE() {
+ return 'TRANS_OPNE';
+ }
+ enter_CLOSE() {
+ var transitionBehavior = this.parent;
+ transitionBehavior.onClose();
+ }
+ exit_CLOSE() {
+ }
+ // CLOSE
+
+ canOpen() {
+ return (this.state === 'IDLE') || (this.state === 'CLOSE');
+ }
+
+ canClose() {
+ return (this.state === 'IDLE') || (this.state === 'OPEN');
+ }
+}
+
+export default State;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/CloseMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/CloseMethods.js
new file mode 100644
index 000000000..a66cdb831
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/CloseMethods.js
@@ -0,0 +1,35 @@
+export default {
+ // Override
+ runTransitionOutCallback() {
+ this.transitOutCallback(this.parent, this.transitOutTime);
+ return this.transitOutTime;
+ },
+
+ // Override
+ onClose() {
+ // Destroy parent and this behavior
+ if (this.oneShotMode) {
+ this.parent.destroy();
+ // Will invoke `this.destroy()`
+ }
+ },
+
+ requestClose(closeEventData, duration) {
+ if (!this._state.canClose) {
+ return this;
+ }
+
+ this.closeEventData = (arguments.length > 0) ? closeEventData : this.parent;
+
+ var transitionTimeSave = this.transitOutTime;
+ if (duration !== undefined) {
+ this.transitOutTime = duration;
+ }
+
+ this._state.goto('TRANS_CLOSE');
+
+ this.transitOutTime = transitionTimeSave;
+
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/ConfigurationMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/ConfigurationMethods.js
new file mode 100644
index 000000000..c8ef2bf2c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/ConfigurationMethods.js
@@ -0,0 +1,34 @@
+import NOOP from '../../../utils/object/NOOP.js';
+
+export default {
+ setTransitInTime(time) {
+ this.transitInTime = time;
+ return this;
+ },
+
+ setTransitOutTime(time) {
+ this.transitOutTime = time;
+ return this;
+ },
+
+ setTransitInCallback(callback) {
+ if (!callback) {
+ callback = NOOP;
+ }
+
+ this.transitInCallback = callback;
+ // callback = function(gameObject, duration) {}
+ return this;
+ },
+
+ setTransitOutCallback(callback) {
+ if (!callback) {
+ callback = NOOP;
+ }
+
+ this.transitOutCallback = callback;
+ // callback = function(gameObject, duration) {}
+ return this;
+ },
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/DelayCallMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/DelayCallMethods.js
new file mode 100644
index 000000000..56b7a2f4b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/DelayCallMethods.js
@@ -0,0 +1,18 @@
+import PostStepDelayCall from '../../../utils/time/PostStepDelayCall.js';
+
+export default {
+ delayCall(delay, callback, scope) {
+ // Invoke callback under scene's 'postupdate' event
+ this.delayCallTimer = PostStepDelayCall(this, delay, callback, scope);
+ return this;
+ },
+
+ removeDelayCall() {
+ if (this.delayCallTimer) {
+ this.delayCallTimer.remove(false);
+ this.delayCallTimer = undefined;
+ }
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/Methods.js
new file mode 100644
index 000000000..92e5180b1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/Methods.js
@@ -0,0 +1,16 @@
+import DelayCallMethods from './DelayCallMethods.js';
+import ConfigurationMethods from './ConfigurationMethods.js';
+import OpenMethods from './OpenMethods.js';
+import CloseMethods from './CloseMethods.js';
+
+var methods = {};
+
+Object.assign(
+ methods,
+ DelayCallMethods,
+ ConfigurationMethods,
+ OpenMethods,
+ CloseMethods,
+)
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/OpenMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/OpenMethods.js
new file mode 100644
index 000000000..4bdcb22e8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/openclosetransition/methods/OpenMethods.js
@@ -0,0 +1,30 @@
+export default {
+ // Override
+ runTransitionInCallback() {
+ this.transitInCallback(this.parent, this.transitInTime);
+ return this.transitInTime;
+ },
+
+ // Override
+ onOpen() {
+ },
+
+ requestOpen(openEventData, duration) {
+ if (!this._state.canOpen()) {
+ return this;
+ }
+
+ this.openEventData = (arguments.length > 0) ? openEventData : this.parent;
+
+ var transitionTimeSave = this.transitInTime;
+ if (duration !== undefined) {
+ this.transitInTime = duration;
+ }
+
+ this._state.goto('TRANS_OPNE');
+
+ this.transitInTime = transitionTimeSave;
+
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.d.ts
new file mode 100644
index 000000000..d1ea43c23
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.d.ts
@@ -0,0 +1,36 @@
+export default ParticlesAlongBounds;
+
+declare namespace ParticlesAlongBounds {
+
+ interface IConfig {
+ textureKey: string,
+ textureFrames?: string |
+ number |
+ (string | number)[] |
+ {
+ frames: (string | number)[],
+ cycle?: boolean,
+ quantity?: number
+ },
+ padding?: number | { left?: number, right?: number, top?: number, bottom?: number },
+
+ blendMode?: Phaser.BlendModes | string,
+ lifespan?: number,
+ stepRate?: number,
+ spread?: number,
+
+ scale?: Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType | Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType
+ alpha?: Phaser.Types.GameObjects.Particles.EmitterOpOnEmitType | Phaser.Types.GameObjects.Particles.EmitterOpOnUpdateType
+ tint?: number,
+
+ repeat?: number,
+ gravityX?: number,
+ gravityY?: number,
+ duration?: number
+ }
+}
+
+declare function ParticlesAlongBounds(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ParticlesAlongBounds.IConfig,
+): Phaser.GameObjects.Particles.ParticleEmitter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.js
new file mode 100644
index 000000000..57d629616
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/ParticlesAlongBounds.js
@@ -0,0 +1,20 @@
+import CreateEmitterConfig from './methods/CreateEmitterConfig.js';
+import SyncToGameObject from './methods/SyncToGameObject.js';
+
+var ParticlesAlongBounds = function (gameObject, config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ var emitterConfig = CreateEmitterConfig(gameObject, config);
+ var particles = gameObject.scene.add.particles(0, 0, config.textureKey, emitterConfig);
+ SyncToGameObject(particles, gameObject, config);
+
+ particles.once('complete', function () {
+ particles.destroy();
+ });
+
+ return particles;
+}
+
+export default ParticlesAlongBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/BoundsToPoints.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/BoundsToPoints.js
new file mode 100644
index 000000000..20ed94716
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/BoundsToPoints.js
@@ -0,0 +1,27 @@
+import GetBoundsConfig from '../../../utils/bounds/GetBoundsConfig.js';
+
+const Rectangle = Phaser.Geom.Rectangle;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var BoundsToPoints = function (gameObject, config) {
+ if (globRect === undefined) {
+ globRect = new Rectangle();
+ }
+
+ globPadding = GetBoundsConfig(GetValue(config, 'padding', 0), globPadding);
+ var w = gameObject.width,
+ h = gameObject.height;
+ var x = (-w / 2) - globPadding.left,
+ y = (-h / 2) - globPadding.top;
+ w += globPadding.left + globPadding.right;
+ h += globPadding.top + globPadding.bottom;
+ globRect.setTo(x, y, w, h);
+ var stepRate = GetValue(config, 'stepRate', 10);
+ var points = globRect.getPoints(0, stepRate);
+ return points; // Return new point array
+}
+
+var globRect;
+var globPadding;
+
+export default BoundsToPoints;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/CreateEmitterConfig.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/CreateEmitterConfig.js
new file mode 100644
index 000000000..440c4f29f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/CreateEmitterConfig.js
@@ -0,0 +1,86 @@
+import BoundsToPoints from './BoundsToPoints.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const TickTime = (1000 / 60);
+
+var CreateEmitterConfig = function (gameObject, config) {
+ var points = BoundsToPoints(gameObject, config);
+ var emitterConfig = {
+ blendMode: GetValue(config, 'blendMode', 'ADD'),
+ emitZone: {
+ type: 'edge',
+ source: {
+ getPoints: function () {
+ return points;
+ }
+ },
+ yoyo: GetValue(config, 'yoyo', false)
+ },
+ speed: GetValue(config, 'spread', 10)
+ };
+
+ // Set lifespan
+ var lifespan = GetValue(config, 'lifespan', 1000);
+ emitterConfig.lifespan = lifespan;
+ // Set quantity or frequency
+ var duration = GetValue(config, 'duration', undefined);
+ if (duration !== undefined) {
+ var lastDelay = duration - lifespan;
+ if (lastDelay <= 0) { // Fire all particles at beginning
+ emitterConfig.quantity = points.length;
+ } else {
+ var delayPerParticle = lastDelay / points.length;
+ if (delayPerParticle <= TickTime) { // Fire more then 1 particle per tick
+ emitterConfig.quantity = Math.ceil(TickTime / delayPerParticle);
+ } else { // Not fire 1 particle per tick, set frequency
+ emitterConfig.frequency = delayPerParticle;
+ }
+ }
+ }
+
+ // stopAfter
+ var repeat = 1 + GetValue(config, 'repeat', 0);
+ var totalParticleCount = repeat * points.length;
+ if (emitterConfig.hasOwnProperty('frequency')) {
+ // Can't use 'stopAfter' in this case
+ emitterConfig.emitCallback = function (particle, emitter) {
+ totalParticleCount -= 1;
+ if (totalParticleCount <= 0) {
+ emitter.stop();
+ }
+ }
+ } else {
+ emitterConfig.stopAfter = totalParticleCount;
+ }
+
+ // Set texture frame
+ var textureFrames = GetValue(config, 'textureFrames', undefined);
+ if (textureFrames) {
+ emitterConfig.frame = {
+ frames: textureFrames,
+ cycle: GetValue(config, 'textureFrameCycle', true)
+ }
+ }
+
+ // Set scale
+ var scale = GetValue(config, 'scale', undefined);
+ if (scale !== undefined) {
+ emitterConfig.scale = scale;
+ }
+
+ // Set alpha
+ var alpha = GetValue(config, 'alpha', undefined);
+ if (alpha !== undefined) {
+ emitterConfig.alpha = alpha;
+ }
+
+ // Set tint
+ var tint = GetValue(config, 'tint', undefined);
+ if (tint !== undefined) {
+ emitterConfig.tint = tint;
+ }
+
+ return emitterConfig;
+}
+
+export default CreateEmitterConfig;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/SyncToGameObject.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/SyncToGameObject.js
new file mode 100644
index 000000000..c3e79e22e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/particlesalongbounds/methods/SyncToGameObject.js
@@ -0,0 +1,60 @@
+const PreUpdate = Phaser.GameObjects.Particles.ParticleEmitter.prototype.preUpdate;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Vector2 = Phaser.Math.Vector2;
+
+var SyncToGameObject = function (particles, gameObject, config) {
+ var gravityX = GetValue(config, 'gravityX', 0);
+ var gravityY = GetValue(config, 'gravityY', 0);
+ var hasGravity = (gravityX !== 0) || (gravityY !== 0);
+
+ // Override update, sync properties of particles to game object
+ particles.preUpdate = (function (delta, step, processors) {
+ if (!gameObject.scene) { // gameObject has been destroyed
+ this.destroy();
+ return;
+ }
+
+ // Sync to gameObject
+ SyncTo.call(particles, gameObject);
+
+ if (hasGravity) {
+ var localGravityX, localGravityY;
+ if (gameObject.rotation !== 0) {
+ var gravityVector = new Vector2();
+ gravityVector
+ .setTo(gravityX, gravityY)
+ .rotate(-gameObject.rotation);
+ localGravityX = gravityVector.x;
+ localGravityY = gravityVector.y;
+ } else {
+ localGravityX = gravityX;
+ localGravityY = gravityY;
+ }
+ particles.setParticleGravity(localGravityX, localGravityY);
+ }
+
+ PreUpdate.call(particles, delta, step, processors);
+ }).bind(particles);
+
+ return particles;
+}
+
+var SyncTo = function (gameObject) {
+ if (globPoint === undefined) {
+ globPoint = { x: 0, y: 0 };
+ }
+ gameObject.getCenter(globPoint);
+ this
+ .setPosition(globPoint.x, globPoint.y)
+ .setScale(gameObject.scaleX, gameObject.scaleY)
+ .setAngle(gameObject.angle)
+ .setAlpha(gameObject.alpha);
+
+ if (this.depth !== gameObject.depth) {
+ this.setDepth(gameObject.depth);
+ }
+}
+
+var globPoint;
+
+export default SyncToGameObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.d.ts
new file mode 100644
index 000000000..7e645a9b3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.d.ts
@@ -0,0 +1,44 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default PathFollower;
+
+declare namespace PathFollower {
+
+ interface IConfig {
+ path?: Phaser.Curves.Path,
+ t?: number,
+ rotateToPath?: boolean,
+ rotationOffset?: number,
+ angleOffset?: number,
+
+ spacedPoints?: {
+ divisions?: number,
+ stepRate?: number
+ } | boolean
+ }
+}
+
+declare class PathFollower extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: PathFollower.IConfig
+ );
+
+ setT(t: number): this;
+ t: number;
+
+ setPath(path: Phaser.Curves.Path): this;
+ path: Phaser.Curves.Path;
+
+ setRotateToPath(
+ rotateToPath: boolean,
+ rotationOffset?: number
+ ): this;
+ rotateToPath: boolean;
+ rotationOffset: number;
+
+ setSpacedPointsMode(
+ divisions?: number | boolean,
+ stepRate?: number
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.js
new file mode 100644
index 000000000..c38f339dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/pathfollower/PathFollower.js
@@ -0,0 +1,137 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Vector2 = Phaser.Math.Vector2;
+const DegToRad = Phaser.Math.DegToRad;
+const AngleBetween = Phaser.Math.Angle.Between;
+const Linear = Phaser.Math.Linear;
+
+class PathFollower extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this._t = 0;
+ this.pathVector = new Vector2();
+ this.spacePoints = undefined;
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.setPath(GetValue(o, 'path', undefined));
+
+ var rotateToPath = GetValue(o, 'rotateToPath', false);
+ var rotationOffset = GetValue(o, 'rotationOffset', undefined);
+ if (rotationOffset === undefined) {
+ rotationOffset = DegToRad(GetValue(o, 'angleOffset', 0));
+ }
+ this.setRotateToPath(rotateToPath, rotationOffset);
+
+ var spacedPoints = GetValue(o, 'spacedPoints', false);
+ if (spacedPoints) {
+ this.setSpacedPointsMode(
+ GetValue(spacedPoints, 'divisions', undefined),
+ GetValue(spacedPoints, 'stepRate', 10)
+ )
+ } else {
+ this.setSpacedPointsMode(false);
+ }
+
+ var t = GetValue(o, 't', undefined);
+ if (t !== undefined) {
+ this.setT(t);
+ }
+ return this;
+ }
+
+ toJSON() {
+ return {
+ path: this.path,
+ t: this.t,
+ rotateToPath: this.rotateToPath,
+ rotationOffset: this.rotationOffset
+ };
+ }
+
+ setPath(path) {
+ this.path = path;
+ return this;
+ }
+
+ setT(t) {
+ this.t = t;
+ return this;
+ }
+
+ get t() {
+ return this._t;
+ }
+
+ set t(value) {
+ this._t = value;
+ this.update();
+ }
+
+ setRotateToPath(rotateToPath, rotationOffset) {
+ this.rotateToPath = rotateToPath;
+ this.rotationOffset = rotationOffset;
+ return this;
+ }
+
+ setSpacedPointsMode(divisions, stepRate) {
+ if ((!divisions) && (!stepRate)) {
+ this.spacePoints = undefined;
+ } else {
+ this.spacePoints = this.path.getSpacedPoints(divisions, stepRate, this.spacePoints);
+ // Add point at t=1
+ this.spacePoints.push(this.path.getPoint(1));
+ }
+ return this;
+ }
+
+ getPoint(t) {
+ if (this.spacePoints === undefined) {
+ return this.path.getPoint(this.t, this.pathVector);
+
+ } else {
+ var start = (this.spacePoints.length - 1) * t;
+ var index = Math.floor(start);
+ var p0 = this.spacePoints[index],
+ p1 = this.spacePoints[index + 1];
+ if (!p1) {
+ this.pathVector.x = p0.x;
+ this.pathVector.y = p0.y;
+ } else {
+ var remainderT = start - index;
+ this.pathVector.x = Linear(p0.x, p1.x, remainderT);
+ this.pathVector.y = Linear(p0.y, p1.y, remainderT);
+ }
+ return this.pathVector;
+ }
+ }
+
+ update() {
+ if (this.path === undefined) {
+ return;
+ }
+
+ var gameObject = this.parent;
+ var curX = gameObject.x,
+ curY = gameObject.y;
+ this.pathVector = this.getPoint(this._t);
+ var newX = this.pathVector.x,
+ newY = this.pathVector.y;
+
+ if ((curX === newX) && (curY === newY)) {
+ return;
+ }
+
+ gameObject.setPosition(newX, newY);
+ if (this.rotateToPath) {
+ gameObject.rotation = AngleBetween(curX, curY, newX, newY) + this.rotationOffset;
+ }
+ }
+}
+
+export default PathFollower;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/perlingrivatywell/PerlinGrivatyWell.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/perlingrivatywell/PerlinGrivatyWell.js
new file mode 100644
index 000000000..a028029e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/perlingrivatywell/PerlinGrivatyWell.js
@@ -0,0 +1,32 @@
+import Perlin from '../../utils/math/noise/Perlin.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const PERIOD_SCALE = 1 / 5000;
+const ROTATE_SCALE = 0.3;
+
+class PerlinGrivatyWell {
+ constructor(config) {
+ this.active = true;
+ this.noise = new Perlin(GetValue(config, 'seed', Math.random()));
+ this.periodScale = GetValue(config, 'periodScale', PERIOD_SCALE);
+ this.rotateScale = GetValue(config, 'rotateScale', ROTATE_SCALE);
+ this.velocity = new Phaser.Math.Vector2();
+ }
+
+ update(particle, delta) {
+ var period = delta * this.periodScale;
+ var noiseValue = this.noise.perlin2(
+ particle.x * period,
+ particle.y * period
+ ); // -1 ~ 1
+
+ var noiseAngle = noiseValue * Math.PI; // -PI ~ PI
+ this.velocity.set(particle.velocityX, particle.velocityY)
+ .rotate(noiseAngle * this.rotateScale)
+
+ particle.velocityX = this.velocity.x
+ particle.velocityY = this.velocity.y;
+ }
+}
+
+export default PerlinGrivatyWell;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.d.ts
new file mode 100644
index 000000000..598fa72f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.d.ts
@@ -0,0 +1,18 @@
+export default AddPolarCoordinateProperties;
+
+declare namespace AddPolarCoordinateProperties {
+ interface PolarCoordinateGameObject extends Phaser.GameObjects.GameObject {
+ polarOX: number;
+ polarOY: number;
+ polarRotation: number;
+ polarAngle: number;
+ polarRadius: number;
+ }
+}
+
+declare function AddPolarCoordinateProperties(
+ gameObject: Phaser.GameObjects.GameObject,
+ ox?: number, oy?: number,
+ rotation?: number,
+ radius?: number
+): AddPolarCoordinateProperties.PolarCoordinateGameObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.js
new file mode 100644
index 000000000..2fd6e0a6e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/polarcoordinate/AddPolarCoordinateProperties.js
@@ -0,0 +1,84 @@
+import PolarToCartesian from '../../utils/math/coordinate/PolarToCartesian.js';
+
+var DegToRad = Phaser.Math.DegToRad;
+var RadToDeg = Phaser.Math.RadToDeg;
+
+var AddPolarCoordinateProperties = function (gameObject, ox, oy, rotation, radius) {
+ // Don't attach properties again
+ if (gameObject.hasOwnProperty('polarOX')) {
+ return gameObject;
+ }
+
+ if (ox === undefined) {
+ ox = 0;
+ }
+ if (oy === undefined) {
+ oy = 0;
+ }
+ if (rotation === undefined) {
+ rotation = 0;
+ }
+ if (radius === undefined) {
+ radius = 0;
+ }
+
+ Object.defineProperty(gameObject, 'polarOX', {
+ get: function () {
+ return ox;
+ },
+ set: function (value) {
+ if (ox !== value) {
+ ox = value;
+ PolarToCartesian(ox, oy, rotation, radius, gameObject);
+ }
+ },
+ });
+
+ Object.defineProperty(gameObject, 'polarOY', {
+ get: function () {
+ return oy;
+ },
+ set: function (value) {
+ if (oy !== value) {
+ oy = value;
+ PolarToCartesian(ox, oy, rotation, radius, gameObject);
+ }
+ },
+ });
+
+ Object.defineProperty(gameObject, 'polarRotation', {
+ get: function () {
+ return rotation;
+ },
+ set: function (value) {
+ if (rotation !== value) {
+ rotation = value;
+ PolarToCartesian(ox, oy, rotation, radius, gameObject);
+ }
+ },
+ });
+ Object.defineProperty(gameObject, 'polarAngle', {
+ get: function () {
+ return RadToDeg(rotation);
+ },
+ set: function (value) {
+ this.polarRotation = DegToRad(value);
+ },
+ });
+
+ Object.defineProperty(gameObject, 'polarRadius', {
+ get: function () {
+ return radius;
+ },
+ set: function (value) {
+ if (radius !== value) {
+ radius = value;
+ PolarToCartesian(ox, oy, rotation, radius, gameObject);
+ }
+ },
+ });
+
+ PolarToCartesian(ox, oy, rotation, radius, gameObject);
+}
+
+export default AddPolarCoordinateProperties;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.d.ts
new file mode 100644
index 000000000..e61c60225
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.d.ts
@@ -0,0 +1,25 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Rotate;
+
+declare namespace Rotate {
+
+ interface IConfig {
+ enable?: boolean,
+ speed?: number,
+ timeScale?: number
+ }
+}
+
+declare class Rotate extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Rotate.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setSpeed(speed: number): this;
+ speed: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.js
new file mode 100644
index 000000000..683981cb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotate/Rotate.js
@@ -0,0 +1,75 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Rotate extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.setEnable(GetValue(o, 'enable', true));
+ this.timeScale = GetValue(o, 'timeScale', 1);
+ this.setSpeed(GetValue(o, 'speed', 180));
+ return this;
+ }
+
+ toJSON() {
+ return {
+ enable: this.isRunning,
+ timeScale: this.timeScale,
+ speed: this.speed,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ get enable() {
+ return this.isRunning;
+ }
+
+ set enable(enable) {
+ if (enable) {
+ this.start();
+ } else {
+ this.stop();
+ }
+ }
+
+ setEnable(enable) {
+ if (enable == undefined) {
+ enable = true;
+ }
+ this.enable = enable;
+ return this;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ update(time, delta) {
+ if (!this.isRunning) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ if ((this.speed === 0) || (delta === 0) || (this.timeScale === 0)) {
+ return this;
+ }
+
+ var dt = (delta * this.timeScale) / 1000;
+ gameObject.angle += this.speed * dt;
+ return this;
+ }
+}
+
+export default Rotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.d.ts
new file mode 100644
index 000000000..7aa64186a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.d.ts
@@ -0,0 +1,47 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default RotateTo;
+
+declare namespace RotateTo {
+
+ type DirectionType = 0 | 1 | 2 | 'cw' | 'ccw';
+
+ interface IConfig {
+ enable?: boolean,
+ speed?: number,
+ timeScale?: number
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ rotateTo: RotateTo
+ ) => void;
+ }
+}
+
+declare class RotateTo extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: RotateTo.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ rotateTowardsPosition(
+ x: number,
+ y: number,
+ direction?: RotateTo.DirectionType,
+ speed?: number
+ ): this;
+
+ rotateTo(
+ degrees: number,
+ direction?: RotateTo.DirectionType,
+ speed?: number
+ ): this;
+
+ setSpeed(speed: number): this;
+ speed: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.js
new file mode 100644
index 000000000..bb7d23619
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/rotateto/RotateTo.js
@@ -0,0 +1,149 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import DegToRad from '../../utils/math/DegToRad.js';
+import RadToDeg from '../../utils/math/RadToDeg.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const MathWrap = Phaser.Math.Wrap;
+const WrapAngle = Phaser.Math.Angle.Wrap;
+const AngleBetween = Phaser.Math.Angle.Between;
+
+
+class RotateTo extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.timeScale = GetValue(o, 'timeScale', 1);
+ this.setSpeed(GetValue(o, 'speed', 180));
+ this.target = GetValue(o, 'target', 0);
+ this.dir = GetValue(o, 'dir', 0);
+ return this;
+ }
+
+ toJSON() {
+ return {
+ isRunning: this.isRunning,
+ timeScale: this.timeScale,
+ speed: this.speed,
+ target: this.target,
+ dir: this.dir,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ rotateTo(angle, dir, speed) {
+ if (typeof (angle) !== 'number') {
+ var config = angle;
+ angle = GetValue(config, 'angle', undefined);
+ dir = GetValue(config, 'dir', undefined);
+ }
+ this.target = MathWrap(angle, 0, 360); // 0~360
+ if (dir === undefined) {
+ dir = 0;
+ }
+ this.dir = (typeof (dir) === 'string') ? DIRMODE[dir] : dir;
+ if (speed !== undefined) {
+ this.setSpeed(speed);
+ }
+ super.start();
+ this.emit('start', this.parent, this);
+ return this;
+ }
+
+ rotateTowardsPosition(x, y, dir, speed) {
+ var gameObject = this.parent;
+ var rad = AngleBetween(gameObject.x, gameObject.y, x, y);
+ var angle = RadToDeg(rad);
+ this.rotateTo(angle, dir, speed);
+ return this;
+ }
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ var target = this.target; // 0~360
+ var targetRad = WrapAngle(DegToRad(target)); // -PI~PI
+ if (targetRad === gameObject.rotation) {
+ this.complete();
+ return this;
+ }
+
+ if ((this.speed === 0) || (delta === 0) || (this.timeScale === 0)) {
+ return this;
+ }
+
+ var curAngle = (360 + gameObject.angle) % 360; // 0~360
+ var dt = (delta * this.timeScale) / 1000;
+ var movingDist = this.speed * dt;
+ var distToTarget, dir = this.dir;
+ switch (dir) {
+ case 0: // shotest
+ var distCW = diffAngle(curAngle, target, true);
+ var distCCW = 360 - distCW;
+ if (distCW < distCCW) {
+ dir = 1;
+ distToTarget = distCW;
+ } else {
+ dir = 2;
+ distToTarget = distCCW;
+ }
+ break;
+ case 1: // cw
+ distToTarget = diffAngle(curAngle, target, true);
+ break;
+ case 2: // ccw
+ distToTarget = diffAngle(curAngle, target, false);
+ break;
+ }
+
+ var newAngle;
+ if (movingDist < distToTarget) {
+ newAngle = (dir === 1) ? (curAngle + movingDist) : (curAngle - movingDist);
+ } else {
+ newAngle = target;
+ }
+
+ gameObject.rotation = DegToRad(newAngle);
+ return this;
+ }
+}
+
+var diffAngle = function (a0, a1, cw) {
+ var diff = (cw) ? (a1 - a0) : (a0 - a1);
+ diff = MathWrap(diff, 0, 360);
+ return diff;
+}
+
+const DIRMODE = {
+ shortest: 0,
+ cw: 1,
+ ccw: 2
+}
+export default RotateTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.d.ts
new file mode 100644
index 000000000..fe8451aa2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.d.ts
@@ -0,0 +1,9 @@
+import Scale from './Scale';
+
+export default function PopUp(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ orientation?: number | string,
+ ease?: string,
+ scale?: Scale
+): Scale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.js
new file mode 100644
index 000000000..66b63a91d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/PopUp.js
@@ -0,0 +1,45 @@
+import Scale from './Scale.js';
+
+var PopUp = function (gameObject, duration, orientation, ease, scale) {
+ if (ease === undefined) {
+ ease = 'Cubic';
+ }
+
+ // Ease scale from 0 to current scale
+ var start, end;
+ switch (orientation) {
+ case 0:
+ case 'x':
+ start = { x: 0 };
+ end = { x: gameObject.scaleX };
+ break;
+ case 1:
+ case 'y':
+ start = { y: 0 };
+ end = { y: gameObject.scaleY };
+ break;
+ default:
+ start = 0;
+ end = gameObject.scale;
+ break;
+ }
+
+ var config = {
+ mode: 0,
+ start: start,
+ end: end,
+ duration: duration,
+ ease: ease
+ }
+
+ if (scale === undefined) {
+ scale = new Scale(gameObject, config);
+ } else {
+ scale.resetFromJSON(config);
+ }
+ scale.restart();
+
+ return scale;
+};
+
+export default PopUp;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.d.ts
new file mode 100644
index 000000000..82c2459ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.d.ts
@@ -0,0 +1,42 @@
+import EaseValueTaskBase from "../../utils/componentbase/tweentask/EaseValueTaskBase";
+
+export default Scale;
+
+declare namespace Scale {
+ type ModeType = 0 | 1 | 2 | 'stop' | 'destroy' | 'yoyo';
+
+ interface IConfig {
+ mode?: ModeType,
+ start?: number,
+ end?: number,
+ duration?: number,
+ delay?: number,
+ ease?: string
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ scale: Scale
+ ) => void;
+ }
+}
+
+declare class Scale extends EaseValueTaskBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Scale.IConfig
+ )
+
+ setMode(mode: Scale.ModeType): this;
+ mode: number;
+
+ setScaleRange(
+ start: number | { x: number, y: number },
+ end: number | { x: number, y: number }
+ ): this;
+ startX: number;
+ startY: number;
+ endX: number;
+ endY: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.js
new file mode 100644
index 000000000..efb376025
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Scale.js
@@ -0,0 +1,122 @@
+import EaseValueTaskBase from '../../utils/componentbase/tweentask/EaseValueTaskBase.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const Linear = Phaser.Math.Linear;
+
+class Scale extends EaseValueTaskBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+ // this.timer
+
+ this.scaleStart = {};
+ this.scaleEnd = {};
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ super.resetFromJSON(o);
+
+ this.setMode(GetValue(o, 'mode', 0));
+ this.setScaleRange(
+ GetAdvancedValue(o, 'start', undefined),
+ GetAdvancedValue(o, 'end', 0)
+ );
+
+ return this;
+ }
+
+ setMode(m) {
+ if (typeof (m) === 'string') {
+ m = MODE[m];
+ }
+ this.mode = m;
+ return this;
+ }
+
+ setScaleRange(start, end) {
+ if (typeof (start) === 'number') {
+ this.startX = start;
+ this.startY = start;
+ } else {
+ this.startX = GetAdvancedValue(start, 'x', this.parent.scaleX);
+ this.startY = GetAdvancedValue(start, 'y', this.parent.scaleY);
+ }
+ if (typeof (end) === 'number') {
+ this.endX = end;
+ this.endY = end;
+ } else {
+ this.endX = GetAdvancedValue(end, 'x', undefined);
+ this.endY = GetAdvancedValue(end, 'y', undefined);
+ }
+
+ this.hasScaleX = (this.startX !== undefined) && (this.endX !== undefined);
+ this.hasScaleY = (this.startY !== undefined) && (this.endY !== undefined);
+ return this;
+ }
+
+ start() {
+ if (this.timer.isRunning) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (this.hasScaleX) {
+ gameObject.scaleX = this.startX;
+ }
+ if (this.hasScaleY) {
+ gameObject.scaleY = this.startY;
+ }
+
+ var repeat = this.repeat;
+ if (this.mode === 2) { // Yoyo
+ if (repeat !== -1) {
+ repeat = ((repeat + 1) * 2) - 1;
+ }
+ }
+
+ this.timer
+ .setDelay(this.delay)
+ .setDuration(this.duration)
+ .setRepeat(repeat);
+
+ super.start();
+ return this;
+ }
+
+ updateGameObject(gameObject, timer) {
+ var t = timer.t;
+ if (timer.isOddIteration) { // Yoyo
+ t = 1 - t;
+ }
+ t = this.easeFn(t);
+
+ if (this.hasScaleX) {
+ gameObject.scaleX = Linear(this.startX, this.endX, t);
+ }
+ if (this.hasScaleY) {
+ gameObject.scaleY = Linear(this.startY, this.endY, t);
+ }
+ }
+
+ complete() {
+ super.complete();
+
+ if (this.mode === 1) {
+ this.parent.destroy();
+ // Will also destroy this behavior
+ }
+ return this;
+ }
+}
+
+const MODE = {
+ stop: 0,
+ destroy: 1,
+ yoyo: 2
+}
+
+export default Scale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.d.ts
new file mode 100644
index 000000000..673547da6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.d.ts
@@ -0,0 +1,9 @@
+import Scale from './Scale';
+
+export default function ScaleDown(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ orientation?: number | string,
+ ease?: string,
+ scale?: Scale
+): Scale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.js
new file mode 100644
index 000000000..518755e11
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDown.js
@@ -0,0 +1,40 @@
+import Scale from './Scale.js';
+
+var ScaleDown = function (gameObject, duration, orientation, ease, scale) {
+ if (ease === undefined) {
+ ease = 'Linear';
+ }
+
+ var config = {};
+ config.mode = 0;
+ switch (orientation) {
+ case 0:
+ case 'x':
+ config.end = {
+ x: 0
+ };
+ break;
+ case 1:
+ case 'y':
+ config.end = {
+ y: 0
+ };
+ break;
+ default:
+ config.end = 0;
+ break;
+ }
+ config.duration = duration;
+ config.ease = ease;
+
+ if (scale === undefined) {
+ scale = new Scale(gameObject, config);
+ } else {
+ scale.resetFromJSON(config);
+ }
+ scale.restart();
+
+ return scale;
+};
+
+export default ScaleDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.d.ts
new file mode 100644
index 000000000..24b7a2048
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.d.ts
@@ -0,0 +1,18 @@
+import Scale from './Scale';
+
+export default function ScaleDownDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ orientation?: number | string,
+ ease?: string,
+ scale?: Scale
+): Scale;
+
+export default function ScaleDownDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ orientation?: number | string,
+ ease?: string,
+ destroyMode?: boolean,
+ scale?: Scale
+): Scale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.js
new file mode 100644
index 000000000..858db1aaf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/ScaleDownDestroy.js
@@ -0,0 +1,50 @@
+import Scale from './Scale.js';
+
+var ScaleDownDestroy = function (gameObject, duration, orientation, ease, destroyMode, scale) {
+ if (ease === undefined) {
+ ease = 'Linear';
+ }
+
+ // Ease from current scale to 0
+ if (destroyMode instanceof Scale) {
+ scale = destroyMode;
+ destroyMode = undefined;
+ }
+
+ if (destroyMode === undefined) {
+ destroyMode = true;
+ }
+
+ var config = {};
+ config.mode = (destroyMode) ? 1 : 0;
+ switch (orientation) {
+ case 0:
+ case 'x':
+ config.end = {
+ x: 0
+ };
+ break;
+ case 1:
+ case 'y':
+ config.end = {
+ y: 0
+ };
+ break;
+ default:
+ config.end = 0;
+ break;
+ }
+ config.duration = duration;
+ config.ease = ease;
+
+ if (scale === undefined) {
+ scale = new Scale(gameObject, config);
+ } else {
+ scale.resetFromJSON(config);
+ }
+ scale.restart();
+
+ return scale;
+};
+
+export default ScaleDownDestroy;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.d.ts
new file mode 100644
index 000000000..0ca6d3088
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.d.ts
@@ -0,0 +1,11 @@
+import Scale from './Scale';
+
+export default function Yoyo(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ peakValue?: number,
+ repeat?: number,
+ orientation?: number | string,
+ ease?: string,
+ scale?: Scale
+): Scale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.js
new file mode 100644
index 000000000..ad87f38ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/scale/Yoyo.js
@@ -0,0 +1,52 @@
+import Scale from './Scale.js';
+
+var Yoyo = function (gameObject, duration, peakValue, repeat, orientation, ease, scale) {
+ if (peakValue === undefined) {
+ peakValue = 1.2;
+ }
+ if (repeat === undefined) {
+ repeat = 0;
+ }
+ if (ease === undefined) {
+ ease = 'Cubic';
+ }
+
+ // Ease scale from 0 to current scale
+ var start, end;
+ switch (orientation) {
+ case 0:
+ case 'x':
+ start = { x: gameObject.scaleX };
+ end = { x: peakValue };
+ break;
+ case 1:
+ case 'y':
+ start = { y: gameObject.scaleX };
+ end = { y: peakValue };
+ break;
+ default:
+ start = gameObject.scaleX;
+ end = peakValue;
+ break;
+ }
+
+ var config = {
+ mode: 2,
+ start: start,
+ end: end,
+ duration: (duration / 2),
+ ease: ease,
+ repeat: repeat,
+ }
+
+ if (scale === undefined) {
+ scale = new Scale(gameObject, config);
+ } else {
+ scale.resetFromJSON(config);
+ }
+ scale.restart();
+
+ return scale;
+};
+
+export default Yoyo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.d.ts
new file mode 100644
index 000000000..b33fb17f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.d.ts
@@ -0,0 +1,58 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default ShakePosition;
+
+declare namespace ShakePosition {
+
+ type ModeType = 0 | 1 | 'effect' | 'behavior';
+ type MagnitudeModeType = 0 | 1 | 'constant' | 'decay';
+ type AixsModeType = 0 | 1 | 2 | 'both' | 'h&v' | 'horizontal' | 'h' | 'vertical' | 'v';
+
+ interface IConfig {
+ mode?: ModeType,
+ duration?: number,
+ magnitude?: number,
+ magnitudeMode?: MagnitudeModeType,
+ axis?: AixsModeType,
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ shake: ShakePosition
+ ) => void;
+ }
+
+}
+
+declare class ShakePosition extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ShakePosition.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ shake(duration?: number, magnitude?: number): this;
+ shake(config: {
+ duration?: number,
+ magnitude?: number,
+ }): this;
+
+ setMode(mode: ShakePosition.ModeType): this;
+ mode: number;
+
+ setDuration(duration: number): this;
+ duration: number;
+
+ setMagnitude(magnitude: number): this;
+ magnitude: number;
+
+ setMagnitudeMode(magnitudeMode: ShakePosition.MagnitudeModeType): this;
+ magnitudeMode: number;
+
+ setAxisMode(axisMode: ShakePosition.AixsModeType): this;
+ axisMode: number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.js
new file mode 100644
index 000000000..7d8e43833
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/shake/ShakePosition.js
@@ -0,0 +1,252 @@
+import TickTask from '../../utils/componentbase/TickTask.js';
+import Timer from '../../utils/componentbase/timerticktask/Timer.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class ShakePosition extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.timer = new Timer();
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.timer.resetFromJSON(GetValue(o, 'timer'));
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setMode(GetValue(o, 'mode', 1));
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setMagnitudeMode(GetValue(o, 'magnitudeMode', 1));
+ this.setAxisMode(GetValue(o, "axis", 0));
+ this.setDuration(GetValue(o, 'duration', 500));
+ this.setMagnitude(GetValue(o, 'magnitude', 10));
+ this.ox = GetValue(o, 'ox', undefined);
+ this.oy = GetValue(o, 'oy', undefined);
+ return this;
+ }
+
+ toJSON() {
+ return {
+ timer: this.timer.toJSON(),
+ enable: this.enable,
+ mode: this.mode,
+ isRunning: this.isRunning,
+ magnitudeMode: magnitudeMode,
+ duration: this.duration,
+ magnitude: this.magnitude,
+ ox: this.ox,
+ oy: this.oy,
+ };
+ }
+
+ // override
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ super.shutdown(fromScene);
+ this.timer.destroy();
+ this.timer = undefined;
+ }
+
+ startTicking() {
+ super.startTicking();
+
+ if (this.mode === 0) { // Effect mode
+ this.scene.game.events.on('poststep', this.update, this);
+ this.scene.game.events.on('prestep', this.backToOrigin, this);
+ } else { // Behavior Mode
+ this.scene.sys.events.on('preupdate', this.update, this);
+ }
+ }
+
+ stopTicking() {
+ super.stopTicking();
+
+ if (this.scene) { // Scene might be destoryed
+ if (this.mode === 0) { // Effect mode
+ this.scene.game.events.off('poststep', this.update, this);
+ this.scene.game.events.off('prestep', this.backToOrigin, this);
+ } else { // Behavior Mode
+ this.scene.sys.events.off('preupdate', this.update, this);
+ }
+
+ }
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ setMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = MODE[mode];
+ }
+ this.mode = mode;
+ return this;
+ }
+
+ setMagnitudeMode(magnitudeMode) {
+ if (typeof (magnitudeMode) === 'string') {
+ magnitudeMode = MANITUDEMODE[magnitudeMode];
+ }
+
+ this.magnitudeMode = magnitudeMode;
+ return this;
+ }
+
+ setAxisMode(m) {
+ if (typeof (m) === 'string') {
+ m = DIRECTIONNODE[m];
+ }
+ this.axisMode = m;
+ return this;
+ }
+
+ setDuration(duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ setMagnitude(magnitude) {
+ this.magnitude = magnitude;
+ return this;
+ }
+
+ start(duration, magnitude) {
+ if (typeof (duration) !== 'number') {
+ var config = duration;
+ magnitude = GetValue(config, 'magnitude', undefined);
+ duration = GetValue(config, 'duration', undefined);
+ }
+ if (magnitude !== undefined) {
+ this.setMagnitude(magnitude);
+ }
+ if (duration !== undefined) {
+ this.setDuration(duration);
+ }
+
+ this.timer
+ .setDuration(this.duration)
+ .start()
+
+ super.start();
+ return this;
+ }
+
+ shake(duration, magnitude) {
+ this.start(duration, magnitude);
+ return this;
+ }
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ this.timer.update(time, delta);
+ if (this.timer.isDone) {
+ this.backToOrigin();
+ this.complete();
+ } else {
+ if (this.ox === undefined) {
+ this.ox = gameObject.x;
+ this.oy = gameObject.y;
+ }
+
+ var magnitude = this.magnitude;
+ if (this.magnitudeMode === 1) // decay
+ {
+ magnitude *= (1 - this.timer.t);
+ }
+ var a = Math.random() * Math.PI * 2;
+ var x = this.ox + (Math.cos(a) * magnitude);
+ var y = this.oy + (Math.sin(a) * magnitude);
+
+ switch (this.axisMode) {
+ case 1:
+ gameObject.x = x;
+ break;
+
+ case 2:
+ gameObject.y = y;
+ break;
+
+ default:
+ gameObject.x = x;
+ gameObject.y = y;
+ break;
+ }
+ }
+
+ return this;
+ }
+
+ backToOrigin() {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ if (this.ox === undefined) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+
+ switch (this.axisMode) {
+ case 1:
+ gameObject.x = this.ox;
+ break;
+
+ case 2:
+ gameObject.y = this.oy;
+ break;
+
+ default:
+ gameObject.x = this.ox;
+ gameObject.y = this.oy;
+ break;
+ }
+
+ this.ox = undefined;
+ this.oy = undefined;
+ return this;
+ }
+}
+
+const MODE = {
+ effect: 0,
+ behavior: 1,
+}
+
+const DIRECTIONNODE = {
+ 'both': 0,
+ 'h&v': 0,
+ 'x&y': 0,
+ 'horizontal': 1,
+ 'h': 1,
+ 'x': 1,
+ 'vertical': 2,
+ 'v': 2,
+ 'y': 2
+};
+
+const MANITUDEMODE = {
+ constant: 0,
+ decay: 1,
+}
+
+export default ShakePosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.d.ts
new file mode 100644
index 000000000..26fd2b4a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.d.ts
@@ -0,0 +1,64 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Ship;
+
+declare namespace Ship {
+
+ type CursorKeys = {
+ up: Phaser.Input.Keyboard.Key,
+ down: Phaser.Input.Keyboard.Key,
+ left: Phaser.Input.Keyboard.Key,
+ right: Phaser.Input.Keyboard.Key
+ }
+
+ interface IConfig {
+
+ maxSpeed?: number,
+ acceleration?: number,
+ drag?: number,
+ turnSpeed?: number,
+ enable?: boolean,
+ wrap?: boolean,
+ padding?: number,
+ cursorKeys?: CursorKeys
+ }
+}
+
+declare class Ship extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Ship.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setCursorKeys(
+ cursorKeys: Ship.CursorKeys
+ ): this;
+ cursorKeys: Ship.CursorKeys;
+
+ setMaxSpeed(maxSpeed: number): this;
+ maxSpeed: number;
+
+ setAcceleration(acceleration: number): this;
+ acceleration: number;
+
+ setDrag(drag: number): this;
+ drag: number;
+
+ setTurnSpeed(angularVelocity: number): this;
+ angularVelocity: number;
+
+ setWrapMode(
+ wrap?: boolean,
+ padding?: number
+ ): this;
+ wrap: boolean;
+ padding: number;
+
+ readonly isLeft: boolean;
+ readonly isRight: boolean;
+ readonly isUp: boolean;
+ readonly isDown: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.js
new file mode 100644
index 000000000..12876d370
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/ship/Ship.js
@@ -0,0 +1,157 @@
+// https://labs.phaser.io/view.html?src=src\physics\arcade\asteroids%20movement.js
+
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import { SetAcceleration, SetAngularVelocity } from '../../utils/arcade/Helpers.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Ship extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ if (!this.parent.body) {
+ this.scene.physics.add.existing(this.parent, false);
+ }
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setMaxSpeed(GetValue(o, 'maxSpeed', 200));
+ this.setAcceleration(GetValue(o, 'acceleration', 200));
+ this.setDrag(GetValue(o, 'drag', 0.99));
+ this.setTurnSpeed(GetValue(o, 'turnSpeed', 300));
+ this.setWrapMode(GetValue(o, 'wrap', true), GetValue(o, 'padding', 0));
+ this.setCursorKeys(GetValue(o, 'cursorKeys', undefined));
+ return this;
+ }
+
+ get enable() {
+ return this.isRunning;
+ }
+
+ set enable(value) {
+ this.isRunning = value;
+ if (!value) {
+ SetAcceleration(this.parent, 0, 0);
+ SetAngularVelocity(this.parent, 0);
+ }
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ get maxSpeed() {
+ return this._maxSpeed;
+ }
+
+ set maxSpeed(value) {
+ this._maxSpeed = value;
+ var body = this.parent.body;
+ body.setMaxSpeed(value);
+ }
+
+ setMaxSpeed(speed) {
+ this.maxSpeed = speed;
+ return this;
+ }
+
+ setAcceleration(acceleration) {
+ this.acceleration = acceleration;
+ return this;
+ }
+
+ get drag() {
+ return this._drag;
+ }
+
+ set drag(value) {
+ this._drag = value;
+ var body = this.parent.body;
+ body.setDrag(value);
+ body.useDamping = true;
+ }
+
+ setDrag(drag) {
+ this.drag = drag;
+ return this;
+ }
+
+ setTurnSpeed(angularVelocity) {
+ this.angularVelocity = angularVelocity;
+ return this;
+ }
+
+ setWrapMode(wrap, padding) {
+ if (wrap === undefined) {
+ wrap = true;
+ }
+ this.wrap = wrap;
+ this.padding = padding;
+ return this;
+ }
+
+ setCursorKeys(cursorKeys) {
+ if (cursorKeys === undefined) {
+ cursorKeys = this.scene.input.keyboard.createCursorKeys();
+ }
+ this.cursorKeys = cursorKeys;
+ return this;
+ }
+
+ get isLeft() {
+ return (this.enable) ? this.cursorKeys.left.isDown : false;
+ }
+
+ get isRight() {
+ return (this.enable) ? this.cursorKeys.right.isDown : false;
+ }
+
+ get isUp() {
+ return (this.enable) ? this.cursorKeys.up.isDown : false;
+ }
+
+ get isDown() {
+ return (this.enable) ? this.cursorKeys.down.isDown : false;
+ }
+
+ update(time, delta) {
+ var gameObject = this.parent;
+ if (!this.enable) {
+ SetAcceleration(gameObject, 0, 0);
+ SetAngularVelocity(gameObject, 0);
+ return this;
+ }
+
+ if (!gameObject.active) {
+ return this;
+ }
+
+ // Speed up
+ if (this.isUp) {
+ var rotation = gameObject.rotation;
+ var ax = Math.cos(rotation) * this.acceleration;
+ var ay = Math.sin(rotation) * this.acceleration;
+ SetAcceleration(gameObject, ax, ay);
+ } else {
+ SetAcceleration(gameObject, 0, 0);
+ }
+
+ // Turn left/right
+ var dx = ((this.isLeft) ? -1 : 0) + ((this.isRight) ? 1 : 0);
+ SetAngularVelocity(gameObject, this.angularVelocity * dx);
+
+ if (this.wrap) {
+ gameObject.body.world.wrap(gameObject, this.padding);
+ }
+ }
+}
+
+export default Ship;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.d.ts
new file mode 100644
index 000000000..8f56fa872
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.d.ts
@@ -0,0 +1,26 @@
+import TickTask from '../../utils/componentbase/TickTask';
+
+export default Step;
+
+declare namespace Step {
+ interface IConfig {
+ enable?: boolean,
+ stepLength?: number,
+ }
+}
+
+declare class Step extends TickTask {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Step.IConfig
+ )
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setStepLength(stepLength: number): this;
+ stepLength: number;
+
+ cancelStep(): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.js
new file mode 100644
index 000000000..1ee2036b8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/step/Step.js
@@ -0,0 +1,139 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Step extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.setEnable(GetValue(o, 'enable', true));
+ this.setStepLength(GetValue(o, 'stepLength', 5));
+ }
+
+ toJSON() {
+ return {
+ enable: this.enable,
+
+ };
+ }
+
+ get enable() {
+ return this._enable;
+ }
+
+ set enable(value) {
+ value = !!value;
+ if (this._enable === value) {
+ return this;
+ }
+
+ this._enable = value;
+ this.isRunning = value;
+
+ if (value) {
+ var gameObject = this.parent;
+ this.preX = gameObject.x;
+ this.preY = gameObject.y;
+ }
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+
+ return this;
+ }
+
+ setStepLength(stepLength) {
+ this.stepLength = stepLength;
+ return this;
+ }
+
+ cancelStep() {
+ this.cancelStepFlag = true;
+ return this;
+ }
+
+ update(time, delta) {
+ if (!this.enable) {
+ return this;
+ }
+
+ var gameObject = this.parent;
+ if (!gameObject.active) {
+ return this;
+ }
+
+ var x0 = this.preX,
+ y0 = this.preY,
+ x1 = gameObject.x,
+ y1 = gameObject.y;
+
+ if ((x0 === x1) && (y0 === y1)) {
+ return this;
+ }
+
+ this.cancelStepFlag = false;
+
+ this.step(x0, y0, x1, y1, this.stepLength);
+
+ this.preX = x1;
+ this.preY = y1;
+ return this;
+ }
+
+ step(x0, y0, x1, y1, stepLength) {
+ if (this.cancelStepFlag) {
+ return this;
+ }
+
+ var dx = x1 - x0,
+ dy = y1 - y0;
+ var d = Math.sqrt(dx * dx + dy * dy);
+
+ var steps = Math.round(d / stepLength);
+ if (steps === 0) {
+ steps = 1;
+ }
+
+ var stepX = dx / steps,
+ stepY = dy / steps;
+ var xt, yt;
+ var gameObject = this.parent;
+ var points = [];
+ for (var i = 1; i <= steps; i++) {
+ xt = x0 + (stepX * i);
+ yt = y0 + (stepY * i);
+ points.push({ x: xt, y: yt });
+
+ this.emit('step', gameObject, this, xt, yt);
+
+ if (this.cancelStepFlag) {
+ break;
+ }
+ }
+
+ this.emit('steps', gameObject, this, points);
+
+ return this;
+ }
+
+}
+
+var StepMode = {
+ linear: 0,
+ 'x,y': 1,
+ 'h,v': 1,
+ 'y,x': 2,
+ 'v,h': 2
+}
+
+export default Step;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.d.ts
new file mode 100644
index 000000000..fed71a9c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.d.ts
@@ -0,0 +1,7 @@
+import TextEdit from './TextEdit';
+
+export default function Edit(
+ textObject: Phaser.GameObjects.GameObject,
+ config?: TextEdit.IConfigOpen,
+ onCloseCallback?: (textObject: Phaser.GameObjects.GameObject) => void
+): TextEdit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.js
new file mode 100644
index 000000000..5928b6d71
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/Edit.js
@@ -0,0 +1,13 @@
+import TextEdit from './TextEdit.js';
+
+var Edit = function (gameObject, config, onCloseCallback) {
+ if (!gameObject._edit) {
+ gameObject._edit = new TextEdit(gameObject, {
+ clickEnable: false
+ });
+ }
+ gameObject._edit.open(config, onCloseCallback);
+ return gameObject._edit;
+}
+
+export default Edit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.d.ts
new file mode 100644
index 000000000..9c816bb03
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.d.ts
@@ -0,0 +1,45 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import InputText from '../../inputtext';
+
+export default TextEdit;
+
+declare namespace TextEdit {
+ interface IConfigOpen {
+ type?: string,
+ enterClose?: boolean,
+ selectAll?: boolean,
+
+ onOpen?: (textObject: Phaser.GameObjects.GameObject) => void,
+ onTextChanged?: (textObject: Phaser.GameObjects.GameObject, text: string) => void,
+ onClose?: (textObject: Phaser.GameObjects.GameObject) => void,
+
+ text?: string,
+ fontFamily?: string,
+ fontSize?: string,
+ color?: string,
+ align?: string,
+ style?: { [name: string]: any },
+ }
+
+ interface IConfig extends IConfigOpen {
+ clickEnable?: boolean;
+ }
+}
+
+declare class TextEdit extends ComponentBase {
+ constructor(
+ textObject: Phaser.GameObjects.GameObject
+ );
+
+ open(
+ config?: TextEdit.IConfigOpen,
+ onCloseCallback?: (textObject: Phaser.GameObjects.GameObject) => void
+ ): this;
+
+ close(): this;
+
+ readonly isOpened: boolean;
+ readonly text: string;
+
+ readonly inputText: InputText;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.js
new file mode 100644
index 000000000..c9384bf97
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/TextEdit.js
@@ -0,0 +1,60 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import Methods from './methods/Methods.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TextEdit extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject);
+ // this.parent = gameObject;
+
+ this.inputText = undefined;
+ this.onClose = undefined;
+ this.delayCall = undefined;
+
+ this.setOpenConfig(config);
+
+ var clickEnable = GetValue(config, 'clickEnable', true);
+ if (clickEnable) {
+ gameObject
+ .on('pointerdown', function () {
+ this.open();
+ }, this)
+ .setInteractive()
+ }
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.close();
+
+ super.shutdown(fromScene);
+ }
+
+ setOpenConfig(config) {
+ if (config === undefined) {
+ config = {};
+ }
+ this.openConfig = config;
+ return this;
+ }
+
+ get isOpened() {
+ return (this.inputText !== undefined);
+ }
+
+ get text() {
+ return (this.isOpened) ? this.inputText.text : this.parent.text;
+ }
+}
+
+Object.assign(
+ TextEdit.prototype,
+ Methods,
+)
+
+export default TextEdit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Close.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Close.js
new file mode 100644
index 000000000..975b9f1b2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Close.js
@@ -0,0 +1,30 @@
+import { CloseLastOpenEditor } from './LastOpenedEditor.js';
+
+var Close = function () {
+ CloseLastOpenEditor(this);
+
+ this.parent.setVisible(true); // Set parent text visible
+
+ if (this.inputText) {
+ this.inputText.destroy();
+ this.inputText = undefined;
+ }
+
+ if (this.delayCall) {
+ this.delayCall.remove();
+ this.delayCall = undefined;
+ }
+
+ // Remove close event
+ this.scene.input.keyboard.off('keydown-ENTER', this.close, this);
+ this.scene.input.off('pointerdown', this.close, this);
+
+ if (this.onClose) {
+ this.onClose(this.parent);
+ }
+ this.emit('close', this.parent);
+
+ return this;
+}
+
+export default Close;
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/CreateInputText.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/CreateInputText.js
new file mode 100644
index 000000000..9d89211a6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/CreateInputText.js
@@ -0,0 +1,87 @@
+import InputText from '../../../gameobjects/dom/inputtext/InputText.js';
+import IsTextGameObject from '../../../utils/text/IsTextGameObject.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Clone = Phaser.Utils.Objects.Clone;
+
+var CreateInputText = function (text, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ config = Clone(config);
+
+ var scene = text.scene;
+ var style = text.style;
+ var backgroundColor = GetValue(config, 'backgroundColor', style.backgroundColor);
+ if (backgroundColor === null) {
+ backgroundColor = 'transparent';
+ }
+
+ config.text = GetValue(config, 'text', text.text);
+ config.fontFamily = GetValue(config, 'fontFamily', style.fontFamily);
+ config.fontSize = GetValue(config, 'fontSize', style.fontSize);
+ config.color = GetValue(config, 'color', style.color);
+ config.backgroundColor = backgroundColor;
+ config.direction = GetValue(config, 'rtl', style.rtl) ? 'rtl' : 'ltr';
+ config.align = GetValue(config, 'align', GetHAlign(style));
+
+ // Built-in text game object with RTL only has 'right' align
+ if ((config.direction === 'rtl') && (IsTextGameObject(text))) {
+ config.align = 'right';
+ }
+
+ // config.paddingLeft = 0;
+ // config.paddingRight = 0;
+ // config.paddingTop = 0;
+ // config.paddingBottom = 0;
+ // var valign = GetVAlign(style);
+ // switch (valign) {
+ // case 'top':
+ // break;
+ // case 'bottom':
+ // break;
+ // }
+
+ var inputText = new InputText(scene,
+ text.x, text.y,
+ GetValue(config, 'width', text.width),
+ GetValue(config, 'height', text.height),
+ config
+ );
+
+ inputText
+ // Sync origin
+ .setOrigin(text.originX, text.originY)
+ // Sync scrollFactor
+ .setScrollFactor(text.scrollFactorX, text.scrollFactorY)
+
+ var textParentContainer = text.parentContainer;
+ if (!textParentContainer) {
+ scene.add.existing(inputText);
+ } else {
+ textParentContainer.add(inputText);
+ }
+
+ return inputText;
+}
+
+var GetHAlign = function (style) {
+ if (style.hasOwnProperty('align')) {
+ return style.align;
+ } else if (style.hasOwnProperty('halign')) {
+ return style.halign;
+ } else {
+ return 'left';
+ }
+}
+
+var GetVAlign = function (style) {
+ if (style.hasOwnProperty('halign')) {
+ return style.halign;
+ } else {
+ return 'top';
+ }
+}
+
+
+export default CreateInputText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/LastOpenedEditor.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/LastOpenedEditor.js
new file mode 100644
index 000000000..ee0f6f509
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/LastOpenedEditor.js
@@ -0,0 +1,27 @@
+var LastOpenedEditor = undefined;
+
+var SetLastOpenedEditor = function (editor) {
+ if (editor === LastOpenedEditor) {
+ return;
+ }
+
+ if (LastOpenedEditor !== undefined) {
+ LastOpenedEditor.close();
+ }
+
+ LastOpenedEditor = editor;
+}
+
+var CloseLastOpenEditor = function (editor) {
+ if (editor !== LastOpenedEditor) {
+ return;
+ }
+
+ // Don't call `LastOpenedEditor.close()`
+ LastOpenedEditor = undefined;
+}
+
+export {
+ SetLastOpenedEditor,
+ CloseLastOpenEditor
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Methods.js
new file mode 100644
index 000000000..b25887976
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Methods.js
@@ -0,0 +1,9 @@
+import Open from './Open.js';
+import Close from './Close.js';
+
+var Methods = {
+ open: Open,
+ close: Close,
+};
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Open.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Open.js
new file mode 100644
index 000000000..7c2fd5e53
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textedit/methods/Open.js
@@ -0,0 +1,60 @@
+import { SetLastOpenedEditor } from './LastOpenedEditor.js';
+import IsFunction from '../../../utils/object/IsFunction.js';
+import CreateInputTextFromText from './CreateInputText.js';
+import NextTick from '../../../utils/time/NextTick.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Merge = Phaser.Utils.Objects.Merge;
+
+var Open = function (config, onCloseCallback) {
+ if (config === undefined) {
+ config = {};
+ }
+ config = Merge(config, this.openConfig)
+
+ SetLastOpenedEditor(this);
+
+ if (IsFunction(config)) {
+ onCloseCallback = config;
+ config = undefined;
+ }
+ if (onCloseCallback === undefined) {
+ onCloseCallback = GetValue(config, 'onClose', undefined);
+ }
+
+ var onOpenCallback = GetValue(config, 'onOpen', undefined);
+ var customOnTextChanged = GetValue(config, 'onTextChanged', undefined);
+
+ this.inputText = CreateInputTextFromText(this.parent, config)
+ .on('textchange', function (inputText) {
+ var text = inputText.text;
+ if (customOnTextChanged) { // Custom on-text-changed callback
+ customOnTextChanged(this.parent, text);
+ } else { // Default on-text-changed callback
+ this.parent.text = text;
+ }
+ }, this)
+ .setFocus();
+ this.parent.setVisible(false); // Set parent text invisible
+
+ // Attach close event
+ this.onClose = onCloseCallback;
+ if (GetValue(config, 'enterClose', true)) {
+ this.scene.input.keyboard.once('keydown-ENTER', this.close, this);
+ }
+ // Attach pointerdown (outside of input-text) event, at next tick
+ this.delayCall = NextTick(this.scene, function () {
+ this.scene.input.once('pointerdown', this.close, this);
+
+ // Open editor completly, invoke onOpenCallback
+ if (onOpenCallback) {
+ onOpenCallback(this.parent);
+ }
+ this.emit('open', this.parent);
+
+ }, this);
+
+ return this;
+}
+
+export default Open;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.d.ts
new file mode 100644
index 000000000..c088aff7b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.d.ts
@@ -0,0 +1,43 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default TextPage;
+
+declare namespace TextPage {
+ interface IConfig {
+ text?: string | string[],
+ maxLines?: number,
+ pageBreak?: string,
+ }
+}
+
+declare class TextPage extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: TextPage.IConfig
+ );
+
+ setMaxLines(maxLines: number): this;
+ maxLines: number;
+
+ setPageBreak(pageBreak?: string): this;
+ pageBreak: string;
+
+ setText(text: string | string[]): this;
+ appendText(text: string | string[]): this;
+ appendPage(text: string | string[]): this;
+ clearText(): this;
+
+ showPage(pageIndex?: number): this;
+ showNextPage(): this;
+ showPreviousPage(): this;
+
+ getPage(pageIndex?: number): string;
+ getNextPage(): string;
+ getPreviousPage(): string;
+
+ readonly isLastPage: boolean;
+ readonly isFirstPage: boolean;
+
+ readonly pageIndex: number;
+ readonly pageCount: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.js
new file mode 100644
index 000000000..936f2fd5c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/TextPage.js
@@ -0,0 +1,155 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import {
+ TextType, TagTextType, BitmapTextType
+} from '../../utils/text/GetTextObjectType.js';
+import GetTextObjectType from '../../utils/text/GetTextObjectType.js';
+import TextToLines from '../../utils/text/TextToLines.js';
+import Methods from './methods/Methods.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Clamp = Phaser.Math.Clamp;
+
+class TextPage extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this.textObjectType = GetTextObjectType(this.parent);
+
+ this.pageStartIndexes = [];
+
+ // Text object : array of string
+ // Tag text object : pens-manager
+ // Bitmap text object : array of string
+ this.lines = TextToLines(this.parent, '');
+
+ this.sections = [];
+
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.setMaxLines(GetValue(o, 'maxLines', undefined));
+ this.setPageBreak(GetValue(o, 'pageBreak', '\f\n'));
+ this.setText(GetValue(o, 'text', ''));
+ this.setStartLineIndex(GetValue(o, 'start', 0));
+ this.setPageIndex(GetValue(o, 'page', -1));
+ return this;
+ }
+
+ toJSON() {
+ return {
+ maxLines: this.maxLines,
+ text: this.content,
+ start: this.startLineIndex,
+ page: this.pageIndex,
+ pageBreak: this.pageBreak
+ };
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ switch (this.textObjectType) {
+ case TextType:
+ this.lines.length = 0;
+ break;
+ case TagTextType:
+ this.lines.destroy();
+ break;
+ case BitmapTextType:
+ this.lines.length = 0;
+ break;
+ }
+
+ this.pageStartIndexes.length = 0;
+ this.sections.length = 0;
+
+ this.lines = undefined;
+ this.pageStartIndexes = undefined;
+ this.sections = undefined;
+
+ super.shutdown(fromScene);
+ }
+
+ setMaxLines(maxLines) {
+ this.maxLines = maxLines;
+ return this;
+ }
+
+ setPageBreak(pageBreak) {
+ this.pageBreak = pageBreak;
+ return this;
+ }
+
+ get pageCount() {
+ return this.pageStartIndexes.length;
+ }
+
+ get isFirstPage() {
+ return (this.pageIndex <= 0);
+ }
+
+ get isLastPage() {
+ return (this.pageIndex >= (this.pageCount - 1));
+ }
+
+ get totalLinesCount() {
+ return (this.lines) ? this.lines.length : 0;
+ }
+
+ get startLineIndex() {
+ return this._startLineIndex;
+ }
+
+ set startLineIndex(value) {
+ value = Clamp(value, 0, this.totalLinesCount - 1);
+ this._startLineIndex = value;
+ }
+
+ setStartLineIndex(idx) {
+ this.startLineIndex = idx;
+ return this;
+ }
+
+ get pageLinesCount() {
+ if (this.maxLines !== undefined) {
+ return this.maxLines;
+
+ } else {
+ var count;
+ switch (this.textObjectType) {
+ case TextType:
+ case TagTextType:
+ var maxLines = this.parent.style.maxLines;
+ if (maxLines > 0) {
+ count = maxLines;
+ } else {
+ count = this.totalLinesCount;
+ }
+ break;
+ case BitmapTextType:
+ count = this.totalLinesCount;
+ break;
+ }
+ return count;
+
+ }
+ }
+
+ get content() {
+ return this.sections.join(this.pageBreak);
+ }
+}
+
+Object.assign(
+ TextPage.prototype,
+ Methods,
+);
+
+
+export default TextPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetLines.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetLines.js
new file mode 100644
index 000000000..9048fb2f3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetLines.js
@@ -0,0 +1,29 @@
+import {
+ TextType, TagTextType, BitmapTextType
+} from '../../../utils/text/GetTextObjectType.js';
+
+var GetLines = function (startLineIndex, endLineIdx) {
+ if (startLineIndex === undefined) {
+ startLineIndex = this.startLineIndex;
+ }
+ if (endLineIdx === undefined) {
+ endLineIdx = startLineIndex + this.pageLinesCount;
+ }
+
+ var text;
+ switch (this.textObjectType) {
+ case TextType:
+ case BitmapTextType:
+ text = this.lines.slice(startLineIndex, endLineIdx).join('\n');
+ break;
+ case TagTextType:
+ var startIdx = this.lines.getLineStartIndex(startLineIndex);
+ var endIdx = this.lines.getLineEndIndex(endLineIdx - 1);
+ text = this.lines.getSliceTagText(startIdx, endIdx, true);
+ break;
+ }
+
+ return text;
+}
+
+export default GetLines;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetPageMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetPageMethods.js
new file mode 100644
index 000000000..73d9e01f7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/GetPageMethods.js
@@ -0,0 +1,32 @@
+const Clamp = Phaser.Math.Clamp;
+
+export default {
+ getPage(idx) {
+ if (idx === undefined) {
+ idx = this.pageIndex;
+ }
+
+ return this.setPageIndex(idx).getLines(this.startLineIndex, this.endLineIndex);
+ },
+
+ getNextPage() {
+ return this.getPage(this.pageIndex + 1);
+ },
+
+ getPreviousPage() {
+ return this.getPage(this.pageIndex - 1);
+ },
+
+ resetPageIdx() {
+ this.pageIndex = -1;
+ return this;
+ },
+
+ setPageIndex(idx) {
+ idx = Clamp(idx, 0, this.pageCount - 1);
+ this.pageIndex = idx;
+ this.startLineIndex = this.pageStartIndexes[idx];
+ this.endLineIndex = this.pageStartIndexes[idx + 1];
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/Methods.js
new file mode 100644
index 000000000..d5aeb5238
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/Methods.js
@@ -0,0 +1,17 @@
+import GetLines from './GetLines.js';
+import SetContentMethods from './SetContentMethods.js';
+import GetPageMethods from './GetPageMethods.js';
+import ShowMethods from './ShowMethods.js';
+
+var Methods = {
+ getLines: GetLines,
+}
+
+Object.assign(
+ Methods,
+ SetContentMethods,
+ GetPageMethods,
+ ShowMethods
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/SetContentMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/SetContentMethods.js
new file mode 100644
index 000000000..921d56b37
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/SetContentMethods.js
@@ -0,0 +1,69 @@
+import TextToLines from "../../../utils/text/TextToLines.js";
+
+var GetString = function (text) {
+ if (Array.isArray(text)) {
+ text = text.join('\n');
+ } else if (typeof (text) === 'number') {
+ text = text.toString();
+ }
+ return text;
+}
+
+export default {
+ clearText() {
+ this.sections.length = 0;
+ this.pageStartIndexes.length = 0;
+ this.lines.length = 0;
+
+ return this;
+ },
+
+ appendPage(text) {
+ var pageStartIndex = this.totalLinesCount;
+
+ this.sections.push(GetString(text));
+ var text = this.sections.join('\n');
+ this.lines = TextToLines(this.parent, text, this.lines);
+
+ var newLinesCount = this.totalLinesCount - pageStartIndex;
+ var pageCount = Math.ceil(newLinesCount / this.pageLinesCount);
+ for (var i = 0; i < pageCount; i++) {
+ this.pageStartIndexes.push(
+ pageStartIndex + (i * this.pageLinesCount)
+ );
+ }
+
+ return this;
+ },
+
+ setText(text, resetPageIdx) {
+ if (resetPageIdx === undefined) {
+ resetPageIdx = true;
+ }
+
+ if (resetPageIdx) {
+ this.resetPageIdx();
+ }
+
+ this.clearText();
+
+ var sections = GetString(text).split(this.pageBreak);
+ // if (sections[sections.length - 1] === '') { // Last section is an empty string
+ // sections.length -= 1;
+ // }
+
+ for (var i = 0, cnt = sections.length; i < cnt; i++) {
+ this.appendPage(sections[i]);
+ }
+
+ return this;
+ },
+
+ appendText(text) {
+ var content = this.content + GetString(text);
+ this.setText(content, false);
+ return this;
+ },
+
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/ShowMethods.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/ShowMethods.js
new file mode 100644
index 000000000..3fa07eeb0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/textpage/methods/ShowMethods.js
@@ -0,0 +1,49 @@
+import SetNoWrapText from '../../../utils/text/SetNoWrapText.js';
+
+export default {
+ showPage(idx) {
+ this.displayText(
+ this.getPage(idx)
+ );
+ return this;
+ },
+
+ showNextPage() {
+ this.displayText(
+ this.getNextPage()
+ );
+ return this;
+ },
+
+ showPreviousPage() {
+ this.displayText(
+ this.getPreviousPage()
+ );
+ return this;
+ },
+
+ show() {
+ this.displayText(
+ this.getLines()
+ );
+ return this;
+ },
+
+ showNextLine() {
+ this.displayText(
+ this.setStartLineIndex(this.startLineIndex + 1).getLines()
+ );
+ return this;
+ },
+
+ showPreviousLine() {
+ this.displayText(
+ this.setStartLineIndex(this.startLineIndex - 1).getLines()
+ );
+ return this;
+ },
+
+ displayText(text) {
+ SetNoWrapText(this.parent, text);
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.d.ts
new file mode 100644
index 000000000..98d6b2633
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.d.ts
@@ -0,0 +1,36 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default TextTranslation;
+
+declare namespace TextTranslation {
+ type SetTextCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ text: string
+ ) => void;
+
+ type InterpolationsType = { [name: string]: any };
+
+ interface IConfig {
+ translationKey?: string,
+ interpolation?: InterpolationsType,
+ updateText?: boolean,
+ setText?: SetTextCallbackType,
+ }
+}
+
+declare class TextTranslation extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: TextTranslation.IConfig
+ );
+
+ setInterpolation(interpolation: TextTranslation.InterpolationsType): this;
+ updateInterpolation(key: string, value: any): this;
+ updateInterpolation(interpolation: TextTranslation.InterpolationsType): this;
+ interpolation: TextTranslation.InterpolationsType;
+
+ setTranslationKey(key: string): this;
+ translationKey: string;
+
+ updateText(): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.js
new file mode 100644
index 000000000..c00f5d4dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttranslation/TextTranslation.js
@@ -0,0 +1,86 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+
+var i18next;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TextTranslation extends ComponentBase {
+ static setI18Next(obj) {
+ i18next = obj;
+ }
+
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.resetFromJSON(config);
+
+ this.onLanguageChanged = this.updateText.bind(this);
+ i18next.on('languageChanged', this.onLanguageChanged);
+ }
+
+ resetFromJSON(o) {
+ this.setSetTextCallback(GetValue(o, 'setText', DefaultSetTextCallback));
+ this.setInterpolation(GetValue(o, 'interpolation'));
+ this.setTranslationKey(GetValue(o, 'translationKey', ''));
+ if (GetValue(o, 'updateText', true)) {
+ this.updateText();
+ }
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ i18next.off('languageChanged', this.onLanguageChanged);
+ this.interpolation = null;
+
+ super.shutdown(fromScene);
+ }
+
+ setSetTextCallback(callback) {
+ this.setTextCallback = callback;
+ return this;
+ }
+
+ setInterpolation(interpolation) {
+ this.interpolation = interpolation;
+ return this;
+ }
+
+ updateInterpolation(key, value) {
+ if (!this.interpolation) {
+ this.interpolation = {};
+ }
+
+ if (typeof (key) === 'string') {
+ this.interpolation[key] = value;
+ } else {
+ var data = key;
+ for (key in data) {
+ this.interpolation[key] = data[key];
+ }
+ }
+ return this;
+ }
+
+ setTranslationKey(key) {
+ this.translationKey = key;
+ return this;
+ }
+
+ updateText() {
+ var text = i18next.t(this.translationKey, this.interpolation);
+ this.setTextCallback(this.parent, text);
+ return this;
+ }
+
+}
+
+var DefaultSetTextCallback = function (gameObject, text) {
+ gameObject.setText(text);
+}
+
+export default TextTranslation;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.d.ts
new file mode 100644
index 000000000..c87d18406
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.d.ts
@@ -0,0 +1,44 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+
+export default TextTyping;
+
+declare namespace TextTyping {
+
+ type TypeModeType = 0 | 1 | 2 | 3 | 'left-to-right' | 'right-to-left' | 'middle-to-sides' | 'sides-to-middle';
+ type SetTextCallbackType = (text: string, isLastChar: boolean, insertIdx: number) => string;
+
+ interface IConfig {
+ speed?: number,
+ typeMode?: TypeModeType,
+ setTextCallback?: SetTextCallbackType,
+ setTextCallbackScope?: Object
+ }
+
+ namespace Events {
+ type TypingCallbackType = () => void;
+ type TypingCompleteCallbackType = (typing: TextTyping, txt: string) => void;
+ }
+}
+
+declare class TextTyping extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: TextTyping.IConfig
+ );
+
+ start(content: string, speed?: number): this;
+ appendText(content: string): this;
+ stop(showAllText?: boolean): this;
+
+ pause(): this;
+ resume(): this;
+
+ setTypeSpeed(speed: number): this;
+ setTypingSpeed(speed: number): this;
+ speed: number;
+
+ setTypeMode(mode: TextTyping.TypeModeType): this;
+ typeMode: number;
+
+ readonly isTyping: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.js
new file mode 100644
index 000000000..84e7a8f36
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/texttyping/TextTyping.js
@@ -0,0 +1,325 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import GetWrapText from '../../utils/text/GetWrapText.js';
+import SetNoWrapText from '../../utils/text/SetNoWrapText.js';
+
+const GetFastValue = Phaser.Utils.Objects.GetFastValue;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TextTyping extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.timer = null;
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.setTextWrapEnable(GetValue(o, 'wrap', false));
+ this.setTypeMode(GetValue(o, 'typeMode', 0));
+ this.setTypingSpeed(GetValue(o, 'speed', 333));
+ this.setTextCallback = GetFastValue(o, 'setTextCallback', null);
+ this.setTextCallbackScope = GetFastValue(o, 'setTextCallbackScope', null);
+
+ this.setTypingContent(GetFastValue(o, 'text', ''));
+ this.typingIdx = GetFastValue(o, 'typingIdx', 0);
+ this.insertIdx = GetFastValue(o, 'insertIdx', null);
+
+ var elapsed = GetFastValue(o, 'elapsed', null);
+ if (elapsed !== null) {
+ this.start(undefined, undefined, this.typingIdx, elapsed);
+ }
+
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.freeTimer();
+
+ super.shutdown(fromScene);
+ }
+
+ setTypeMode(m) {
+ if (typeof (m) === 'string') {
+ m = TYPEMODE[m];
+ }
+ this.typeMode = m;
+ return this;
+ }
+
+ setTypeSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ setTypingSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ setTextWrapEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.textWrapEnable = enable;
+ return this;
+ }
+
+ set text(value) {
+ var text = TransferText(value);
+ if (this.textWrapEnable) {
+ text = GetWrapText(this.parent, text);
+ }
+
+ this._text = text;
+ }
+
+ get text() {
+ return this._text;
+ }
+
+ get isTyping() {
+ return (this.getTimer() !== null);
+ }
+
+ get isLastChar() {
+ return (this.typingIdx === this.textLen);
+ }
+
+ start(text, speed, startIdx, timerStartAt) {
+ if (text !== undefined) {
+ this.setTypingContent(text);
+ }
+ if (speed !== undefined) {
+ this.speed = speed;
+ }
+ if (startIdx === undefined) {
+ startIdx = 0;
+ }
+
+ this.typingIdx = startIdx + 1;
+ if (this.speed === 0) {
+ this.stop(true);
+ } else {
+ this.setText('');
+ this.startTimer(timerStartAt);
+ }
+
+ return this;
+ }
+
+ appendText(text) {
+ var newText = this.text.concat(TransferText(text));
+ if (this.isTyping) {
+ this.setTypingContent(newText);
+ } else {
+ this.start(newText, undefined, this.textLen);
+ }
+
+ return this;
+ }
+
+ stop(showAllText) {
+ var timer = this.getTimer();
+ if (timer) {
+ this.freeTimer();
+ }
+ if (showAllText) {
+ this.typingIdx = this.textLen;
+ this.setText(this.text);
+ this.emit('type');
+ this.emit('complete', this, this.parent);
+ }
+
+ return this;
+ }
+
+ pause() {
+ var timer = this.getTimer();
+ if (timer) {
+ timer.paused = true;
+ }
+ return this;
+ }
+
+ resume() {
+ var timer = this.getTimer();
+ if (timer) {
+ timer.paused = false;
+ }
+ return this;
+ }
+
+ setTypingContent(text) {
+ this.text = text;
+ this.textLen = this.getTextLength(this.text);
+ return this;
+ }
+
+ onTyping() {
+ var newText = this.getTypingString(this.text, this.typingIdx, this.textLen, this.typeMode);
+
+ this.setText(newText);
+
+ this.emit('type');
+
+ if (this.isLastChar) {
+ this.freeTimer();
+ this.emit('complete', this, this.parent);
+ } else {
+ this.timer.delay = this.speed; // delay of next typing
+ this.typingIdx++;
+ }
+ }
+
+ getTypingString(text, typeIdx, textLen, typeMode) {
+ var result;
+ if (typeMode === 0) { //left-to-right
+ var startIdx = 0;
+ var endIdx = typeIdx;
+ this.insertIdx = endIdx;
+ result = this.getSubString(text, startIdx, endIdx);
+
+ } else if (typeMode === 1) { //right-to-left
+ var endIdx = textLen;
+ var startIdx = endIdx - typeIdx;
+ this.insertIdx = 0;
+ result = this.getSubString(text, startIdx, endIdx);
+
+ } else if (typeMode === 2) { //middle-to-sides
+ var midIdx = textLen / 2;
+ var startIdx = Math.floor(midIdx - (typeIdx / 2));
+ var endIdx = startIdx + typeIdx;
+ this.insertIdx = (typeIdx % 2) ? typeIdx : 0;
+ result = this.getSubString(text, startIdx, endIdx);
+
+ } else if (typeMode === 3) { //sides-to-middle
+ var lowerLen = Math.floor(typeIdx / 2);
+ var lowerResult;
+ if (lowerLen > 0) {
+ var endIdx = textLen;
+ var startIdx = endIdx - lowerLen;
+ lowerResult = this.getSubString(text, startIdx, endIdx);
+ } else {
+ lowerResult = "";
+ }
+
+ var upperLen = typeIdx - lowerLen;
+ var upperResult;
+ if (upperLen > 0) {
+ var startIdx = 0;
+ var endIdx = startIdx + upperLen;
+ this.insertIdx = endIdx;
+ upperResult = this.getSubString(text, startIdx, endIdx);
+ } else {
+ upperResult = "";
+ this.insertIdx = 0;
+ }
+ result = upperResult + lowerResult;
+ }
+
+ return result;
+ }
+
+ startTimer(timerStartAt) {
+ if (this.timer) {
+ this.freeTimer();
+ }
+ var delay, startAt;
+ if (timerStartAt === undefined) {
+ delay = 0;
+ startAt = 0;
+ } else {
+ delay = this.speed;
+ startAt = timerStartAt;
+ }
+
+ this.timer = this.scene.time.addEvent({
+ delay: 0.0001,
+ startAt: startAt,
+ loop: true,
+ callback: this.onTyping,
+ callbackScope: this
+ });
+ // Note: Throw error message if delay is 0 with repeat/loop
+
+ return this;
+ }
+
+ getTimer() {
+ return this.timer;
+ }
+
+ freeTimer() {
+ if (this.timer) {
+ this.timer.remove();
+ this.timer = null;
+ }
+
+ return this;
+ }
+
+ setText(text) {
+ if (this.setTextCallback) {
+ if (this.setTextCallbackScope) {
+ text = this.setTextCallback.call(this.setTextCallbackScope, text, this.isLastChar, this.insertIdx);
+ } else {
+ text = this.setTextCallback(text, this.isLastChar, this.insertIdx);
+ }
+ }
+
+ if (this.textWrapEnable) {
+ SetNoWrapText(this.parent, text);
+ } else {
+ this.parent.setText(text);
+ }
+ }
+
+ getTextLength(text) {
+ var gameObject = this.parent;
+ var len;
+ if (gameObject.getPlainText) {
+ len = gameObject.getPlainText(text).length;
+ } else {
+ len = text.length;
+ }
+
+ return len;
+ }
+
+ getSubString(text, startIdx, endIdx) {
+ var gameObject = this.parent;
+ var result;
+ if (gameObject.getSubString) {
+ result = gameObject.getSubString(text, startIdx, endIdx);
+ } else {
+ result = text.slice(startIdx, endIdx);
+ }
+
+ return result;
+ }
+}
+
+var TransferText = function (text) {
+ if (Array.isArray(text)) {
+ text = text.join('\n');
+ } else if (typeof (text) === 'number') {
+ text = text.toString();
+ }
+ return text;
+}
+
+const TYPEMODE = {
+ 'left-to-right': 0,
+ 'right-to-left': 1,
+ 'middle-to-sides': 2,
+ 'sides-to-middle': 3
+};
+
+
+export default TextTyping;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.d.ts
new file mode 100644
index 000000000..3f2120060
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.d.ts
@@ -0,0 +1,15 @@
+export default AddTintRGBProperties;
+
+declare namespace AddTintRGBProperties {
+ interface TintRGBGameObject extends Phaser.GameObjects.GameObject {
+ tintR: number;
+ tintG: number;
+ tintB: number;
+ tintGray: number;
+ }
+}
+
+declare function AddTintRGBProperties(
+ gameObject: Phaser.GameObjects.GameObject,
+ colorRGB?: number
+): AddTintRGBProperties.TintRGBGameObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.js
new file mode 100644
index 000000000..f51f4d0e8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/tintrgb/AddTintRGBProperties.js
@@ -0,0 +1,94 @@
+import { GetR, GetG, GetB } from '../../utils/color/GetRGB.js';
+import { SetR, SetG, SetB, SetRGB } from '../../utils/color/SetColor.js';
+
+var AddTintRGBProperties = function (gameObject, tintRGB) {
+ // Don't attach properties again
+ if (gameObject.hasOwnProperty('tintR')) {
+ return gameObject;
+ }
+
+ if (tintRGB === undefined) {
+ tintRGB = 0xffffff;
+ }
+
+ var tintR = GetR(tintRGB);
+ var tintG = GetG(tintRGB);
+ var tintB = GetB(tintRGB);
+
+ // Override tint property
+ Object.defineProperty(gameObject, 'tint', {
+ get: function () {
+ return tintRGB;
+ },
+ set: function (value) {
+ value = Math.floor(value) & 0xffffff;
+ if (gameObject.setTint) {
+ gameObject.setTint(value);
+ }
+ if (tintRGB !== value) {
+ tintRGB = value;
+ tintR = GetR(tintRGB);
+ tintG = GetG(tintRGB);
+ tintB = GetB(tintRGB);
+ // gameObject.emit('_tintchange', value, tintR, tintG, tintB);
+ }
+ }
+ });
+
+ Object.defineProperty(gameObject, 'tintR', {
+ get: function () {
+ return tintR;
+ },
+ set: function (value) {
+ value = Math.floor(value) & 0xff;
+ if (tintR !== value) {
+ tintR = value;
+ gameObject.tint = SetR(tintRGB, value);
+ }
+ },
+ })
+ Object.defineProperty(gameObject, 'tintG', {
+ get: function () {
+ return tintG;
+ },
+ set: function (value) {
+ value = Math.floor(value) & 0xff;
+ if (tintG !== value) {
+ tintG = value;
+ gameObject.tint = SetG(tintRGB, value);
+ }
+ },
+ })
+ Object.defineProperty(gameObject, 'tintB', {
+ get: function () {
+ return tintB;
+ },
+ set: function (value) {
+ value = Math.floor(value) & 0xff;
+ if (tintB !== value) {
+ tintB = value;
+ gameObject.tint = SetB(tintRGB, value);
+ }
+ },
+ })
+ Object.defineProperty(gameObject, 'tintGray', {
+ get: function () {
+ return Math.floor((tintR + tintG + tintB) / 3);
+ },
+ set: function (value) {
+ value = Math.floor(value) & 0xff;
+ if ((tintR !== value) || (tintG !== value) || (tintB !== value)) {
+ tintR = value;
+ tintG = value;
+ tintB = value;
+ gameObject.tint = SetRGB(tintRGB, value, value, value);
+ }
+ },
+ })
+
+ gameObject.tint = tintRGB;
+
+ return gameObject;
+}
+
+export default AddTintRGBProperties;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.d.ts
new file mode 100644
index 000000000..633ac4048
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.d.ts
@@ -0,0 +1,55 @@
+export default AddViewportCoordinateProperties;
+
+declare namespace AddViewportCoordinateProperties {
+ interface PolarCoordinateGameObject extends Phaser.GameObjects.GameObject {
+ vp: Phaser.Geom.Rectangle;
+ vpx: number;
+ vpy: number;
+ vpxOffset: number;
+ vpyOffset: number;
+ }
+
+ type TransformCallbackType0 =
+ (
+ vpx: number,
+ vpy: number,
+ viewport: Phaser.Geom.Rectangle,
+ gameObject: Phaser.GameObjects.GameObject,
+ ) => void;
+
+ type TransformCallbackType1 =
+ (
+ vpx: number,
+ vpy: number,
+ vpxOffset: number,
+ vpyOffset: number,
+ viewport: Phaser.Geom.Rectangle,
+ gameObject: Phaser.GameObjects.GameObject,
+ ) => void;
+
+ type TransformCallbackType = TransformCallbackType0 | TransformCallbackType1;
+}
+
+declare function AddViewportCoordinateProperties(
+ gameObject: Phaser.GameObjects.GameObject,
+ viewport?: Phaser.Geom.Rectangle,
+ vpx?: number,
+ vpy?: number,
+ vpxOffset?: number,
+ vpyOffset?: number,
+ transformCallback?: AddViewportCoordinateProperties.TransformCallbackType
+): AddViewportCoordinateProperties.PolarCoordinateGameObject;
+
+declare function AddViewportCoordinateProperties(
+ gameObject: Phaser.GameObjects.GameObject,
+ viewport?: Phaser.Geom.Rectangle,
+ vpx?: number,
+ vpy?: number,
+ transformCallback?: AddViewportCoordinateProperties.TransformCallbackType
+): AddViewportCoordinateProperties.PolarCoordinateGameObject;
+
+declare function AddViewportCoordinateProperties(
+ gameObject: Phaser.GameObjects.GameObject,
+ viewport?: Phaser.Geom.Rectangle,
+ transformCallback?: AddViewportCoordinateProperties.TransformCallbackType
+): AddViewportCoordinateProperties.PolarCoordinateGameObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.js
new file mode 100644
index 000000000..7fbc23587
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/AddViewportCoordinateProperties.js
@@ -0,0 +1,97 @@
+import MonitorViewport from './MonitorViewport.js';
+import VPXYToXY from './VPXYToXY.js';
+
+var AddViewportCoordinateProperties = function (gameObject, viewport, vpx, vpy, vpxOffset, vpyOffset, transformCallback) {
+ // Don't attach properties again
+ if (gameObject.hasOwnProperty('vp')) {
+ return gameObject;
+ }
+
+ if (typeof (vpx) === 'function') {
+ transformCallback = vpx;
+ vpx = undefined;
+ }
+
+ if (typeof (vpxOffset) === 'function') {
+ transformCallback = vpxOffset;
+ vpxOffset = undefined;
+ }
+
+
+ if (vpx === undefined) { vpx = 0.5; }
+ if (vpy === undefined) { vpy = 0.5; }
+ if (vpxOffset === undefined) { vpxOffset = 0; }
+ if (vpyOffset === undefined) { vpyOffset = 0; }
+
+ if (transformCallback === undefined) {
+ transformCallback = VPXYToXY;
+ }
+
+ MonitorViewport(viewport);
+ var events = viewport.events;
+
+ gameObject.vp = viewport;
+
+ // Set position of game object when view-port changed.
+ var Transform = function () {
+ transformCallback(vpx, vpy, vpxOffset, vpyOffset, viewport, gameObject);
+ }
+
+ events.on('update', Transform);
+ gameObject.once('destroy', function () {
+ events.off('update', Transform);
+ gameObject.vp = undefined;
+ })
+
+ Object.defineProperty(gameObject, 'vpx', {
+ get: function () {
+ return vpx;
+ },
+ set: function (value) {
+ if (vpx !== value) {
+ vpx = value;
+ Transform();
+ }
+ },
+ });
+
+ Object.defineProperty(gameObject, 'vpy', {
+ get: function () {
+ return vpy;
+ },
+ set: function (value) {
+ if (vpy !== value) {
+ vpy = value;
+ Transform();
+ }
+ },
+ });
+
+ Object.defineProperty(gameObject, 'vpxOffset', {
+ get: function () {
+ return vpxOffset;
+ },
+ set: function (value) {
+ if (vpxOffset !== value) {
+ vpxOffset = value;
+ Transform();
+ }
+ },
+ });
+
+ Object.defineProperty(gameObject, 'vpyOffset', {
+ get: function () {
+ return vpyOffset;
+ },
+ set: function (value) {
+ if (vpyOffset !== value) {
+ vpyOffset = value;
+ Transform();
+ }
+ },
+ });
+
+ Transform();
+}
+
+export default AddViewportCoordinateProperties;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/MonitorViewport.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/MonitorViewport.js
new file mode 100644
index 000000000..2f9331286
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/MonitorViewport.js
@@ -0,0 +1,71 @@
+const EventEmitter = Phaser.Events.EventEmitter;
+
+var MonitorViewport = function (viewport) {
+ if (viewport.events) {
+ return viewport;
+ }
+
+ var events = new EventEmitter();
+
+ var x = viewport.x;
+ Object.defineProperty(viewport, 'x', {
+ get: function () {
+ return x;
+ },
+
+ set: function (value) {
+ if (x !== value) {
+ x = value;
+ events.emit('update', viewport);
+ }
+ },
+ });
+
+ var y = viewport.y;
+ Object.defineProperty(viewport, 'y', {
+ get: function () {
+ return y;
+ },
+
+ set: function (value) {
+ if (y !== value) {
+ y = value;
+ events.emit('update', viewport);
+ }
+ },
+ });
+
+ var width = viewport.width;
+ Object.defineProperty(viewport, 'width', {
+ get: function () {
+ return width;
+ },
+
+ set: function (value) {
+ if (width !== value) {
+ width = value;
+ events.emit('update', viewport);
+ }
+ },
+ });
+
+ var height = viewport.height;
+ Object.defineProperty(viewport, 'height', {
+ get: function () {
+ return height;
+ },
+
+ set: function (value) {
+ if (height !== value) {
+ height = value;
+ events.emit('update', viewport);
+ }
+ },
+ });
+
+ viewport.events = events;
+
+ return viewport;
+}
+
+export default MonitorViewport;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.d.ts b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.d.ts
new file mode 100644
index 000000000..c2a2ea7f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.d.ts
@@ -0,0 +1,17 @@
+export default VPXYToXY;
+
+declare function VPXYToXY(
+ vpx: number,
+ vpy: number,
+ vpxOffset: number,
+ vpyOffset: number,
+ viewport: Phaser.Geom.Rectangle,
+ out?: Phaser.Math.Vector2,
+): Phaser.Math.Vector2;
+
+declare function VPXYToXY(
+ vpx: number,
+ vpy: number,
+ viewport: Phaser.Geom.Rectangle,
+ out?: Phaser.Math.Vector2,
+): Phaser.Math.Vector2;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.js b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.js
new file mode 100644
index 000000000..d884976e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviors/viewportcoordinate/VPXYToXY.js
@@ -0,0 +1,20 @@
+var VPXYToXY = function (vpx, vpy, vpxOffset, vpyOffset, viewport, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = GlobXY;
+ }
+
+ if (typeof (vpxOffset) !== 'number') {
+ vpxOffset = 0;
+ vpyOffset = 0;
+ }
+
+ out.x = viewport.x + (viewport.width * vpx) + vpxOffset;
+ out.y = viewport.y + (viewport.height * vpy) + vpyOffset;
+ return out;
+}
+
+var GlobXY = {};
+
+export default VPXYToXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/behaviortree-plugin.js b/ui/src/phaser3-rex-plugins/plugins/behaviortree-plugin.js
new file mode 100644
index 000000000..f350a8e63
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/behaviortree-plugin.js
@@ -0,0 +1,17 @@
+import ObjectFactory from './logic/behaviortree/ObjectFactory.js';
+import Factory from './logic/behaviortree/Factory.js';
+
+class BehaviorTreePlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ this.add = new ObjectFactory();
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+export default BehaviorTreePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.d.ts
new file mode 100644
index 000000000..bae23bd84
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.d.ts
@@ -0,0 +1,9 @@
+import BitmapZone from './bitmapzone';
+
+export default class BitmapZonePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: BitmapZone.IConfig
+ ): BitmapZone;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.js
new file mode 100644
index 000000000..765bc9706
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bitmapzone-plugin.js
@@ -0,0 +1,19 @@
+import BitmapZone from './bitmapzone.js';
+
+class BitmapZonePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(source, config) {
+ return new BitmapZone(source, config);
+ }
+}
+
+export default BitmapZonePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bitmapzone.d.ts b/ui/src/phaser3-rex-plugins/plugins/bitmapzone.d.ts
new file mode 100644
index 000000000..7ffab78f9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bitmapzone.d.ts
@@ -0,0 +1,2 @@
+import BitmapZone from './behaviors/bitmapzone/BitmapZone.js';
+export default BitmapZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bitmapzone.js b/ui/src/phaser3-rex-plugins/plugins/bitmapzone.js
new file mode 100644
index 000000000..7ffab78f9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bitmapzone.js
@@ -0,0 +1,2 @@
+import BitmapZone from './behaviors/bitmapzone/BitmapZone.js';
+export default BitmapZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/blitter-plugin.js b/ui/src/phaser3-rex-plugins/plugins/blitter-plugin.js
new file mode 100644
index 000000000..fda4ab16d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/blitter-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/blitter/blitter/Factory.js';
+import Creator from './gameobjects/blitter/blitter/Creator.js';
+import Blitter from './gameobjects/blitter/blitter/Blitter.js';
+import SetValue from './utils/object/SetValue.js';
+
+class BlitterPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexBlitter', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.Blitter', Blitter);
+
+export default BlitterPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/blitter.js b/ui/src/phaser3-rex-plugins/plugins/blitter.js
new file mode 100644
index 000000000..a66b9f23d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/blitter.js
@@ -0,0 +1,2 @@
+import Blitter from './gameobjects/blitter/blitter/Blitter.js';
+export default Blitter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-components.d.ts b/ui/src/phaser3-rex-plugins/plugins/board-components.d.ts
new file mode 100644
index 000000000..b8e95c9b1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-components.d.ts
@@ -0,0 +1,29 @@
+import Board from './board/board/Board';
+import HexagonGrid from './board/grid/hexagon/Hexagon';
+import QuadGrid from './board/grid/quad/Quad';
+import Shape from './board/shape/Shape';
+import Match from './board/match/Match';
+import MoveTo from './board/moveto/MoveTo';
+import PathFinder from './board/pathfinder/PathFinder';
+import FieldOfView from './board/fieldofview/FieldOfView';
+import Monopoly from './board/monopoly/Monopoly';
+import MiniBoard from './board/miniboard/MiniBoard';
+import HexagonMap from './board/hexagonmap/index';
+import CreateTileTexture from './board/texture/CreateTileTexture';
+import CreateBoardFromTilemap from './board/tilemap/CreateBoardFromTilemap';
+
+export {
+ Board,
+ HexagonGrid,
+ QuadGrid,
+ Shape,
+ Match,
+ MoveTo,
+ PathFinder,
+ FieldOfView,
+ Monopoly,
+ MiniBoard,
+ HexagonMap,
+ CreateTileTexture,
+ CreateBoardFromTilemap
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-components.js b/ui/src/phaser3-rex-plugins/plugins/board-components.js
new file mode 100644
index 000000000..aaf2ee752
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-components.js
@@ -0,0 +1,29 @@
+import Board from './board/board/Board.js';
+import HexagonGrid from './board/grid/hexagon/Hexagon.js';
+import QuadGrid from './board/grid/quad/Quad.js';
+import Shape from './board/shape/Shape.js';
+import Match from './board/match/Match.js';
+import MoveTo from './board/moveto/MoveTo.js';
+import PathFinder from './board/pathfinder/PathFinder.js';
+import FieldOfView from './board/fieldofview/FieldOfView.js';
+import Monopoly from './board/monopoly/Monopoly.js';
+import MiniBoard from './board/miniboard/MiniBoard.js';
+import HexagonMap from './board/hexagonmap/index.js';
+import CreateTileTexture from './board/texture/CreateTileTexture.js';
+import CreateBoardFromTilemap from './board/tilemap/CreateBoardFromTilemap.js';
+
+export {
+ Board,
+ HexagonGrid,
+ QuadGrid,
+ Shape,
+ Match,
+ MoveTo,
+ PathFinder,
+ FieldOfView,
+ Monopoly,
+ MiniBoard,
+ HexagonMap,
+ CreateTileTexture,
+ CreateBoardFromTilemap
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-logic.d.ts b/ui/src/phaser3-rex-plugins/plugins/board-logic.d.ts
new file mode 100644
index 000000000..678649ff5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-logic.d.ts
@@ -0,0 +1,19 @@
+import Board from './board/board/LogicBoard';
+import Quad from './board/grid/quad/Quad';
+import Hexagon from './board/grid/hexagon/Hexagon';
+import Match from './board/match/Match';
+import PathFinder from './board/pathfinder/PathFinder';
+import FieldOfView from './board/fieldofview/FieldOfView';
+import Monopoly from './board/monopoly/Monopoly';
+import HexagonMap from './board/hexagonmap/index';
+
+export {
+ Board,
+ Quad,
+ Hexagon,
+ Match,
+ PathFinder,
+ FieldOfView,
+ Monopoly,
+ HexagonMap,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-logic.js b/ui/src/phaser3-rex-plugins/plugins/board-logic.js
new file mode 100644
index 000000000..cb1352a44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-logic.js
@@ -0,0 +1,19 @@
+import Board from './board/board/LogicBoard.js';
+import Quad from './board/grid/quad/Quad.js';
+import Hexagon from './board/grid/hexagon/Hexagon.js';
+import Match from './board/match/Match.js';
+import PathFinder from './board/pathfinder/PathFinder.js';
+import FieldOfView from './board/fieldofview/FieldOfView.js';
+import Monopoly from './board/monopoly/Monopoly.js';
+import HexagonMap from './board/hexagonmap/index.js';
+
+export {
+ Board,
+ Quad,
+ Hexagon,
+ Match,
+ PathFinder,
+ FieldOfView,
+ Monopoly,
+ HexagonMap,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/board-plugin.d.ts
new file mode 100644
index 000000000..82abef55b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-plugin.d.ts
@@ -0,0 +1,63 @@
+// import * as Phaser from 'phaser';
+
+import BoardFactory from './board/board/Factory.js';
+import QuadGridFactory from './board/grid/quad/Factory';
+import HexagonGridFactory from './board/grid/hexagon/Factory';
+import ShapeFactory from './board/shape/Factory';
+import MoveToFactory from './board/moveto/Factory';
+import PathFinderFactory from './board/pathfinder/Factory';
+import MatchFactory from './board/match/Factory';
+import FieldOfViewFactory from './board/fieldofview/Factory';
+import MonopolyFactory from './board/monopoly/Factory';
+import MiniBoardFactory from './board/miniboard/Factory';
+
+import HexagonMap from './board/hexagonmap/index';
+import CreateTileTexture from './board/texture/CreateTileTexture';
+import CreateBoardFromTilemap from './board/tilemap/CreateBoardFromTilemap';
+
+export default BoardPlugin;
+
+declare class Factories {
+ board: typeof BoardFactory;
+ quadGrid: typeof QuadGridFactory;
+ hexagonGrid: typeof HexagonGridFactory;
+ shape: typeof ShapeFactory;
+ moveTo: typeof MoveToFactory;
+ pathFinder: typeof PathFinderFactory;
+ match: typeof MatchFactory;
+ fieldOfView: typeof FieldOfViewFactory;
+ monopoly: typeof MonopolyFactory;
+ miniBoard: typeof MiniBoardFactory;
+}
+
+declare class BoardPlugin extends Phaser.Plugins.ScenePlugin {
+ add: Factories;
+
+ hexagonMap: HexagonMap;
+ createTileTexture: typeof CreateTileTexture;
+ createBoardFromTilemap: typeof CreateBoardFromTilemap;
+}
+
+import BoardClass from './board/board/Board';
+import HexagonClass from './board/grid/hexagon/Hexagon';
+import QuadClass from './board/grid/quad/Quad';
+import ShapeClass from './board/shape/Shape';
+import MoveToClass from './board/moveto/MoveTo';
+import MatchClass from './board/match/Match';
+import PathFinderClass from './board/pathfinder/PathFinder';
+import FieldOfViewClass from './board/fieldofview/FieldOfView';
+import MonopolyClass from './board/monopoly/Monopoly';
+import MiniBoardClass from './board/miniboard/MiniBoard';
+
+declare namespace BoardPlugin {
+ type Board = BoardClass;
+ type Quad = QuadClass;
+ type Hexagon = HexagonClass;
+ type Shape = ShapeClass;
+ type MoveTo = MoveToClass;
+ type Match = MatchClass;
+ type PathFinder = PathFinderClass;
+ type FieldOfView = FieldOfViewClass;
+ type Monopoly = MonopolyClass;
+ type MiniBoard = MiniBoardClass;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board-plugin.js b/ui/src/phaser3-rex-plugins/plugins/board-plugin.js
new file mode 100644
index 000000000..c358d4215
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board-plugin.js
@@ -0,0 +1,40 @@
+import ObjectFactory from './board/ObjectFactory.js';
+
+import BoardFactory from './board/board/Factory.js';
+import HexagonFactory from './board/grid/hexagon/Factory.js';
+import QuadFactory from './board/grid/quad/Factory.js';
+import ShapeFactory from './board/shape/Factory.js';
+
+import MoveToFactory from './board/moveto/Factory.js';
+import MatchFactory from './board/match/Factory.js';
+import PathFinderFactory from './board/pathfinder/Factory.js';
+import FieldOfViewFactory from './board/fieldofview/Factory.js';
+import MonopolyFactory from './board/monopoly/Factory.js';
+
+import MiniBoardFactory from './board/miniboard/Factory.js';
+
+import HexagonMap from './board/hexagonmap/index.js';
+
+import CreateTileTexture from './board/texture/CreateTileTexture.js';
+
+import CreateBoardFromTilemap from './board/tilemap/CreateBoardFromTilemap.js';
+
+class BoardPlugin extends Phaser.Plugins.ScenePlugin {
+ constructor(scene, pluginManager) {
+ super(scene, pluginManager);
+
+ this.add = new ObjectFactory(scene);
+
+ // Helper functions
+ this.hexagonMap = HexagonMap;
+ this.createTileTexture = CreateTileTexture;
+ this.createBoardFromTilemap = CreateBoardFromTilemap;
+ }
+
+ start() {
+ var eventEmitter = this.scene.sys.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+export default BoardPlugin;
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/ObjectFactory.js b/ui/src/phaser3-rex-plugins/plugins/board/ObjectFactory.js
new file mode 100644
index 000000000..ebd288be8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/ObjectFactory.js
@@ -0,0 +1,20 @@
+class ObjectFactory {
+ constructor(scene) {
+ this.scene = scene;
+ this.displayList = scene.sys.displayList;
+ this.updateList = scene.sys.updateList;
+
+ scene.sys.events.once('destroy', this.destroy, this);
+ }
+
+ destroy() {
+ this.scene = null;
+ this.displayList = null;
+ this.updateList = null;
+ }
+
+ static register(type, callback) {
+ ObjectFactory.prototype[type] = callback;
+ }
+};
+export default ObjectFactory;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/Board.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/board/Board.d.ts
new file mode 100644
index 000000000..cbe431d49
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/Board.d.ts
@@ -0,0 +1,143 @@
+// import * as Phaser from 'phaser';
+import LogicBoard from './LogicBoard';
+import { TileXYZType, TileXYType } from '../types/Position';
+import { Tap, Press, Swipe } from '../../gestures'
+
+export default Board;
+
+declare namespace Board {
+ interface IConfig extends LogicBoard.IConfig { }
+
+ interface SetInteractiveIConfig {
+ enable?: boolean;
+ useTouchZone?: boolean;
+ }
+
+ namespace Events {
+ type KickOutCallbackType = (
+ chessToAdd: unknown,
+ occupiedChess: unknown,
+ tileXYZ: TileXYZType
+ ) => void;
+
+ type TileDownCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectDownCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerDownCallbackType = (
+ pointer: Phaser.Input.Pointer
+ ) => void;
+
+ type TileUpCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectUpCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerUpCallbackType = (
+ pointer: Phaser.Input.Pointer
+ ) => void;
+
+ type TileMoveCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectMoveCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerMoveCallbackType = (
+ pointer: Phaser.Input.Pointer
+ ) => void;
+
+ type TileOverCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectOverCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerOverCallbackType = (
+ pointer: Phaser.Input.Pointer
+ ) => void;
+
+ type TileOutCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectOutCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerOutCallbackType = (
+ pointer: Phaser.Input.Pointer
+ ) => void;
+
+ type TileTapCallbackType = (
+ tap: Tap,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectTapCallbackType = (
+ tap: Tap,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type TapCallbackType = (tap: Tap) => void;
+
+ type TilePressCallbackType = (
+ press: Press,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectPressCallbackType = (
+ press: Press,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PressCallbackType = (press: Press) => void;
+
+ type TileSwipeCallbackType = (
+ swipe: Swipe,
+ tileXY: TileXYType
+ ) => void;
+
+ type GameObjectSwipeCallbackType = (
+ swipe: Swipe,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type SwipeCallbackType = (swipe: Swipe) => void;
+ }
+}
+
+declare class Board extends LogicBoard {
+ constructor(scene: Phaser.Scene, config?: Board.IConfig);
+ scene: Phaser.Scene;
+
+ setInteractive(config?: Board.SetInteractiveIConfig): this;
+ setInteractive(enable?: boolean): this;
+
+ getTouchZone(): Phaser.GameObjects.Zone;
+ readonly touchZone: Phaser.GameObjects.Zone;
+
+ chessToBoard(chess: any): Board;
+ static GetBoard(chess: any): Board;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/Board.js b/ui/src/phaser3-rex-plugins/plugins/board/board/Board.js
new file mode 100644
index 000000000..8dbf0d1fc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/Board.js
@@ -0,0 +1,28 @@
+import LogicBoard from './LogicBoard.js';
+import SetInteractive from './input/SetInteractive.js';
+import ForEachCullTileXY from './camera/ForEachCullTileXY.js';
+
+class Board extends LogicBoard {
+ get touchZone() {
+ if (this.input) {
+ return this.input.touchZone;
+ } else {
+ return null;
+ }
+ }
+
+ getTouchZone() {
+ return this.touchZone;
+ }
+}
+
+var methods = {
+ setInteractive: SetInteractive,
+ forEachCullTileXY: ForEachCullTileXY,
+}
+Object.assign(
+ Board.prototype,
+ methods
+);
+
+export default Board;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.d.ts
new file mode 100644
index 000000000..65ec51e6d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.d.ts
@@ -0,0 +1,5 @@
+import Board from './Board';
+
+export default function (
+ config?: Board.IConfig
+): Board;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.js
new file mode 100644
index 000000000..b7e26d2d6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/Factory.js
@@ -0,0 +1,11 @@
+import Board from './Board.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('board', function (config) {
+ return new Board(this.scene, config);
+});
+
+SetValue(window, 'RexPlugins.Board.Board', Board);
+
+export default Board;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.d.ts
new file mode 100644
index 000000000..47624601b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.d.ts
@@ -0,0 +1,441 @@
+import EE from '../../utils/eventemitter/EventEmitter';
+import QuadGrid from '../grid/quad/Quad';
+import HexagonGrid from '../grid/hexagon/Hexagon';
+import Quad from '../grid/quad/Quad';
+import Hexagon from '../grid/hexagon/Hexagon';
+import {
+ TileXYZType, TileXYType, TileXYDirectionType,
+ WorldXYType
+} from '../types/Position';
+import Line from '../../utils/geom/line/Line';
+import Circle from '../../utils/geom/circle/Circle';
+import Rectangle from '../../utils/geom/rectangle/Rectangle';
+import Ellipse from '../../utils/geom/ellipse/Ellipse';
+import Triangle from '../../utils/geom/triangle/Triangle';
+import Polygon from '../../utils/geom/polygon/Polygon';
+
+export default Board;
+
+declare namespace Board {
+ type ForEachTileXYOrderTypes = 0 | 1 | 2 | 3 | 'x+' | 'x-' | 'y+' | 'y-';
+
+ interface IConfigQuadGrid extends Quad.IConfig {
+ gridType: 'quadGrid',
+ }
+
+ interface IConfigHexagonGrid extends Hexagon.IConfig {
+ gridType: 'hexagonGrid',
+ }
+
+ interface IConfig {
+ grid?: QuadGrid | HexagonGrid | IConfigQuadGrid | IConfigHexagonGrid,
+ width?: number,
+ height?: number,
+
+ wrap?: boolean,
+ infinity?: boolean
+ }
+
+ namespace Events {
+ type KickOutCallbackType = (
+ chessToAdd: unknown,
+ occupiedChess: unknown,
+ tileXYZ: TileXYZType
+ ) => void;
+ }
+
+}
+
+declare class Board extends EE {
+ constructor(
+ scene: unknown,
+ config?: Board.IConfig
+ );
+
+ scene: unknown;
+
+ setGrid(grid: QuadGrid | HexagonGrid | Board.IConfigQuadGrid | Board.IConfigHexagonGrid): this;
+ grid: QuadGrid | HexagonGrid;
+
+ setBoardWidth(width: number): this;
+ readonly width: number;
+ setBoardHeight(height: number): this;
+ readonly height: number;
+
+ setWrapMode(enable?: boolean): this;
+ wrapMode: boolean;
+
+ setInfinityMode(enable?: boolean): this;
+ infinityMode: boolean;
+
+ addChess(
+ chess: ChessType,
+ tileX: number,
+ tileY: number,
+ tileZ: number | string,
+ align?: boolean
+ ): this;
+
+ removeChess(
+ chess: ChessType,
+ tileX?: null,
+ tileY?: null,
+ tileZ?: null,
+ destroy?: boolean
+ ): this;
+ removeChess(
+ chess: null,
+ tileX: number,
+ tileY: number,
+ tileZ: number | string,
+ destroy?: boolean
+ ): this;
+
+ removeAllChess(destroy?: boolean): this;
+
+ moveChess(
+ chess: ChessType,
+ toTileX: number,
+ toTileY: number,
+ toTileZ: number | string,
+ align?: boolean
+ ): this;
+
+ setChessTileZ(
+ chess: ChessType,
+ toTileZ: number | string,
+ align?: boolean
+ ): this;
+
+ swapChess(
+ chessA: ChessType,
+ chessB: ChessType,
+ align?: boolean
+ ): this;
+
+ chessToTileXYZ(
+ chess: ChessType | TileXYType | number | undefined | null
+ ): TileXYZType | null;
+
+ tileXYZToChess(
+ tileX: number,
+ tileY: number,
+ tileZ: number | string
+ ): ChessType | null;
+
+ tileXYToChessArray(
+ tileX: number,
+ tileY: number,
+ out?: ChessType[]
+ ): ChessType[];
+
+ tileZToChessArray(
+ tileZ: number,
+ out?: ChessType[]
+ ): ChessType[];
+
+ tileXYArrayToChessArray(
+ tileXYArray: TileXYType[],
+ tileZ?: number | string,
+ out?: ChessType[]
+ ): ChessType[];
+ tileXYArrayToChessArray(
+ tileXYArray: TileXYType[],
+ out?: ChessType[]
+ ): ChessType[];
+
+ worldXYToChessArray(
+ worldX: number,
+ worldY: number,
+ out?: ChessType[]
+ ): ChessType[];
+
+ worldXYToChess(
+ worldX: number,
+ worldY: number,
+ tileZ?: number | string
+ ): ChessType;
+
+ contains(
+ tileX: number,
+ tileY: number,
+ tileZ?: number | string
+ ): boolean;
+
+ exists(
+ chess: ChessType
+ ): boolean;
+
+ forEachTileXY(
+ callback: (tileXY: TileXYType, board: Board) => void | boolean,
+ scope?: object,
+ order?: Board.ForEachTileXYOrderTypes
+ ): this;
+
+ tileXYToWorldXY(
+ tileX: number,
+ tileY: number,
+ out?: WorldXYType | true
+ ): WorldXYType;
+
+ worldXYToTileXY(
+ worldX: number,
+ worldY: number,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ worldXYSnapToGrid(
+ worldX: number,
+ worldY: number,
+ out?: WorldXYType | true
+ ): WorldXYType;
+
+ getDistance(
+ tileA: ChessType | TileXYType,
+ tileB: ChessType | TileXYType
+ ): number;
+
+ ringToTileXYArray(
+ centerTileXY: ChessType | TileXYType,
+ radius: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ filledRingToTileXYArray(
+ centerTileXY: ChessType | TileXYType,
+ radius: number,
+ nearToFar?: boolean,
+ out?: TileXYType[]
+ ): TileXYType[];
+ filledRingToTileXYArray(
+ centerTileXY: ChessType | TileXYType,
+ radius: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ lineToTileXYArray(
+ line: Line,
+ out?: TileXYType[]
+ ): TileXYType[];
+ lineToTileXYArray(
+ startWorldX: number,
+ startWorldY: number,
+ endWorldX: number,
+ endWorldY: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ circleToTileXYArray(
+ circle: Circle,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ circleToTileXYArray(
+ circle: Circle,
+ testMode?: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ rectangleToTileXYArray(
+ rectangle: Rectangle,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ rectangleToTileXYArray(
+ rectangle: Rectangle,
+ testMode?: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ ellipseToTileXYArray(
+ ellipse: Ellipse,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ ellipseToTileXYArray(
+ ellipse: Ellipse,
+ testMode?: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ triangleToTileXYArray(
+ triangle: Triangle,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ triangleToTileXYArray(
+ triangle: Triangle,
+ testMode?: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ polygonToTileXYArray(
+ polygon: Polygon,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ polygonToTileXYArray(
+ polygon: Polygon,
+ testMode?: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ angleBetween(
+ tileA: ChessType | TileXYType,
+ tileB: ChessType | TileXYType
+ ): number;
+
+ isAngleInCone(
+ chessA: ChessType | TileXYType,
+ chessB: ChessType | TileXYType,
+ face: number,
+ cone: number
+ ): boolean;
+
+ directionBetween(
+ chessA: ChessType | TileXYType,
+ chessB: ChessType | TileXYType,
+ round?: boolean
+ ): number;
+
+ isDirectionInCone(
+ chessA: ChessType | TileXYType,
+ chessB: ChessType | TileXYType,
+ face: number,
+ cone: number
+ ): boolean;
+
+ getOppositeDirection(
+ tileX: number | ChessType | TileXYType,
+ tileY: number,
+ direction?: number
+ ): number;
+
+ angleSnapToDirection(
+ tileXY: ChessType | TileXYType | undefined,
+ angle: number
+ ): number;
+
+ gridAlign(chess?: ChessType): this;
+
+ isOverlappingPoint(
+ worldX: number,
+ worldY: number,
+ tileZ?: number | string
+ ): boolean;
+
+ getNeighborTileXY(
+ srcTileXY: ChessType | TileXYType,
+ direction: number,
+ out?: TileXYType | true
+ ): TileXYDirectionType;
+ getNeighborTileXY(
+ srcTileXY: ChessType | TileXYType,
+ direction: number | number[] | string | null,
+ out?: TileXYType[]
+ ): TileXYDirectionType | TileXYDirectionType[];
+
+ getNeighborTileDirection(
+ srcTile: ChessType | TileXYType,
+ neighborTileXY: TileXYType
+ ): number | null;
+
+ getNeighborTileXYAtAngle(
+ srcTileXY: ChessType | TileXYType,
+ angle: number,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ getNeighborChess(
+ tileXYZ: ChessType | TileXYType,
+ direction: number | number[] | string | null,
+ neighborTileZ?: number
+ ): ChessType | ChessType[];
+
+ areNeighbors(
+ tileA: ChessType | TileXYType,
+ tileB: ChessType | TileXYType
+ ): boolean;
+
+ mapNeighbors(
+ tileXYZ: ChessType | TileXYType,
+ callback: (
+ tileXY: TileXYDirectionType,
+ index: number,
+ tileXYArray: TileXYDirectionType[]
+ ) => any,
+ scope?: object
+ ): any[];
+
+ mapNeighbors(
+ tileXYZ: ChessType | TileXYType,
+ distance: number,
+ callback: (
+ tileXY: TileXYDirectionType,
+ index: number,
+ tileXYArray: TileXYDirectionType[]
+ ) => any,
+ scope?: object
+ ): any[];
+
+ getTileXYAtDirection(
+ srcTileXY: ChessType | TileXYType,
+ direction: number | number[] | string | null,
+ distance: number | number[] | { start?: number, end?: number, step?: number },
+ out?: TileXYType[]
+ ): TileXYDirectionType | TileXYDirectionType[];
+
+ isEmptyTileXYZ(
+ tileX: number,
+ tileY?: number,
+ tileZ?: number | string
+ ): boolean;
+
+ getRandomEmptyTileXY(
+ tileZ: number | string,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ getEmptyTileXYArray(
+ tileZ: number | string,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ getRandomEmptyTileXYInRange(
+ centerTileXY: ChessType | TileXYType,
+ radius: number,
+ tileZ: number | string,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ getEmptyTileXYArrayInRange(
+ centerTileXY: ChessType | TileXYType,
+ radius: number,
+ tileZ: number | string,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ getAllChess(): ChessType[];
+
+ fit(tileXYArray: TileXYType[]): this;
+
+ hasBlocker(
+ tileX: number | ChessType | TileXYType,
+ tileY?: number,
+ tileZ?: number | string
+ ): boolean;
+
+ getGridPoints(
+ tileX: number,
+ tileY?: number,
+ out?: WorldXYType[] | true
+ ): WorldXYType[];
+ getGridPoints(
+ tileXY: ChessType | TileXYType,
+ out?: WorldXYType[] | true
+ ): WorldXYType[];
+
+ chessToBoard(chess: any): Board;
+ static GetBoard(chess: any): Board;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.js
new file mode 100644
index 000000000..611be6f76
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicBoard.js
@@ -0,0 +1,130 @@
+import EE from '../../utils/eventemitter/EventEmitter.js';
+import LogicMethods from './LogicMethods.js';
+import BoardData from './boarddata/BoardData.js';
+import DefaultGrids from '../grid/index.js';
+import GetValue from '../../utils/object/GetValue.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+import GetBoard from './chess/GetBoard.js';
+
+class Board extends EE {
+ constructor(scene, config) {
+ // scene: scene instance, or undefined
+ super();
+
+ this.isShutdown = false;
+ this.scene = scene;
+ this.boardData = new BoardData();
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isBoard = GetValue(o, 'isBoard', true); // false: in Miniboard
+ this.setGrid(GetValue(o, 'grid', undefined));
+ this.setWrapMode(GetValue(o, 'wrap', false));
+ this.setInfinityMode(GetValue(o, 'infinity', false));
+ this.setBoardWidth(GetValue(o, 'width', 0));
+ this.setBoardHeight(GetValue(o, 'height', 0));
+ return this;
+ }
+
+ boot() {
+ if (this.scene && this.isBoard) {
+ this.scene.sys.events.once('shutdown', this.destroy, this);
+ }
+ }
+
+ shutdown(fromScene) {
+ if (this.isShutdown) {
+ return;
+ }
+
+ if (this.scene && this.isBoard) {
+ this.scene.sys.events.off('shutdown', this.destroy, this);
+ }
+
+ if (this.isBoard) {
+ this.removeAllChess(!fromScene, true);
+ } else {
+
+ }
+
+ super.shutdown();
+ this.boardData.shutdown(fromScene);
+
+ this.scene = undefined;
+ this.boardData = undefined;
+ this.isShutdown = true;
+
+ return this;
+ }
+
+ destroy(fromScene) {
+ if (this.isShutdown) {
+ return;
+ }
+ this.emit('destroy', this, fromScene);
+ this.shutdown(fromScene);
+ }
+
+ setGrid(grid) {
+ if (IsPlainObject(grid)) {
+ var config = grid;
+ var gridType = GetValue(config, 'gridType', 'quadGrid');
+ var grid = new DefaultGrids[gridType](config);
+ }
+ this.grid = grid;
+ return this;
+ }
+
+ setWrapMode(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.wrapMode = enable;
+ return this;
+ }
+
+ setInfinityMode(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.infinityMode = enable;
+ return this;
+ }
+
+ setBoardSize(width, height) {
+ this.setBoardWidth(width);
+ this.setBoardHeight(height);
+ return this;
+ }
+
+ exists(gameObject) {
+ // game object or uid
+ return this.boardData.exists(this.getChessUID(gameObject));
+ }
+
+ get chessCount() {
+ return this.boardData.chessCount;
+ }
+
+ clear(destroy) {
+ if (destroy === undefined) {
+ destroy = true;
+ }
+ this.removeAllChess(destroy, true);
+ this.boardData.clear();
+ return this;
+ }
+
+ static GetBoard(chess) {
+ return GetBoard(chess);
+ }
+}
+
+Object.assign(
+ Board.prototype,
+ LogicMethods
+);
+
+export default Board;
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/LogicMethods.js b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicMethods.js
new file mode 100644
index 000000000..179065fe5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/LogicMethods.js
@@ -0,0 +1,183 @@
+import GetChessData from '../chess/GetChessData.js';
+import GetChessUID from '../chess/GetChessUID.js';
+
+import SetBoardWidth from './boarddata/SetBoardWidth.js';
+import SetBoardHeight from './boarddata/SetBoardHeight.js';
+
+import TileXYZToKey from '../utils/tilexyzkey/TileXYZToKey.js';
+import TileXYToKey from '../utils/tilexyzkey/TileXYToKey.js';
+import KeyToTileXYZ from '../utils/tilexyzkey/KeyToTileXYZ.js';
+
+import TileXYToWorldX from './worldposition/TileXYToWorldX.js';
+import TileXYToWorldY from './worldposition/TileXYToWorldY.js';
+import TileXYToWorldXY from './worldposition/TileXYToWorldXY.js';
+import TileXYArrayToWorldXYArray from './worldposition/TileXYArrayToWorldXYArray.js';
+import WorldXYToTileX from './worldposition/WorldXYToTileX.js';
+import WorldXYToTileY from './worldposition/WorldXYToTileY.js';
+import WorldXYToTileXY from './worldposition/WorldXYToTileXY.js';
+import WorldXYToChessArray from './worldposition/WorldXYToChessArray.js';
+import WorldXYToChess from './worldposition/WorldXYToChess.js';
+import WorldXYSnapToGrid from './worldposition/WorldXYSnapToGrid.js';
+import AngleBetween from './worldposition/AngleBetween.js';
+import IsAngleInCone from './worldposition/IsAngleInCone.js';
+import AngleToward from './worldposition/AngleToward.js';
+import AngleSnapToDirection from './worldposition/AngleSnapToDirection.js';
+import IsOverlappingPoint from './worldposition/IsOverlappingPoint.js';
+import GridAlign from './worldposition/GridAlign.js';
+import GetGridPoints from './worldposition/GetGridPoints.js';
+import GetGridBounds from './worldposition/GetGridBounds.js';
+import GetBoardBounds from './worldposition/GetBoardBounds.js';
+
+import LineToTileXYArray from './shape/LineToTileXYArray.js';
+import CircleToTileXYArray from './shape/CircleToTileXYArray.js';
+import EllipseToTileXYArray from './shape/EllipseToTileXYArray.js';
+import PolygonToTileXYArray from './shape/PolygonToTileXYArray.js';
+import RectangleToTileXYArray from './shape/RectangleToTileXYArray.js';
+import TriangleToTileXYArray from './shape/TriangleToTileXYArray.js';
+import ShapeToTileXYArray from './shape/ShapeToTileXYArray.js';
+import ForEachTileXYInShape from './shape/ForEachTileXYInShape.js';
+
+import UidToChess from './chess/UidToChess.js';
+import AddChess from './chess/AddChess.js';
+import SetChessTileZ from './chess/SetChessTileZ.js';
+import RemoveChess from './chess/RemoveChess.js';
+import RemoveAllChess from './chess/RemoveAllChess.js';
+import SwapChess from './chess/SwapChess.js';
+import GetAllChess from './chess/GetAllChess.js';
+
+import Contains from './tileposition/Contains.js';
+import ForEachTileXY from './tileposition/ForEachTileXY.js';
+import GetWrapTileXY from './tileposition/GetWrapTileXY.js';
+import TileXYZToChess from './tileposition/TileXYZToChess.js';
+import TileXYToChessArray from './tileposition/TileXYToChessArray.js';
+import TileZToChessArray from './tileposition/TileZToChessArray.js';
+import TileXYArrayToChessArray from './tileposition/TileXYArrayToChessArray.js';
+import ChessToTileXYZ from './tileposition/ChessToTileXYZ.js';
+import GetOppositeDirection from './tileposition/GetOppositeDirection.js';
+import GetDistance from './tileposition/GetDistance.js';
+import DirectionBetween from './tileposition/DirectionBetween.js';
+import IsDirectionInCone from './tileposition/IsDirectionInCone.js';
+
+import Offset from './transform/Offset.js';
+import Mirror from './transform/Mirror.js';
+import Rotate from './transform/Rotate.js';
+import Fit from './transform/Fit.js';
+
+import IsEmptyTileXYZ from './empty/IsEmptyTileXYZ.js';
+import GetEmptyTileXYArray from './empty/GetEmptyTileXYArray.js';
+import GetRandomEmptyTileXY from './empty/GetRandomEmptyTileXY.js';
+import GetEmptyTileXYArrayInRange from './empty/GetEmptyTileXYArrayInRange.js';
+import GetRandomEmptyTileXYInRange from './empty/GetRandomEmptyTileXYInRange.js';
+
+import GetTileXYAtDirection from './neighbors/GetTileXYAtDirection.js';
+import GetNeighborTileXY from './neighbors/GetNeighborTileXY.js';
+import GetNeighborTileXYAtAngle from './neighbors/GetNeighborTileXYAtAngle.js';
+import GetNeighborChess from './neighbors/GetNeighborChess.js';
+import GetNeighborTileDirection from './neighbors/GetNeighborTileDirection.js';
+import GetNeighborChessDirection from './neighbors/GetNeighborChessDirection.js';
+import AreNeighbors from './neighbors/AreNeighbors.js';
+import MapNeighbors from './neighbors/MapNeighobrs.js';
+
+import RingToTileXYArray from './ring/RingToTileXYArray.js';
+import RingToChessArray from './ring/RingToChessArray.js';
+import FilledRingToTileXYArray from './ring/FilledRingToTileXYArray.js';
+import FilledRingToChessArray from './ring/FilledRingToChessArray.js';
+
+import HasBlocker from './blocker/HasBlocker.js';
+import HasEdgeBlocker from './blocker/HasEdgeBlocker.js';
+
+import GetBoard from './chess/GetBoard.js';
+
+export default {
+ getChessData: GetChessData,
+ getChessUID: GetChessUID,
+
+ setBoardWidth: SetBoardWidth,
+ setBoardHeight: SetBoardHeight,
+
+ tileXYZToKey: TileXYZToKey,
+ tileXYToKey: TileXYToKey,
+ keyToTileXYZ: KeyToTileXYZ,
+
+ tileXYToWorldX: TileXYToWorldX,
+ tileXYToWorldY: TileXYToWorldY,
+ tileXYToWorldXY: TileXYToWorldXY,
+ tileXYArrayToWorldXYArray: TileXYArrayToWorldXYArray,
+ worldXYToTileX: WorldXYToTileX,
+ worldXYToTileY: WorldXYToTileY,
+ worldXYToTileXY: WorldXYToTileXY,
+ worldXYToChessArray: WorldXYToChessArray,
+ worldXYToChess: WorldXYToChess,
+ worldXYSnapToGrid: WorldXYSnapToGrid,
+ angleBetween: AngleBetween,
+ isAngleInCone: IsAngleInCone,
+ angleToward: AngleToward,
+ angleSnapToDirection: AngleSnapToDirection,
+ isOverlappingPoint: IsOverlappingPoint,
+ gridAlign: GridAlign,
+ getGridPoints: GetGridPoints,
+ getGridBounds: GetGridBounds,
+ getBoardBounds: GetBoardBounds,
+
+ lineToTileXYArray: LineToTileXYArray,
+ circleToTileXYArray: CircleToTileXYArray,
+ ellipseToTileXYArray: EllipseToTileXYArray,
+ polygonToTileXYArray: PolygonToTileXYArray,
+ rectangleToTileXYArray: RectangleToTileXYArray,
+ triangleToTileXYArray: TriangleToTileXYArray,
+ shapeToTileXYArray: ShapeToTileXYArray,
+ forEachTileXYInShape: ForEachTileXYInShape,
+
+ uidToChess: UidToChess,
+ addChess: AddChess,
+ removeChess: RemoveChess,
+ removeAllChess: RemoveAllChess,
+ swapChess: SwapChess,
+ moveChess: AddChess,
+ setChessTileZ: SetChessTileZ,
+ getAllChess: GetAllChess,
+
+ contains: Contains,
+ forEachTileXY: ForEachTileXY,
+ getWrapTileXY: GetWrapTileXY,
+ tileXYZToChess: TileXYZToChess,
+ tileXYToChessArray: TileXYToChessArray,
+ tileZToChessArray: TileZToChessArray,
+ tileXYArrayToChessArray: TileXYArrayToChessArray,
+ chessToTileXYZ: ChessToTileXYZ,
+ offset: Offset,
+ mirror: Mirror,
+ rotate: Rotate,
+ getOppositeDirection: GetOppositeDirection,
+ getDistance: GetDistance,
+ directionBetween: DirectionBetween,
+ isDirectionInCone: IsDirectionInCone,
+ fit: Fit,
+
+ isEmptyTileXYZ: IsEmptyTileXYZ,
+ getEmptyTileXYArray: GetEmptyTileXYArray,
+ getRandomEmptyTileXY: GetRandomEmptyTileXY,
+ getEmptyTileXYArrayInRange: GetEmptyTileXYArrayInRange,
+ getRandomEmptyTileXYInRange: GetRandomEmptyTileXYInRange,
+
+ getTileXYAtDirection: GetTileXYAtDirection,
+ getNeighborTileXY: GetNeighborTileXY,
+ getNeighborTileXYAtAngle: GetNeighborTileXYAtAngle,
+ getNeighborChess: GetNeighborChess,
+ getNeighborTileDirection: GetNeighborTileDirection,
+ getNeighborChessDirection: GetNeighborChessDirection,
+ areNeighbors: AreNeighbors,
+ mapNeighbors: MapNeighbors,
+
+ ringToTileXYArray: RingToTileXYArray,
+ ringToChessArray: RingToChessArray,
+ filledRingToTileXYArray: FilledRingToTileXYArray,
+ filledRingToChessArray: FilledRingToChessArray,
+
+ hasBlocker: HasBlocker,
+ hasEdgeBlocker: HasEdgeBlocker,
+
+ getGridPoints: GetGridPoints,
+
+ chessToBoard: GetBoard,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasBlocker.js b/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasBlocker.js
new file mode 100644
index 000000000..3f8e2c36d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasBlocker.js
@@ -0,0 +1,36 @@
+var HasBlocker = function (tileX, tileY, tileZ) {
+ if (tileX && (typeof (tileX) !== 'number')) {
+ var tileXYZ = this.chessToTileXYZ(tileX); // tileX is a Chess or TileXY
+ tileX = tileXYZ.x;
+ tileY = tileXYZ.y;
+ tileZ = tileXYZ.z;
+ }
+
+ var chess, blocker;
+ if (tileZ === undefined) {
+ // any chess at (tileX, tileY) has blocker
+ chess = this.tileXYToChessArray(tileX, tileY, globChessArray);
+ for (var i = 0, cnt = chess.length; i < cnt; i++) {
+ blocker = this.getChessData(chess[i]).blocker;
+ if (blocker === true) {
+ globChessArray.length = 0;
+ return true;
+ }
+ }
+ globChessArray.length = 0;
+ return false;
+
+ } else {
+ // chess at (tileX, tileY, tileZ) has blocker
+ var chess = this.tileXYZToChess(tileX, tileY, tileZ);
+ if (chess === null) {
+ return false;
+ }
+ blocker = this.getChessData(chess).blocker;
+ return (blocker === true);
+
+ }
+}
+var globChessArray = [];
+
+export default HasBlocker;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasEdgeBlocker.js b/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasEdgeBlocker.js
new file mode 100644
index 000000000..f1f835c81
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/blocker/HasEdgeBlocker.js
@@ -0,0 +1,35 @@
+var HasEdgeBlocker = function (tileX, tileY, tileZ, direction) {
+ var chess, blocker;
+ if (tileZ === undefined) {
+ // any chess at (tileX, tileY) has blocker
+ chess = this.tileXYToChessArray(tileX, tileY, globChessArray);
+ for (var i = 0, cnt = chess.length; i < cnt; i++) {
+ if (isEdgeBlocker(this.getChessData(chess[i]).blocker)) {
+ globChessArray.length = 0;
+ return true;
+ }
+ }
+ globChessArray.length = 0;
+ return false;
+
+ } else {
+ // chess at (tileX, tileY, tileZ) has blocker
+ var chess = this.tileXYZToChess(tileX, tileY, tileZ);
+ if (chess === null) {
+ return false;
+ }
+ return isEdgeBlocker(this.getChessData(chess).blocker);
+ }
+}
+
+var isEdgeBlocker = function (blocker, direction) {
+ if ((blocker === false) || (blocker === true)) {
+ return blocker;
+ } else {
+ return (blocker[direction] === true);
+ }
+}
+
+var globChessArray = [];
+
+export default HasEdgeBlocker;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/BoardData.js b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/BoardData.js
new file mode 100644
index 000000000..35e14618d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/BoardData.js
@@ -0,0 +1,182 @@
+import Clear from '../../../utils/object/Clear.js';
+import IsEmpty from '../../../utils/object/IsEmpty.js';
+
+class BoardData {
+ constructor() {
+ this.XYZToUID = {}; // [x][y][z] : uid
+ this.UIDToXYZ = {}; // uid : xyz
+ this.clear();
+ }
+
+ shutdown(fromScene) {
+ this.XYZToUID = undefined;
+ this.UIDToXYZ = undefined;
+ return this;
+ }
+
+ destroy(fromScene) {
+ this.shutdown(fromScene);
+ return this;
+ }
+
+ clear() {
+ Clear(this.UIDToXYZ);
+ Clear(this.XYZToUID);
+ this.chessCount = 0;
+ this.clearBounds();
+ return this;
+ }
+
+ clearBounds() {
+ this._xMax = undefined;
+ this._xMin = undefined;
+ this._yMax = undefined;
+ this._yMin = undefined;
+ return this;
+ }
+
+ addUID(uid, x, y, z) {
+ if (!this.XYZToUID.hasOwnProperty(x)) {
+ this.XYZToUID[x] = {};
+ }
+ var tmpx = this.XYZToUID[x];
+ if (!tmpx.hasOwnProperty(y)) {
+ tmpx[y] = {};
+ }
+ var tmpy = tmpx[y];
+ tmpy[z] = uid;
+ this.UIDToXYZ[uid] = {
+ x: x,
+ y: y,
+ z: z
+ };
+
+ this.chessCount++;
+ this.clearBounds();
+ return this;
+ }
+
+ getUID(x, y, z) {
+ // (x,y,z) -> uid
+ // (x,y) -> zHash = {z:uid}
+ var tmp = this.XYZToUID[x];
+ if (tmp) {
+ tmp = tmp[y];
+ if (tmp) {
+ if (z !== undefined) {
+ tmp = tmp[z];
+ }
+ }
+ }
+ return tmp;
+ }
+
+ removeUID(x, y, z) {
+ if (!this.XYZToUID.hasOwnProperty(x)) {
+ return this;
+ }
+ var tmpx = this.XYZToUID[x];
+ if (!tmpx.hasOwnProperty(y)) {
+ return this;
+ }
+ var tmpy = tmpx[y];
+ if (!tmpy.hasOwnProperty(z)) {
+ return this;
+ }
+
+ var uid = tmpy[z];
+ delete tmpy[z];
+ delete this.UIDToXYZ[uid];
+ if (IsEmpty(tmpy)) {
+ delete tmpx[y];
+ }
+ if (IsEmpty(tmpx)) {
+ delete this.XYZToUID[x];
+ }
+
+ this.chessCount--;
+ this.clearBounds();
+ return this;
+ }
+
+ exists(uid) {
+ return this.UIDToXYZ.hasOwnProperty(uid);
+ }
+
+ contains(x, y, z) {
+ return (this.getUID(x, y, z) != null);
+ }
+
+ getXYZ(uid) {
+ if (this.exists(uid)) {
+ return this.UIDToXYZ[uid];
+ }
+ return null;
+ }
+
+ get xMax() {
+ if (this._xMax === undefined) {
+ this._xMax = -Infinity;
+ var UIDToXYZ = this.UIDToXYZ,
+ x;
+ for (var uid in UIDToXYZ) {
+ x = UIDToXYZ[uid].x;
+ if (this._xMax < x) {
+ this._xMax = x;
+ }
+ }
+ }
+
+ return this._xMax;
+ }
+
+ get xMin() {
+ if (this._xMin === undefined) {
+ this._xMin = Infinity;
+ var UIDToXYZ = this.UIDToXYZ,
+ x;
+ for (var uid in UIDToXYZ) {
+ x = UIDToXYZ[uid].x;
+ if (this._xMin > x) {
+ this._xMin = x;
+ }
+ }
+ }
+
+ return this._xMin;
+ }
+
+ get yMax() {
+ if (this._yMax === undefined) {
+ this._yMax = -Infinity;
+ var UIDToXYZ = this.UIDToXYZ,
+ y;
+ for (var uid in UIDToXYZ) {
+ y = UIDToXYZ[uid].y;
+ if (this._yMax < y) {
+ this._yMax = y;
+ }
+ }
+ }
+
+ return this._yMax;
+ }
+
+ get yMin() {
+ if (this._yMin === undefined) {
+ this._yMin = Infinity;
+ var UIDToXYZ = this.UIDToXYZ,
+ y;
+ for (var uid in UIDToXYZ) {
+ y = UIDToXYZ[uid].y;
+ if (this._yMin > y) {
+ this._yMin = y;
+ }
+ }
+ }
+
+ return this._yMin;
+ }
+}
+
+export default BoardData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardHeight.js b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardHeight.js
new file mode 100644
index 000000000..f109d6786
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardHeight.js
@@ -0,0 +1,25 @@
+var SetBoardHeight = function (height) {
+ if (this.infinityMode) {
+ return this;
+ }
+ if ((this.height === undefined) || (this.height <= height)) {
+ this.height = height;
+ return this;
+ }
+
+ // this.height > height : collapse
+ var tileX, tileY, tileZ, tileZToUIDs;
+ for (tileY = height; tileY < this.height; tileY++) {
+ for (tileX = 0; tileX < this.width; tileX++) {
+ tileZToUIDs = this.boardData.getUID(tileX, tileY);
+ for (tileZ in tileZToUIDs) {
+ this.RemoveChess(false, tileX, tileY, tileZ);
+ }
+ }
+ }
+
+ this.height = height;
+ return this;
+}
+
+export default SetBoardHeight;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardWidth.js b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardWidth.js
new file mode 100644
index 000000000..249eefeb3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/boarddata/SetBoardWidth.js
@@ -0,0 +1,25 @@
+var SetBoardWidth = function (width) {
+ if (this.infinityMode) {
+ return this;
+ }
+ if ((this.width === undefined) || (this.width <= width)) {
+ this.width = width;
+ return this;
+ }
+
+ // this.width > width : collapse
+ var tileX, tileY, tileZ, tileZToUIDs;
+ for (tileX = width; tileX < this.width; tileX++) {
+ for (tileY = 0; tileY < this.height; tileY++) {
+ tileZToUIDs = this.boardData.getUID(tileX, tileY);
+ for (tileZ in tileZToUIDs) {
+ this.RemoveChess(false, tileX, tileY, tileZ);
+ }
+ }
+ }
+
+ this.width = width;
+ return this;
+}
+
+export default SetBoardWidth;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/camera/ForEachCullTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/camera/ForEachCullTileXY.js
new file mode 100644
index 000000000..a3e66e0b7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/camera/ForEachCullTileXY.js
@@ -0,0 +1,44 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import Rectangle from '../../../utils/geom/rectangle/Rectangle.js';
+
+var ForEachCullTileXY = function (callback, scope, config) {
+ if (typeof (config) === 'number') {
+ config = {
+ order: config
+ }
+ }
+
+ if (config === undefined) {
+ config = {};
+ }
+
+ var order = GetValue(config, 'order', 0);
+
+ var camera = GetValue(config, 'camera', this.scene.cameras.main);
+ var paddingX = GetValue(config, 'paddingX', 1);
+ var paddingY = GetValue(config, 'paddingY', 1);
+
+ if (ViewportBounds === undefined) {
+ ViewportBounds = new Rectangle();
+ }
+ ViewportBounds.width = (camera.width + paddingX * 2) / camera.zoomX;
+ ViewportBounds.height = (camera.height + paddingY * 2) / camera.zoomY;
+ ViewportBounds.centerX = camera.centerX + camera.scrollX;
+ ViewportBounds.centerY = camera.centerY + camera.scrollY;
+
+ this.forEachTileXYInShape(
+ ViewportBounds,
+ callback,
+ scope,
+ {
+ order: order,
+ testMode: 1
+ }
+ );
+
+ return this;
+}
+
+var ViewportBounds;
+
+export default ForEachCullTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/AddChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/AddChess.js
new file mode 100644
index 000000000..d77330e2f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/AddChess.js
@@ -0,0 +1,44 @@
+var AddChess = function (gameObject, tileX, tileY, tileZ, align) {
+ if (!this.contains(tileX, tileY)) {
+ return this;
+ }
+ if (align === undefined) {
+ align = true;
+ }
+
+ var curTileXYZ = this.chessToTileXYZ(gameObject);
+ if (tileZ === undefined) {
+ if (curTileXYZ) {
+ tileZ = curTileXYZ.z;
+ } else {
+ tileZ = 0;
+ }
+ }
+ if (curTileXYZ &&
+ (curTileXYZ.x === tileX) && (curTileXYZ.y === tileY) && (curTileXYZ.z === tileZ)) {
+ // Move to current position
+ return this;
+ }
+ var occupiedChess = this.tileXYZToChess(tileX, tileY, tileZ);
+ if (occupiedChess) {
+ this.emit('kickout', gameObject, occupiedChess, curTileXYZ);
+ }
+
+ this.removeChess(gameObject);
+ if (occupiedChess) {
+ this.removeChess(occupiedChess, tileX, tileY, tileZ);
+ }
+ this.boardData.addUID(this.getChessUID(gameObject), tileX, tileY, tileZ);
+
+ if (this.isBoard) {
+ this.getChessData(gameObject).setBoard(this);
+ }
+
+ if (align) {
+ this.gridAlign(gameObject, tileX, tileY);
+ }
+
+ return this;
+};
+
+export default AddChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetAllChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetAllChess.js
new file mode 100644
index 000000000..de3d24aae
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetAllChess.js
@@ -0,0 +1,11 @@
+var GetAllChess = function (out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var uids = this.boardData.UIDToXYZ;
+ for (var uid in uids) {
+ out.push(this.uidToChess(uid));
+ }
+ return out;
+};
+export default GetAllChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetBoard.js
new file mode 100644
index 000000000..4060124e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/GetBoard.js
@@ -0,0 +1,13 @@
+var GetBoard = function (chess) {
+ if (!chess) {
+ return undefined;
+ } else if (chess.rexChess) { // Chess game object
+ return chess.rexChess.board;
+ } else if (chess.mainBoard) { // Mini-board
+ return chess.mainBoard;
+ } else {
+ return undefined;
+ }
+}
+
+export default GetBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveAllChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveAllChess.js
new file mode 100644
index 000000000..2bffd182a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveAllChess.js
@@ -0,0 +1,8 @@
+var RemoveAllChess = function (destroy, fromBoardRemove) {
+ var chess = this.getAllChess();
+ for (var i = 0, cnt = chess.length; i < cnt; i++) {
+ this.removeChess(chess[i], undefined, undefined, undefined, destroy, fromBoardRemove);
+ }
+ return this;
+}
+export default RemoveAllChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveChess.js
new file mode 100644
index 000000000..dde54567d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/RemoveChess.js
@@ -0,0 +1,40 @@
+var RemoveChess = function (gameObject, tileX, tileY, tileZ, destroy, fromBoardRemove) {
+ if (destroy === undefined) {
+ destroy = false;
+ }
+ if (fromBoardRemove === undefined) {
+ fromBoardRemove = false;
+ }
+ if (gameObject) {
+ var tileXYZ = this.chessToTileXYZ(gameObject);
+ if (tileXYZ) {
+ tileX = tileXYZ.x;
+ tileY = tileXYZ.y;
+ tileZ = tileXYZ.z;
+ } else {
+ // chess is not in this board
+ return this;
+ }
+ } else {
+ gameObject = this.tileXYZToChess(tileX, tileY, tileZ);
+ if (!gameObject) {
+ // chess is not in this board
+ return this;
+ }
+ }
+
+ if (!fromBoardRemove) {
+ this.boardData.removeUID(tileX, tileY, tileZ);
+ }
+ if (this.isBoard) {
+ this.getChessData(gameObject).setBoard(null);
+ }
+
+ if (destroy && gameObject.destroy) {
+ gameObject.destroy();
+ }
+
+ return this;
+}
+
+export default RemoveChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SetChessTileZ.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SetChessTileZ.js
new file mode 100644
index 000000000..e30d2f71e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SetChessTileZ.js
@@ -0,0 +1,12 @@
+var SetChessTileZ = function (chess, tileZ, align) {
+ if (align === undefined) {
+ align = false;
+ }
+ var tileXYZ = this.chessToTileXYZ(chess);
+ if (tileXYZ) {
+ this.moveChess(chess, tileXYZ.x, tileXYZ.y, tileZ, align);
+ }
+ return this;
+}
+
+export default SetChessTileZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SwapChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SwapChess.js
new file mode 100644
index 000000000..4f5c4e6ae
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/SwapChess.js
@@ -0,0 +1,18 @@
+var SwapChess = function (gameObjectA, gameObjectB, align) {
+ if (align === undefined) {
+ align = true;
+ }
+
+ var tileXYZA = this.chessToTileXYZ(gameObjectA);
+ var tileXYZB = this.chessToTileXYZ(gameObjectB);
+ if ((tileXYZA == null) || (tileXYZB == null)) {
+ return this;
+ }
+ this.removeChess(gameObjectA);
+ this.removeChess(gameObjectB);
+ this.addChess(gameObjectA, tileXYZB.x, tileXYZB.y, tileXYZB.z, align);
+ this.addChess(gameObjectB, tileXYZA.x, tileXYZA.y, tileXYZA.z, align);
+ return this;
+};
+
+export default SwapChess;
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/chess/UidToChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/UidToChess.js
new file mode 100644
index 000000000..eebcbf96b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/chess/UidToChess.js
@@ -0,0 +1,13 @@
+import ChessBank from '../../chess/ChessBank.js';
+
+var UidToChess = function (uid) {
+ if (uid == null) {
+ return null;
+ } else {
+ if (!this.boardData.exists(uid)) {
+ return null;
+ }
+ return ChessBank.get(uid).parent;
+ }
+}
+export default UidToChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArray.js
new file mode 100644
index 000000000..b795cc3f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArray.js
@@ -0,0 +1,21 @@
+var GetEmptyTileXYArray = function (tileZ, out) {
+ if (tileZ === undefined) {
+ tileZ = 0;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+
+ for (var tileY = 0; tileY < this.height; tileY++) {
+ for (var tileX = 0; tileX < this.width; tileX++) {
+ if (this.isEmptyTileXYZ(tileX, tileY, tileZ)) {
+ out.push({
+ x: tileX,
+ y: tileY
+ });
+ }
+ }
+ }
+ return out;
+}
+export default GetEmptyTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArrayInRange.js b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArrayInRange.js
new file mode 100644
index 000000000..ce28151f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetEmptyTileXYArrayInRange.js
@@ -0,0 +1,26 @@
+var GetEmptyTileXYArrayInRange = function (centerTileXY, radius, tileZ, out) {
+ if (radius === undefined) {
+ radius = 1;
+ }
+ if (tileZ === undefined) {
+ tileZ = 0;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+ this.grid.ringToTileXYArray(centerTileXY, radius, globTileXYArray);
+ var tileXY;
+ for (var i = 0, cnt = globTileXYArray.length; i < cnt; i++) {
+ tileXY = globTileXYArray[i];
+ if (this.isEmptyTileXYZ(tileXY.x, tileXY.y, tileZ)) {
+ out.push(tileXY);
+ }
+ }
+ globTileXYArray.length = 0;
+ return out;
+}
+
+var globTileXYArray = [];
+export default GetEmptyTileXYArrayInRange;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXY.js
new file mode 100644
index 000000000..d94f41ba0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXY.js
@@ -0,0 +1,44 @@
+import Random from '../../../utils/math/Between.js';
+import GetRandomItem from '../../../utils/array/GetRandom.js';
+
+var GetRandomEmptyTileXY = function (tileZ, out) {
+ if (tileZ === undefined) {
+ tileZ = 0;
+ }
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ var tileX, tileY;
+ var isOccupied = true;
+ var tryCount = 20;
+ while (isOccupied && (tryCount > 0)) {
+ tileX = Random(0, this.width - 1);
+ tileY = Random(0, this.height - 1);
+ isOccupied = (this.tileXYZToChess(tileX, tileY, tileZ) !== null);
+ tryCount--;
+ }
+
+ if (!isOccupied) {
+ out.x = tileX;
+ out.y = tileY;
+ return out;
+ } else {
+ globTileXYArray = this.getEmptyTileXYArray(tileZ, globTileXYArray);
+ if (globTileXYArray.length === 0) {
+ return null;
+ } else {
+ var tileXY = GetRandomItem(globTileXYArray);
+ out.x = tileXY.x;
+ out.y = tileXY.y;
+ globTileXYArray.length = 0;
+ return out;
+ }
+ }
+}
+
+var globTileXYArray = [];
+var globTileXY = {};
+export default GetRandomEmptyTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXYInRange.js b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXYInRange.js
new file mode 100644
index 000000000..7416635d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/GetRandomEmptyTileXYInRange.js
@@ -0,0 +1,37 @@
+import Shuffle from '../../../utils/array/Shuffle.js';
+
+var GetRandomEmptyTileXYInRange = function (centerTileXY, radius, tileZ, out) {
+ if (radius === undefined) {
+ radius = 1;
+ }
+ if (tileZ === undefined) {
+ tileZ = 0;
+ }
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+ this.grid.ringToTileXYArray(centerTileXY, radius, globTileXYArray);
+ Shuffle(globTileXYArray);
+
+ var tileXY;
+ for (var i = 0, cnt = globTileXYArray.length; i < cnt; i++) {
+ tileXY = globTileXYArray[i];
+ if (this.isEmptyTileXYZ(tileXY.x, tileXY.y, tileZ)) {
+ out.x = tileXY.x;
+ out.y = tileXY.y;
+ globTileXYArray.length = 0;
+ return out;
+ }
+ }
+
+ globTileXYArray.length = 0;
+ return null;
+}
+
+var globTileXYArray = [];
+var globTileXY = {};
+export default GetRandomEmptyTileXYInRange;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/empty/IsEmptyTileXYZ.js b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/IsEmptyTileXYZ.js
new file mode 100644
index 000000000..199146613
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/empty/IsEmptyTileXYZ.js
@@ -0,0 +1,6 @@
+var IsEmptyTileXYZ = function (tileX, tileY, tileZ) {
+ // TileXY is inside board, and TileXYZ has no chess
+ return this.contains(tileX, tileY) && !this.contains(tileX, tileY, tileZ);
+}
+
+export default IsEmptyTileXYZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/EmitChessEvent.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/EmitChessEvent.js
new file mode 100644
index 000000000..aa533454f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/EmitChessEvent.js
@@ -0,0 +1,39 @@
+import InputCandidate from './../../../utils/input/InputCandidate.js';
+
+var EmitChessEvent = function (boardEventName, chessEventName, board, tileX, tileY, pointer) {
+ if ((tileX == null) || (tileY == null)) {
+ return;
+ }
+
+ var boardEventCallback = (typeof (boardEventName) !== 'string') ? boardEventName : undefined;
+ var chessEventCallback = (typeof (chessEventName) !== 'string') ? chessEventName : undefined;
+
+ var gameObjects = board.tileXYToChessArray(tileX, tileY, globChessArray);
+ // Fire events
+ var gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ gameObject = gameObjects[i];
+ if (!InputCandidate(gameObject)) {
+ continue;
+ }
+
+ if (gameObject.emit) {
+ if (!chessEventCallback) {
+ gameObject.emit(chessEventName, pointer);
+ } else {
+ chessEventCallback(gameObject);
+ }
+ }
+
+ if (!boardEventCallback) {
+ board.emit(boardEventName, pointer, gameObject);
+ } else {
+ boardEventCallback(gameObject);
+ }
+ }
+ globChessArray.length = 0;
+}
+
+var globChessArray = [];
+
+export default EmitChessEvent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/Input.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/Input.js
new file mode 100644
index 000000000..55e24b548
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/Input.js
@@ -0,0 +1,112 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import TouchZone from './TouchZone.js';
+
+import OnPointerDown from './OnPointerDown.js';
+import OnPointerUp from './OnPointerUp.js';
+import OnPointerMove from './OnPointerMove.js';
+
+import InstallTap from './InstallTap.js';
+import InstallPress from './InstallPress.js';
+import InstallSwipe from './InstallSwipe.js';
+
+class Input {
+ constructor(board, config) {
+ var enable = GetValue(config, 'enable', true);
+ var useTouchZone = GetValue(config, 'useTouchZone', true);
+
+ var scene = board.scene;
+
+ this.board = board;
+ this.touchZone = undefined;
+ this._enable = true;
+ this.pointer = null;
+ this.tilePosition = { x: undefined, y: undefined };
+ this.prevTilePosition = { x: undefined, y: undefined };
+
+ if (useTouchZone) {
+ var touchZone = new TouchZone(scene);
+ touchZone.on('pointerdown', OnPointerDown, this);
+ touchZone.on('pointerup', OnPointerUp, this);
+ touchZone.on('pointermove', OnPointerMove, this);
+ this.touchZone = touchZone;
+
+ } else {
+ scene.input.on('pointerdown', OnPointerDown, this);
+ scene.input.on('pointerup', OnPointerUp, this);
+ scene.input.on('pointermove', OnPointerMove, this);
+
+ }
+
+ this.tap = InstallTap.call(this);
+ this.press = InstallPress.call(this);
+ this.swipe = InstallSwipe.call(this);
+
+ board.once('destroy', this.onBoardDestroy, this);
+
+ this.setEnable(enable);
+ }
+
+ destroy(fromScene) {
+ this.tap.destroy(fromScene);
+ this.press.destroy(fromScene);
+ this.swipe.destroy(fromScene);
+
+ if (this.touchZone) {
+ this.touchZone.destroy(fromScene);
+ this.touchZone = undefined;
+
+ } else {
+ var scene = this.board.scene;
+ if (scene) {
+ scene.input.off('pointerdown', OnPointerDown, this);
+ scene.input.off('pointerup', OnPointerUp, this);
+ scene.input.off('pointermove', OnPointerMove, this);
+ }
+
+ }
+
+ this.board = undefined;
+
+ // board.off('destroy', this.onBoardDestroy, this);
+ }
+
+ onBoardDestroy(parent, fromScene) {
+ this.destroy(fromScene);
+ }
+
+ get enable() {
+ return this._enable;
+ }
+
+ set enable(e) {
+ if (this._enable === e) {
+ return;
+ }
+
+ if (!e) {
+ this.pointer = null;
+ }
+ this._enable = e;
+
+ if (this.touchZone) {
+ if (e) {
+ this.touchZone.setInteractive();
+ } else {
+ this.touchZone.disableInteractive();
+ }
+ }
+ this.tap.setEnable(e);
+ this.press.setEnable(e);
+ this.swipe.setEnable(e);
+ }
+
+ setEnable(e) {
+ if (e === undefined) {
+ e = true;
+ }
+
+ this.enable = e;
+ return this;
+ }
+}
+export default Input;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallPress.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallPress.js
new file mode 100644
index 000000000..a60b9a9d7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallPress.js
@@ -0,0 +1,54 @@
+import Press from '../../../input/gestures/press/Press.js';
+import EmitChessEvent from './EmitChessEvent.js';
+
+var InstallPress = function () {
+ var touchZone = (this.touchZone) ? this.touchZone : this.board.scene;
+ var press = new Press(touchZone);
+ press
+ .on('pressstart', OnPressStart, this)
+ .on('pressend', OnPressEnd, this);
+
+ return press;
+}
+
+var OnPressStart = function (press) {
+ var board = this.board;
+ // Get touched tileX, tileY
+ var tileXY = board.worldXYToTileXY(press.worldX, press.worldY);
+ var tileX = tileXY.x,
+ tileY = tileXY.y;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+
+ board.emit('tilepressstart', press, tileXY);
+
+ EmitChessEvent(
+ 'gameobjectpressstart',
+ 'board.pressstart',
+ board, tileX, tileY,
+ press
+ );
+}
+
+var OnPressEnd = function (press) {
+ var board = this.board;
+ // Get touched tileX, tileY
+ var tileXY = board.worldXYToTileXY(press.worldX, press.worldY);
+ var tileX = tileXY.x,
+ tileY = tileXY.y;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+
+ board.emit('tilepressend', press, tileXY);
+
+ EmitChessEvent(
+ 'gameobjectpressend',
+ 'board.pressend',
+ board, tileX, tileY,
+ press
+ );
+}
+
+export default InstallPress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallSwipe.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallSwipe.js
new file mode 100644
index 000000000..a06a816bb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallSwipe.js
@@ -0,0 +1,35 @@
+import Swipe from '../../../input/gestures/swipe/Swipe.js';
+import EmitChessEvent from './EmitChessEvent.js';
+
+var InstallSwipe = function () {
+ var touchZone = (this.touchZone) ? this.touchZone : this.board.scene;
+ var swipe = new Swipe(touchZone);
+ swipe
+ .on('swipe', OnSwipe, this);
+
+ return swipe;
+}
+
+var OnSwipe = function (swipe) {
+ var board = this.board;
+ // Get touched tileX, tileY
+ var tileXY = board.worldXYToTileXY(swipe.worldX, swipe.worldY);
+ var tileX = tileXY.x,
+ tileY = tileXY.y;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+
+ swipe.direction = board.angleSnapToDirection(tileXY, swipe.getVelocityAngle());
+
+ board.emit('tileswipe', swipe, tileXY);
+
+ EmitChessEvent(
+ 'gameobjectswipe',
+ 'board.swipe',
+ board, tileX, tileY,
+ swipe
+ );
+}
+
+export default InstallSwipe;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallTap.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallTap.js
new file mode 100644
index 000000000..bb171f115
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/InstallTap.js
@@ -0,0 +1,40 @@
+import Tap from '../../../input/gestures/tap/Tap.js';
+import EmitChessEvent from './EmitChessEvent.js';
+
+var InstallTap = function () {
+ var touchZone = (this.touchZone) ? this.touchZone : this.board.scene;
+ var tap = new Tap(touchZone);
+ tap.on('tap', OnTap, this);
+ return tap;
+}
+
+var OnTap = function (tap) {
+ var board = this.board;
+ // Get touched tileX, tileY
+ var tileXY = board.worldXYToTileXY(tap.worldX, tap.worldY);
+ var tileX = tileXY.x,
+ tileY = tileXY.y;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+
+ board.emit('tiletap', tap, tileXY);
+ board.emit(`tile${tap.tapsCount}tap`, tap, tileXY);
+
+ var boardEventCallback = function (gameObject) {
+ board.emit('gameobjecttap', tap, gameObject);
+ board.emit(`gameobject${tap.tapsCount}tap`, tap, gameObject);
+ }
+ var chessEventCallback = function (gameObject) {
+ gameObject.emit('board.tap', tap);
+ gameObject.emit(`board.${tap.tapsCount}tap`, tap);
+ }
+ EmitChessEvent(
+ boardEventCallback,
+ chessEventCallback,
+ board, tileX, tileY,
+ tap
+ );
+}
+
+export default InstallTap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerDown.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerDown.js
new file mode 100644
index 000000000..28f9e45d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerDown.js
@@ -0,0 +1,46 @@
+import EmitChessEvent from './EmitChessEvent.js';
+
+var OnPointerDown = function (pointer) {
+ if (!this.enable) {
+ return;
+ }
+ if (!pointer.isDown) {
+ return;
+ }
+
+ var board = this.board;
+ if (this.pointer === null) { // Catch new touch pointer
+ this.pointer = pointer;
+ }
+ // Get touched tileX, tileY
+ var out = board.worldXYToTileXY(pointer.worldX, pointer.worldY, true);
+ var tileX = out.x,
+ tileY = out.y;
+ this.prevTilePosition.x = this.tilePosition.x;
+ this.prevTilePosition.y = this.tilePosition.y;
+ this.tilePosition.x = tileX;
+ this.tilePosition.y = tileY;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+ board.emit('tiledown', pointer, this.tilePosition);
+ board.emit('tileover', pointer, this.tilePosition);
+
+ var boardEventCallback = function (gameObject) {
+ board.emit('gameobjectdown', pointer, gameObject);
+ board.emit('gameobjectover', pointer, gameObject);
+ }
+ var chessEventCallback = function (gameObject) {
+ gameObject.emit('board.pointerdown', pointer);
+ gameObject.emit('board.pointerover', pointer);
+ }
+
+ EmitChessEvent(
+ boardEventCallback,
+ chessEventCallback,
+ board, tileX, tileY,
+ pointer
+ );
+};
+
+export default OnPointerDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerMove.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerMove.js
new file mode 100644
index 000000000..1980a75ea
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerMove.js
@@ -0,0 +1,74 @@
+import AreTileXYEqual from '../../utils/AreTileXYEqual.js';
+import EmitChessEvent from './EmitChessEvent.js';
+
+var OnPointerMove = function (pointer) {
+ if (!this.enable) {
+ return;
+ }
+
+ var board = this.board;
+ // Get touched tileX, tileY
+ var out = board.worldXYToTileXY(pointer.worldX, pointer.worldY, true);
+ if (AreTileXYEqual(this.tilePosition, out)) {
+ // Tile position dose not change
+ return;
+ }
+
+ this.prevTilePosition.x = this.tilePosition.x;
+ this.prevTilePosition.y = this.tilePosition.y;
+ // prevTilePosition might be undefined at beginning
+ if ((this.prevTilePosition.x != null) && (this.prevTilePosition.y != null)) {
+ board.emit('tileout', pointer, this.prevTilePosition);
+ }
+
+ var tileX = out.x,
+ tileY = out.y;
+ this.tilePosition.x = tileX;
+ this.tilePosition.y = tileY;
+ if (!board.contains(tileX, tileY)) {
+ // Move outside
+ EmitChessEvent(
+ 'gameobjectout',
+ 'board.pointerout',
+ board, this.prevTilePosition.x, this.prevTilePosition.y,
+ pointer
+ );
+
+ if (this.pointer === pointer) { // Release touch pointer
+ this.pointer = null;
+ }
+ return;
+ }
+
+ if (this.pointer === null) { // Catch new touch pointer
+ this.pointer = pointer;
+ }
+
+ board.emit('tilemove', pointer, this.tilePosition);
+ board.emit('tileover', pointer, this.tilePosition);
+
+ EmitChessEvent(
+ 'gameobjectout',
+ 'board.pointerout',
+ board, this.prevTilePosition.x, this.prevTilePosition.y,
+ pointer
+ );
+
+ var boardEventCallback = function (gameObject) {
+ board.emit('gameobjectmove', pointer, gameObject);
+ board.emit('gameobjectover', pointer, gameObject);
+ }
+ var chessEventCallback = function (gameObject) {
+ gameObject.emit('board.pointermove', pointer);
+ gameObject.emit('board.pointerover', pointer);
+ }
+
+ EmitChessEvent(
+ boardEventCallback,
+ chessEventCallback,
+ board, tileX, tileY,
+ pointer
+ );
+};
+
+export default OnPointerMove;
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerUp.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerUp.js
new file mode 100644
index 000000000..9dc1cce3d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/OnPointerUp.js
@@ -0,0 +1,44 @@
+import EmitChessEvent from './EmitChessEvent.js';
+
+var OnPointerUp = function (pointer) {
+ if (!this.enable) {
+ return;
+ }
+
+ var board = this.board;
+ // Get touched tileX, tileY
+ var out = board.worldXYToTileXY(pointer.worldX, pointer.worldY, true);
+ var tileX = out.x,
+ tileY = out.y;
+ this.prevTilePosition.x = this.tilePosition.x;
+ this.prevTilePosition.y = this.tilePosition.y;
+ this.tilePosition.x = tileX;
+ this.tilePosition.y = tileY;
+ if (!board.contains(tileX, tileY)) {
+ return;
+ }
+ board.emit('tileup', pointer, this.tilePosition);
+ board.emit('tileout', pointer, this.prevTilePosition);
+
+ var boardEventCallback = function (gameObject) {
+ board.emit('gameobjectup', pointer, gameObject);
+ board.emit('gameobjectout', pointer, gameObject);
+ }
+ var chessEventCallback = function (gameObject) {
+ gameObject.emit('board.pointerup', pointer);
+ gameObject.emit('board.pointerout', pointer);
+ }
+
+ EmitChessEvent(
+ boardEventCallback,
+ chessEventCallback,
+ board, tileX, tileY,
+ pointer
+ );
+
+ if (this.pointer === pointer) { // Release touch pointer
+ this.pointer = null;
+ }
+};
+
+export default OnPointerUp;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/SetInteractive.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/SetInteractive.js
new file mode 100644
index 000000000..650171e00
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/SetInteractive.js
@@ -0,0 +1,15 @@
+import Input from './Input.js';
+
+var SetInteractive = function (config) {
+ // Input
+ if (!this.input) {
+ this.input = new Input(this, config);
+ } else {
+ var enable = (config === false) ? false : true;
+ this.input.setEnable(enable);
+ }
+
+ return this;
+};
+
+export default SetInteractive;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/input/TouchZone.js b/ui/src/phaser3-rex-plugins/plugins/board/board/input/TouchZone.js
new file mode 100644
index 000000000..f49c89806
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/input/TouchZone.js
@@ -0,0 +1,14 @@
+const Zone = Phaser.GameObjects.Zone;
+
+class TouchZone extends Zone {
+ constructor(scene) {
+ super(scene, 0, 0, 1, 1);
+ scene.add.existing(this); // Add to scene
+ this.setScrollFactor(0);
+ this.setInteractive({
+ hitArea: {},
+ hitAreaCallback: function () { return true; }
+ });
+ }
+}
+export default TouchZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/AreNeighbors.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/AreNeighbors.js
new file mode 100644
index 000000000..068e9777c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/AreNeighbors.js
@@ -0,0 +1,4 @@
+var AreNeighbors = function(chessA, chessB) {
+ return (this.getNeighborChessDirection(chessA, chessB) !== null);
+}
+export default AreNeighbors;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChess.js
new file mode 100644
index 000000000..865989fe5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChess.js
@@ -0,0 +1,43 @@
+var GetNeighborChess = function (tileXYZ, directions, neighborTileZ, out) {
+ var tileXYZ = this.chessToTileXYZ(tileXYZ);
+ if (tileXYZ === null) {
+ return null;
+ }
+
+ if (neighborTileZ == null) {
+ neighborTileZ = tileXYZ.z;
+ }
+
+ var typeOfDirection = typeof (directions);
+ if (
+ (typeOfDirection === 'number') ||
+ ((typeOfDirection === 'string') && (directions.indexOf(',') === -1))
+ ) {
+ // 1 direction
+ var dir = directions;
+ var neighborTileXY = this.getNeighborTileXY(tileXYZ, dir, true);
+ if (neighborTileXY === null) {
+ return null;
+ }
+ return this.tileXYZToChess(neighborTileXY.x, neighborTileXY.y, neighborTileZ);
+ } else {
+ // directions array
+ if (out === undefined) {
+ out = [];
+ }
+ this.getNeighborTileXY(tileXYZ, directions, globTileXYArray);
+ var neighborChess;
+ for (var i = 0, cnt = globTileXYArray.length; i < cnt; i++) {
+ neighborChess = this.tileXYZToChess(globTileXYArray[i].x, globTileXYArray[i].y, neighborTileZ);
+ if (neighborChess == null) {
+ continue;
+ }
+ out.push(neighborChess);
+ }
+ globTileXYArray.length = 0;
+ return out;
+ }
+}
+
+var globTileXYArray = [];
+export default GetNeighborChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChessDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChessDirection.js
new file mode 100644
index 000000000..cd9e4291e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborChessDirection.js
@@ -0,0 +1,6 @@
+var GetNeighborChessDirection = function (chess, neighborChess) {
+ var srcTileXYZ = this.chessToTileXYZ(chess);
+ var neighborTileXYZ = this.chessToTileXYZ(neighborChess);
+ return this.getNeighborTileDirection(srcTileXYZ, neighborTileXYZ);
+}
+export default GetNeighborChessDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileDirection.js
new file mode 100644
index 000000000..408e211a7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileDirection.js
@@ -0,0 +1,31 @@
+import AreTileXYEqual from '../../utils/AreTileXYEqual.js';
+
+var GetNeighborTileDirection = function (srcTileXY, neighborTileXY) {
+ if ((srcTileXY === null) || (neighborTileXY === null)) {
+ return null;
+ }
+
+ srcTileXY = this.chessToTileXYZ(srcTileXY);
+ neighborTileXY = this.chessToTileXYZ(neighborTileXY);
+
+ if (AreTileXYEqual(srcTileXY, neighborTileXY)) {
+ return null;
+ }
+
+ var direction = this.grid.getNeighborTileDirection(srcTileXY, neighborTileXY);
+ if (this.wrapMode && (direction === null)) {
+ globNeighborTileXYArray = this.getNeighborTileXY(srcTileXY, null, globNeighborTileXYArray);
+ for (var i = 0, cnt = globNeighborTileXYArray.length; i < cnt; i++) {
+ if (AreTileXYEqual(neighborTileXY, globNeighborTileXYArray[i])) {
+ direction = i;
+ break;
+ }
+ }
+ globNeighborTileXYArray.length = 0;
+ }
+ return direction;
+}
+
+var globNeighborTileXYArray = [];
+
+export default GetNeighborTileDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXY.js
new file mode 100644
index 000000000..36cd98047
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXY.js
@@ -0,0 +1,5 @@
+var GetNeighborTileXY = function (srcTileXY, directions, out) {
+ return this.getTileXYAtDirection(srcTileXY, directions, 1, out);
+};
+
+export default GetNeighborTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXYAtAngle.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXYAtAngle.js
new file mode 100644
index 000000000..876cccab2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetNeighborTileXYAtAngle.js
@@ -0,0 +1,6 @@
+var GetNeighborTileXYAtAngle = function (srcTileXY, angle, out) {
+ var direction = this.angleSnapToDirection(srcTileXY, angle);
+ return this.getTileXYAtDirection(srcTileXY, direction, 1, out);
+};
+
+export default GetNeighborTileXYAtAngle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetTileXYAtDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetTileXYAtDirection.js
new file mode 100644
index 000000000..14fdf97fd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/GetTileXYAtDirection.js
@@ -0,0 +1,90 @@
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import GetValue from '../../../utils/object/GetValue.js';
+
+var GetTileXYAtDirection = function (chess, directions, distance, out) {
+ var srcTileXY = this.chessToTileXYZ(chess);
+ if (!srcTileXY) {
+ return null;
+ }
+
+ if (typeof (directions) === 'string') {
+ if (directions.indexOf(',') === -1) {
+ directions = parseInt(directions);
+ } else {
+ directions = directions.split(',');
+ }
+ }
+
+ var isNumberDirection = (typeof (directions) === 'number');
+ var isNumberDistance = (typeof (distance) === 'number');
+ if (isNumberDirection && isNumberDistance) {
+ // Return a single tileXY
+ out = this.grid.getTileXYAtDirection(srcTileXY.x, srcTileXY.y, directions, distance, out); // directions is a number, distance is a number, return a singl tileXY
+ this.getWrapTileXY(out.x, out.y, out);
+ if ((out.x == null) || (out.y == null)) {
+ out = null;
+ } else {
+ out.direction = directions;
+ }
+
+ } else {
+ // Return an array of tileXY
+ if (out === undefined) {
+ out = [];
+ }
+ if (directions == null) {
+ directions = this.grid.allDirections;
+ }
+
+ var resultTileXY;
+ if (isNumberDirection) { // directions is a number, distance is an object or list
+ if (IsPlainObject(distance)) {
+ var endIdx = GetValue(distance, 'end', 1);
+ var startIdx = GetValue(distance, 'start', (endIdx > 0) ? 1 : -1);
+ var step = GetValue(distance, 'step', ((endIdx >= startIdx) ? 1 : -1));
+ if (startIdx === endIdx) {
+ resultTileXY = this.getTileXYAtDirection(srcTileXY, directions, endIdx); // Return a single tileXY
+ if (resultTileXY !== null) {
+ out.push(resultTileXY);
+ }
+ } else if (startIdx < endIdx) {
+ for (var i = startIdx; i <= endIdx; i += step) {
+ resultTileXY = this.getTileXYAtDirection(srcTileXY, directions, i); // Return a single tileXY
+ if (resultTileXY !== null) {
+ out.push(resultTileXY);
+ }
+ }
+ } else {
+ for (var i = startIdx; i >= endIdx; i += step) {
+ resultTileXY = this.getTileXYAtDirection(srcTileXY, directions, i); // Return a single tileXY
+ if (resultTileXY !== null) {
+ out.push(resultTileXY);
+ }
+ }
+ }
+ } else { // Is array
+ for (var i = 0, cnt = distance.length; i < cnt; i++) {
+ resultTileXY = this.getTileXYAtDirection(srcTileXY, directions, distance[i]);
+ if (resultTileXY !== null) {
+ out.push(resultTileXY);
+ }
+ }
+ }
+ } else { // directions is a list
+ for (var i = 0, cnt = directions.length; i < cnt; i++) {
+ if (isNumberDistance) { // return a single tileXY
+ resultTileXY = this.getTileXYAtDirection(srcTileXY, directions[i], distance);
+ if (resultTileXY !== null) {
+ out.push(resultTileXY);
+ }
+ } else { // append an array of tileXY
+ this.getTileXYAtDirection(srcTileXY, directions[i], distance, out);
+ }
+
+ }
+ }
+ }
+
+ return out;
+}
+export default GetTileXYAtDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/MapNeighobrs.js b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/MapNeighobrs.js
new file mode 100644
index 000000000..dd75af5f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/neighbors/MapNeighobrs.js
@@ -0,0 +1,13 @@
+var MapNeighbors = function (chess, distance, callback, scope) {
+ if (typeof (distance) !== 'number') {
+ scope = callback;
+ callback = distance;
+ distance = 1;
+ }
+
+ var tileXYArray = this.getTileXYAtDirection(chess, undefined, distance);
+ // Array of {x,y,direction}
+ return tileXYArray.map(callback, scope);
+}
+
+export default MapNeighbors;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToChessArray.js
new file mode 100644
index 000000000..583e0e878
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToChessArray.js
@@ -0,0 +1,25 @@
+import IsArray from '../../../utils/object/IsArray.js';
+
+var FilledRingToChessArray = function (centerTileXY, radius, tileZ, nearToFar, out) {
+ if (IsArray(nearToFar)) {
+ out = nearToFar;
+ nearToFar = undefined;
+ }
+
+ if (nearToFar === undefined) {
+ nearToFar = true;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+
+ var level;
+ for (var i = 0; i <= radius; i++) {
+ level = (nearToFar) ? i : (radius - i);
+ this.ringToChessArray(centerTileXY, level, tileZ, out);
+ }
+ return out;
+}
+export default FilledRingToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToTileXYArray.js
new file mode 100644
index 000000000..2a8d0fbcb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/FilledRingToTileXYArray.js
@@ -0,0 +1,25 @@
+import IsArray from '../../../utils/object/IsArray.js';
+
+var FilledRingToTileXYArray = function (centerTileXY, radius, nearToFar, out) {
+ if (IsArray(nearToFar)) {
+ out = nearToFar;
+ nearToFar = undefined;
+ }
+
+ if (nearToFar === undefined) {
+ nearToFar = true;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+
+ var level;
+ for (var i = 0; i <= radius; i++) {
+ level = (nearToFar) ? i : (radius - i);
+ this.ringToTileXYArray(centerTileXY, level, out);
+ }
+ return out;
+}
+export default FilledRingToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToChessArray.js
new file mode 100644
index 000000000..b41a57668
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToChessArray.js
@@ -0,0 +1,27 @@
+var RingToChessArray = function (centerTileXY, radius, tileZ, out) {
+ if (Array.isArray(tileZ)) {
+ out = tileZ;
+ tileZ = undefined;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+ this.grid.ringToTileXYArray(centerTileXY, radius, globTileArray);
+ var tileXY, chess;
+ for (var i = 0, cnt = globTileArray.length; i < cnt; i++) {
+ tileXY = globTileArray[i];
+ chess = this.tileXYZToChess(tileXY.x, tileXY.y, tileZ);
+ if (chess) {
+ out.push(chess);
+ }
+ }
+ globTileArray.length = 0;
+
+ return out;
+}
+
+var globTileArray = [];
+
+export default RingToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToTileXYArray.js
new file mode 100644
index 000000000..4866cd07f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/ring/RingToTileXYArray.js
@@ -0,0 +1,20 @@
+var RingToTileXYArray = function (centerTileXY, radius, out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ centerTileXY = this.chessToTileXYZ(centerTileXY);
+ this.grid.ringToTileXYArray(centerTileXY, radius, globTileArray);
+ var tileXY;
+ for (var i = 0, cnt = globTileArray.length; i < cnt; i++) {
+ tileXY = globTileArray[i];
+ if (this.contains(tileXY.x, tileXY.y)) {
+ out.push(tileXY);
+ }
+ }
+ globTileArray.length = 0;
+ return out;
+}
+
+var globTileArray = [];
+export default RingToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/CircleToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/CircleToTileXYArray.js
new file mode 100644
index 000000000..d19e0e186
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/CircleToTileXYArray.js
@@ -0,0 +1,5 @@
+var CircleToTileXYArray = function (circle, testMode, out) {
+ return this.shapeToTileXYArray(circle, testMode, out);
+}
+
+export default CircleToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/EllipseToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/EllipseToTileXYArray.js
new file mode 100644
index 000000000..567116479
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/EllipseToTileXYArray.js
@@ -0,0 +1,5 @@
+var EllipseToTileXYArray = function (ellipse, testMode, out) {
+ return this.shapeToTileXYArray(ellipse, testMode, out);
+}
+
+export default EllipseToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ForEachTileXYInShape.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ForEachTileXYInShape.js
new file mode 100644
index 000000000..fd1f9fa89
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ForEachTileXYInShape.js
@@ -0,0 +1,89 @@
+import GetValue from '../../../utils/object/GetValue.js';
+
+var ForEachTileXYInShape = function (shape, callback, scope, config) {
+ var testMode = GetValue(config, 'testMode', 0);
+ var searchRectangle = GetValue(config, 'searchRectangle', shape);
+ var order = GetValue(config, 'order', 0);
+
+ if (scope) {
+ callback = callback.bind(scope);
+ }
+
+ globLeftToptileXY = this.worldXYToTileXY(searchRectangle.left, searchRectangle.top, globLeftToptileXY);
+ globRightBottomTileXY = this.worldXYToTileXY(searchRectangle.right, searchRectangle.bottom, globRightBottomTileXY);
+ var left = globLeftToptileXY.x - 1,
+ top = globLeftToptileXY.y - 1,
+ right = globRightBottomTileXY.x + 1,
+ bottom = globRightBottomTileXY.y + 1;
+
+ this.forEachTileXY(
+ function (tileXY, board) {
+ if (IsInShape(board, shape, tileXY.x, tileXY.y, testMode)) {
+ return callback(tileXY, board);
+ }
+ },
+ this,
+ {
+ left: left,
+ right: right,
+ top: top,
+ bottom: bottom
+ }
+ )
+
+ return this;
+};
+
+var IsInShape = function (board, shape, x, y, testMode) {
+ var targetWorldXY = board.tileXYToWorldXY(x, y, true);
+ if (shape.contains(targetWorldXY.x, targetWorldXY.y)) {
+ return true;
+ }
+
+ switch (testMode) {
+ case 1: // Test grid bounds (a rectangle)
+ var rect = board.getGridBounds(x, y, true);
+ return OverlapRectangle(shape, rect);
+
+ case 2: // Test grid points
+ var points = board.getGridPoints(x, y, true);
+ return ContainsAnyPoint(shape, points);
+
+ default:
+ return false;
+ }
+}
+
+var OverlapRectangle = function (shape, rectangle) {
+ var top = rectangle.top,
+ bottom = rectangle.bottom,
+ left = rectangle.left,
+ right = rectangle.right;
+ if (shape.contains(left, top)) {
+ return true;
+ }
+ if (shape.contains(left, bottom)) {
+ return true;
+ }
+ if (shape.contains(right, top)) {
+ return true;
+ }
+ if (shape.contains(right, bottom)) {
+ return true;
+ }
+ return false;
+}
+
+var ContainsAnyPoint = function (shape, points) {
+ for (var i = 0, cnt = points.length; i < cnt; i++) {
+ var point = points[i];
+ if (shape.contains(point.x, point.y)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+var globLeftToptileXY, globRightBottomTileXY;
+
+export default ForEachTileXYInShape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/LineToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/LineToTileXYArray.js
new file mode 100644
index 000000000..77c1c97a9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/LineToTileXYArray.js
@@ -0,0 +1,42 @@
+import DistanceBetween from '../../../utils/math/distance/DistanceBetween.js';
+import Linear from '../../../utils/math/Linear.js';
+import AreTileXYEqual from '../../utils/AreTileXYEqual.js';
+
+var LineToTileXYArray = function (startX, startY, endX, endY, out) {
+ if (typeof (startX) !== 'number') {
+ var line = startX;
+ out = startY;
+ startX = line.x1;
+ startY = line.y1;
+ endX = line.x2;
+ endY = line.y2;
+ }
+
+ if (out === undefined) {
+ out = [];
+ }
+
+ var totalDistance = DistanceBetween(startX, startY, endX, endY);
+ var gridSize = Math.min(this.grid.cellWidth, this.grid.cellHeight);
+ var quantity = Math.ceil(totalDistance / (gridSize / 4)),
+ t;
+ var worldX, worldY;
+ var preTileXY, tileXY;
+ for (var i = 0; i <= quantity; i++) {
+ t = i / quantity;
+ worldX = Linear(startX, endX, t);
+ worldY = Linear(startY, endY, t);
+ tileXY = this.worldXYToTileXY(worldX, worldY);
+ if (!this.contains(tileXY.x, tileXY.y)) {
+ continue;
+ }
+ if (preTileXY && AreTileXYEqual(preTileXY, tileXY)) {
+ continue;
+ }
+
+ out.push(tileXY);
+ preTileXY = tileXY;
+ }
+ return out;
+}
+export default LineToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/PolygonToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/PolygonToTileXYArray.js
new file mode 100644
index 000000000..9460396a6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/PolygonToTileXYArray.js
@@ -0,0 +1,18 @@
+import GetAABB from '../../../utils/geom/polygon/GetAABB.js';
+
+var PolygonToTileXYArray = function (polygon, testMode, out) {
+ if (Array.isArray(testMode)) {
+ out = testMode;
+ testMode = undefined;
+ }
+ globSearchRectangle = GetAABB(polygon, globSearchRectangle);
+ var config = {
+ testMode: testMode,
+ searchRectangle: globSearchRectangle
+ }
+ return this.shapeToTileXYArray(polygon, config, out);
+}
+
+var globSearchRectangle;
+
+export default PolygonToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/RectangleToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/RectangleToTileXYArray.js
new file mode 100644
index 000000000..245704e23
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/RectangleToTileXYArray.js
@@ -0,0 +1,5 @@
+var RectangleToTileXYArray = function (rectangle, testMode, out) {
+ return this.shapeToTileXYArray(rectangle, testMode, out);
+}
+
+export default RectangleToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ShapeToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ShapeToTileXYArray.js
new file mode 100644
index 000000000..dff6f6a36
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/ShapeToTileXYArray.js
@@ -0,0 +1,29 @@
+var ShapeToTileXYArray = function (shape, config, out) {
+ if (typeof (config) === 'number') {
+ config = {
+ testMode: config
+ }
+ }
+
+ if (Array.isArray(config)) {
+ out = config;
+ config = undefined;
+ }
+
+ if (out === undefined) {
+ out = [];
+ }
+
+ this.forEachTileXYInShape(
+ shape,
+ function (tileXY) {
+ out.push({ x: tileXY.x, y: tileXY.y });
+ },
+ undefined,
+ config
+ );
+
+ return out;
+};
+
+export default ShapeToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/shape/TriangleToTileXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/TriangleToTileXYArray.js
new file mode 100644
index 000000000..7c1282361
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/shape/TriangleToTileXYArray.js
@@ -0,0 +1,5 @@
+var TriangleToTileXYArray = function (triangle, testMode, out) {
+ return this.shapeToTileXYArray(triangle, testMode, out);
+}
+
+export default TriangleToTileXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ChessToTileXYZ.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ChessToTileXYZ.js
new file mode 100644
index 000000000..718345a01
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ChessToTileXYZ.js
@@ -0,0 +1,21 @@
+import IsUID from '../../chess/IsUID.js';
+import IsChess from '../../chess/IsChess';
+import GetChessUID from '../../chess/GetChessUID.js';
+import IsTileXYZ from '../../utils/IsTileXYZ.js';
+
+var ChessToTileXYZ = function (chess) {
+ if (!chess) {
+ return null;
+ }
+
+ // chess: chess object, UID, or tileXYZ
+ if (IsUID(chess) || IsChess(chess)) { // UID, or game object
+ var uid = GetChessUID(chess);
+ return this.boardData.getXYZ(uid);
+ } else if (IsTileXYZ(chess)) { // {x, y}, or {x, y, z}
+ return chess;
+ } else {
+ return null;
+ }
+}
+export default ChessToTileXYZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/Contains.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/Contains.js
new file mode 100644
index 000000000..6d1c2c7d6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/Contains.js
@@ -0,0 +1,14 @@
+var Contains = function (tileX, tileY, tileZ) {
+ var result;
+ if (this.infinityMode) {
+ result = true;
+ } else {
+ result = (tileX >= 0) && (tileX < this.width) && (tileY >= 0) && (tileY < this.height);
+ }
+ if (result && (tileZ !== undefined)) {
+ result = this.boardData.contains(tileX, tileY, tileZ);
+ }
+ return result;
+};
+
+export default Contains;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/DirectionBetween.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/DirectionBetween.js
new file mode 100644
index 000000000..a137db456
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/DirectionBetween.js
@@ -0,0 +1,9 @@
+var DirectionBetween = function (chessA, chessB, round) {
+ if (round === undefined) {
+ round = true;
+ }
+ var tileA = this.chessToTileXYZ(chessA);
+ var tileB = this.chessToTileXYZ(chessB);
+ return this.grid.directionBetween(tileA, tileB, round);
+}
+export default DirectionBetween;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ForEachTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ForEachTileXY.js
new file mode 100644
index 000000000..8b2a1ab40
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/ForEachTileXY.js
@@ -0,0 +1,110 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import Clamp from '../../../utils/math/Clamp.js';
+
+var ForEachTileXY = function (callback, scope, config) {
+ if (typeof (config) === 'number') {
+ config = {
+ order: config
+ }
+ }
+
+ var lastX = this.width - 1,
+ lastY = this.height - 1;
+ var order = GetValue(config, 'order', 0);
+ var left = GetValue(config, 'left', 0);
+ var right = GetValue(config, 'right', lastX);
+ var top = GetValue(config, 'top', 0);
+ var bottom = GetValue(config, 'bottom', lastY);
+
+ if (!this.infinityMode) {
+ left = Clamp(left, 0, lastX);
+ top = Clamp(top, 0, lastY);
+ right = Clamp(right, 0, lastX);
+ bottom = Clamp(bottom, 0, lastY);
+ }
+
+ switch (order) {
+ case 0: // x+,y+
+ var isBreak;
+ for (var y = top; y <= bottom; y++) {
+ for (var x = left; x <= right; x++) {
+ globTileXY.x = x;
+ globTileXY.y = y;
+ if (scope) {
+ isBreak = callback.call(scope, globTileXY, this);
+ } else {
+ isBreak = callback(globTileXY, this);
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 1: // x-,y+
+ var isBreak;
+ for (var y = top; y <= bottom; y++) {
+ for (var x = right; x >= left; x--) {
+ globTileXY.x = x;
+ globTileXY.y = y;
+ if (scope) {
+ isBreak = callback.call(scope, globTileXY, this);
+ } else {
+ isBreak = callback(globTileXY, this);
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 2: // y+,x+
+ var isBreak;
+ for (var x = left; x <= right; x++) {
+ for (var y = top; y <= bottom; y++) {
+ globTileXY.x = x;
+ globTileXY.y = y;
+ if (scope) {
+ isBreak = callback.call(scope, globTileXY, this);
+ } else {
+ isBreak = callback(globTileXY, this);
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 3: // y-,x+
+ var isBreak;
+ for (var x = left; x <= right; x++) {
+ for (var y = bottom; y >= top; y--) {
+ globTileXY.x = x;
+ globTileXY.y = y;
+ if (scope) {
+ isBreak = callback.call(scope, globTileXY, this);
+ } else {
+ isBreak = callback(globTileXY, this);
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+ }
+ return this;
+};
+
+var globTileXY = {
+ x: 0,
+ y: 0
+}
+
+export default ForEachTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetDistance.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetDistance.js
new file mode 100644
index 000000000..3170de858
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetDistance.js
@@ -0,0 +1,6 @@
+var GetDistance = function (tileA, tileB, roughMode) {
+ tileA = this.chessToTileXYZ(tileA);
+ tileB = this.chessToTileXYZ(tileB);
+ return this.grid.getDistance(tileA, tileB, roughMode);
+}
+export default GetDistance;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetOppositeDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetOppositeDirection.js
new file mode 100644
index 000000000..067629561
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetOppositeDirection.js
@@ -0,0 +1,11 @@
+var GetOppositeDirection = function (tileX, tileY, direction) {
+ if (tileX && (typeof (tileX) !== 'number')) {
+ direction = tileY;
+ var chess = tileX;
+ var tileXY = this.chessToTileXYZ(chess);
+ tileX = tileXY.x;
+ tileY = tileXY.y;
+ }
+ return this.grid.getOppositeDirection(tileX, tileY, direction);
+}
+export default GetOppositeDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetWrapTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetWrapTileXY.js
new file mode 100644
index 000000000..09d5d9783
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/GetWrapTileXY.js
@@ -0,0 +1,29 @@
+import Wrap from '../../../utils/math/Wrap.js';
+
+var GetWrapTileXY = function (tileX, tileY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ if (this.wrapMode) {
+ tileX = Wrap(tileX, 0, this.width);
+ } else if ((!this.infinityMode) &&
+ ((tileX < 0) || (tileX >= this.width))) {
+ tileX = null;
+ }
+ if (this.wrapMode) {
+ tileY = Wrap(tileY, 0, this.height);
+ } else if ((!this.infinityMode) &&
+ ((tileY < 0) || (tileY >= this.height))) {
+ tileY = null;
+ }
+ out.x = tileX;
+ out.y = tileY;
+ return out;
+}
+
+var globTileXY = {};
+
+export default GetWrapTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/IsDirectionInCone.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/IsDirectionInCone.js
new file mode 100644
index 000000000..039d8b5f6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/IsDirectionInCone.js
@@ -0,0 +1,14 @@
+var IsDirectionInCone = function (chessA, chessB, face, cone) {
+ var tileXYA = this.chessToTileXYZ(chessA);
+ var tileXYB = this.chessToTileXYZ(chessB);
+
+ var savedDirections = this.grid.directions; // Save directions
+ this.grid.setDirectionMode(this.sides);
+ var direction = this.grid.directionBetween(tileXYA, tileXYB, false);
+ this.grid.setDirectionMode(savedDirections); // Restore directions
+
+ var deltaDirection = Math.abs(direction - face);
+ deltaDirection = Math.min(deltaDirection, this.grid.directions - deltaDirection);
+ return (deltaDirection <= (cone / 2));
+}
+export default IsDirectionInCone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYArrayToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYArrayToChessArray.js
new file mode 100644
index 000000000..1aaccfe77
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYArrayToChessArray.js
@@ -0,0 +1,21 @@
+var TileXYArrayToChessArray = function (tileXYArray, tileZ, out) {
+ if (Array.isArray(tileZ)) {
+ out = tileZ;
+ tileZ = undefined;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+ var tileZMode = (tileZ != null);
+ var tileXY;
+ for (var i = 0, cnt = tileXYArray.length; i < cnt; i++) {
+ tileXY = tileXYArray[i];
+ if (tileZMode) {
+ out.push(this.tileXYZToChess(tileXY.x, tileXY.y, tileZ));
+ } else {
+ this.tileXYToChessArray(tileXY.x, tileXY.y, out);
+ }
+ }
+ return out;
+}
+export default TileXYArrayToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYToChessArray.js
new file mode 100644
index 000000000..9c3799357
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYToChessArray.js
@@ -0,0 +1,15 @@
+var TileXYToChessArray = function (tileX, tileY, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var tileZToUIDs = this.boardData.getUID(tileX, tileY);
+ if (tileZToUIDs == null) {
+ return out;
+ }
+
+ for (var tileZ in tileZToUIDs) {
+ out.push(this.uidToChess(tileZToUIDs[tileZ]));
+ }
+ return out;
+}
+export default TileXYToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYZToChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYZToChess.js
new file mode 100644
index 000000000..687b51b79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileXYZToChess.js
@@ -0,0 +1,5 @@
+var TileXYZToChess = function (tileX, tileY, tileZ) {
+ var uid = this.boardData.getUID(tileX, tileY, tileZ);
+ return this.uidToChess(uid);
+}
+export default TileXYZToChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileZToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileZToChessArray.js
new file mode 100644
index 000000000..972cd5978
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/tileposition/TileZToChessArray.js
@@ -0,0 +1,16 @@
+var TileZToChessArray = function (tileZ, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var uids = this.boardData.UIDToXYZ;
+ var tileXYZ;
+ for (var uid in uids) {
+ tileXYZ = uids[uid];
+ if (tileXYZ.z !== tileZ) {
+ continue;
+ }
+ out.push(this.uidToChess(uid));
+ }
+ return out;
+}
+export default TileZToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Fit.js b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Fit.js
new file mode 100644
index 000000000..971ab26ee
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Fit.js
@@ -0,0 +1,33 @@
+// Offset tileXYArray to (0,0), and set board size to fit tileXYArray
+var Fit = function (tileXYArray) {
+ // Get minimum tileX, tileY
+ var minX = Infinity;
+ var minY = Infinity;
+ var tileXY;
+ for (var i in tileXYArray) {
+ tileXY = tileXYArray[i];
+ minX = Math.min(minX, tileXY.x);
+ minY = Math.min(minY, tileXY.y);
+ }
+ // Offset tileXYArray to (0,0)
+ if ((minX !== 0) || (minY !== 0)) {
+ for (var i in tileXYArray) {
+ tileXY = tileXYArray[i];
+ this.offset(tileXY, -minX, -minY, tileXY);
+ }
+ }
+
+ // Get maximun tileX, tileY
+ var maxX = -Infinity;
+ var maxY = -Infinity;
+ for (var i in tileXYArray) {
+ tileXY = tileXYArray[i];
+ maxX = Math.max(maxX, tileXY.x);
+ maxY = Math.max(maxY, tileXY.y);
+ }
+ // Set board size
+ this.setBoardWidth(maxX + 1);
+ this.setBoardHeight(maxY + 1);
+ return tileXYArray;
+}
+export default Fit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Mirror.js b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Mirror.js
new file mode 100644
index 000000000..32626b614
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Mirror.js
@@ -0,0 +1,22 @@
+var Mirror = function (tileXY, mode, originTileXY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ if (originTileXY !== undefined) {
+ this.offset(tileXY, -originTileXY.x, -originTileXY.y, out);
+ } else {
+ out.x = tileXY.x;
+ out.y = tileXY.y;
+ }
+ this.grid.mirror(out, mode, out);
+ if (originTileXY !== undefined) {
+ this.offset(out, originTileXY.x, originTileXY.y, out);
+ }
+ return out;
+};
+
+var globTileXY = {};
+export default Mirror;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Offset.js b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Offset.js
new file mode 100644
index 000000000..5bfd1735c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Offset.js
@@ -0,0 +1,18 @@
+var Offset = function (tileXY, OffsetTileX, OffsetTileY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ if ((OffsetTileX === 0) && (OffsetTileY === 0)) {
+ out.x = tileXY.x;
+ out.y = tileXY.y;
+ } else {
+ this.grid.offset(tileXY, OffsetTileX, OffsetTileY, out);
+ }
+ return out;
+};
+
+var globTileXY = {};
+export default Offset;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Rotate.js b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Rotate.js
new file mode 100644
index 000000000..bfe83f8ea
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/transform/Rotate.js
@@ -0,0 +1,22 @@
+var Rotate = function (tileXY, direction, originTileXY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+
+ if (originTileXY !== undefined) {
+ this.offset(tileXY, -originTileXY.x, -originTileXY.y, out);
+ } else {
+ out.x = tileXY.x;
+ out.y = tileXY.y;
+ }
+ this.grid.rotate(out, direction, out);
+ if (originTileXY !== undefined) {
+ this.offset(out, originTileXY.x, originTileXY.y, out);
+ }
+ return out;
+};
+
+var globTileXY = {};
+export default Rotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleBetween.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleBetween.js
new file mode 100644
index 000000000..f90660d5a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleBetween.js
@@ -0,0 +1,15 @@
+import GetAngle from '../../../utils/math/angle/Between.js';
+
+var AngleBetween = function (tileA, tileB) {
+ tileA = this.chessToTileXYZ(tileA);
+ tileB = this.chessToTileXYZ(tileB);
+ var out = this.tileXYToWorldXY(tileA.x, tileA.y, true);
+ var x0 = out.x;
+ var y0 = out.y;
+ out = this.tileXYToWorldXY(tileB.x, tileB.y, true);
+ var x1 = out.x;
+ var y1 = out.y;
+ return GetAngle(x0, y0, x1, y1); // -PI~PI
+}
+
+export default AngleBetween;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleSnapToDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleSnapToDirection.js
new file mode 100644
index 000000000..b81f81d8e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleSnapToDirection.js
@@ -0,0 +1,22 @@
+import RadToDeg from '../../../utils/math/RadToDeg.js';
+import ShortestBetween from '../../../utils/math/angle/ShortestBetween.js';
+
+var AngleSnapToDirection = function (tileXY, angle) {
+ angle = RadToDeg(angle); // -180~180
+ var directions = this.grid.allDirections;
+ var neighborAngle, deltaAngle;
+ var minDeltaAngle = Infinity,
+ direction = undefined;
+ for (var i = 0, cnt = directions.length; i < cnt; i++) {
+ neighborAngle = RadToDeg(this.angleToward(tileXY, directions[i])); // -PI~PI -> -180~180
+ deltaAngle = Math.abs(ShortestBetween(angle, neighborAngle));
+ if (deltaAngle < minDeltaAngle) {
+ minDeltaAngle = deltaAngle;
+ direction = i;
+ }
+ }
+
+ return direction;
+};
+
+export default AngleSnapToDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleToward.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleToward.js
new file mode 100644
index 000000000..c9914e190
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/AngleToward.js
@@ -0,0 +1,22 @@
+var AngleToward = function (tileXY, direction) {
+ if (tileXY === undefined) {
+ tileXY = zeroTileXY;
+ }
+ // Save wrapMode, infinityMode and clear them
+ var wrapModeSave = this.wrapMode;
+ var infinityModeSave = this.infinityMode;
+ this.wrapMode = false;
+ this.infinityMode = true;
+
+ // Get neighborTileXY
+ var neighborTileXY = this.getNeighborTileXY(tileXY, direction, true);
+
+ // Restore wrapMode, infinityMode and clear them
+ this.wrapMode = wrapModeSave;
+ this.infinityMode = infinityModeSave;
+ return this.angleBetween(tileXY, neighborTileXY); // -PI~PI
+}
+
+var zeroTileXY = { x: 0, y: 0 };
+
+export default AngleToward;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetBoardBounds.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetBoardBounds.js
new file mode 100644
index 000000000..96cf52b79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetBoardBounds.js
@@ -0,0 +1,27 @@
+import Rectangle from '../../../utils/geom/rectangle/Rectangle.js';
+import Union from '../../../utils/geom/rectangle/Union.js';
+
+var GetBoardBounds = function (out) {
+ if (out === undefined) {
+ out = new Rectangle();
+ } else if (out === true) {
+ out = globalBounds;
+ }
+
+ var isFirstTile = true;
+ this.forEachTileXY(function (tileXY, board) {
+ var tileBounds = board.getGridBounds(tileXY.x, tileXY.y, true);
+ if (isFirstTile) {
+ out.setTo(tileBounds.x, tileBounds.y, tileBounds.width, tileBounds.height);
+ isFirstTile = false;
+ } else {
+ out = Union(out, tileBounds, out);
+ }
+ });
+
+ return out;
+}
+
+var globalBounds = new Rectangle();
+
+export default GetBoardBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridBounds.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridBounds.js
new file mode 100644
index 000000000..369f9752c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridBounds.js
@@ -0,0 +1,11 @@
+var GetGridBounds = function (tileX, tileY, out) {
+ if (tileX && (typeof (tileX) !== 'number')) {
+ out = tileY;
+ var tileXY = this.chessToTileXYZ(tileX); // tileX is a Chess or TileXY
+ tileX = tileXY.x;
+ tileY = tileXY.y;
+ }
+ return this.grid.getBounds(tileX, tileY, out);
+}
+
+export default GetGridBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridPoints.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridPoints.js
new file mode 100644
index 000000000..d0511292f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GetGridPoints.js
@@ -0,0 +1,10 @@
+var GetGridPoints = function (tileX, tileY, points) {
+ if (tileX && (typeof (tileX) !== 'number')) {
+ points = tileY;
+ var tileXY = this.chessToTileXYZ(tileX); // tileX is a Chess or TileXY
+ tileX = tileXY.x;
+ tileY = tileXY.y;
+ }
+ return this.grid.getGridPoints(tileX, tileY, points);
+}
+export default GetGridPoints;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GridAlign.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GridAlign.js
new file mode 100644
index 000000000..6573c6eb3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/GridAlign.js
@@ -0,0 +1,24 @@
+import IsUID from '../../chess/IsUID.js';
+
+var GridAlign = function (gameObject, tileX, tileY) {
+ if (gameObject === undefined) {
+ var chess = this.getAllChess();
+ for (var i = 0, cnt = chess.length; i < cnt; i++) {
+ this.gridAlign(chess[i]);
+ }
+ } else {
+ if (IsUID(gameObject)) {
+ gameObject = this.uidToChess(gameObject);
+ }
+ if (tileX === undefined) {
+ var tileXYZ = this.chessToTileXYZ(gameObject);
+ tileX = tileXYZ.x;
+ tileY = tileXYZ.y;
+ }
+
+ this.tileXYToWorldXY(tileX, tileY, gameObject);
+ }
+ return this;
+};
+
+export default GridAlign;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsAngleInCone.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsAngleInCone.js
new file mode 100644
index 000000000..ac081229e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsAngleInCone.js
@@ -0,0 +1,17 @@
+import AngleNormalize from '../../../utils/math/angle/Normalize.js';
+import Equal from '../../../utils/math/fuzzy/Equal.js';
+
+var IsAngleInCone = function (chessA, chessB, face, cone) {
+ var tileXYA = this.chessToTileXYZ(chessA);
+ var tileXYB = this.chessToTileXYZ(chessB);
+ var targetAngle = this.angleBetween(tileXYA, tileXYB); // -PI~PI
+ targetAngle = AngleNormalize(targetAngle); // 0~2PI
+ var deltaAngle = Math.abs(targetAngle - face);
+ deltaAngle = Math.min(deltaAngle, PI2 - deltaAngle);
+ var halfCone = cone / 2;
+ return Equal(deltaAngle, halfCone) || (deltaAngle < halfCone);
+}
+
+const PI = Math.PI;
+const PI2 = Math.PI * 2;
+export default IsAngleInCone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsOverlappingPoint.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsOverlappingPoint.js
new file mode 100644
index 000000000..85e149291
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/IsOverlappingPoint.js
@@ -0,0 +1,9 @@
+var IsOverlappingPoint = function (worldX, worldY, tileZ) {
+ if (this.infinityMode && (tileZ === undefined)) {
+ return true;
+ }
+
+ var out = this.worldXYToTileXY(worldX, worldY, true);
+ return this.contains(out.x, out.y, tileZ);
+}
+export default IsOverlappingPoint;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYArrayToWorldXYArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYArrayToWorldXYArray.js
new file mode 100644
index 000000000..7a6817180
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYArrayToWorldXYArray.js
@@ -0,0 +1,13 @@
+var TileXYArrayToWorldXYArray = function (tileXYArray, out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ var tileXY;
+ for (var i = 0, cnt = tileXYArray.length; i < cnt; i++) {
+ tileXY = tileXYArray[i];
+ out.push(this.tileXYToWorldXY(tileXY.x, tileXY.y));
+ }
+ return out;
+};
+export default TileXYArrayToWorldXYArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldX.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldX.js
new file mode 100644
index 000000000..e030839ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldX.js
@@ -0,0 +1,5 @@
+var TileXYToWorldX = function (tileX, tileY) {
+ // console.warn('Use board.tileXYToWorldXY instead of (board.tileXYToWorldX, board.tileXYToWorldY)');
+ return this.tileXYToWorldXY(tileX, tileY, true).x;
+}
+export default TileXYToWorldX;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldXY.js
new file mode 100644
index 000000000..2863f5bfd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldXY.js
@@ -0,0 +1,4 @@
+var TileXYToWorldXY = function (tileX, tileY, out) {
+ return this.grid.getWorldXY(tileX, tileY, out);
+}
+export default TileXYToWorldXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldY.js
new file mode 100644
index 000000000..503083b27
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/TileXYToWorldY.js
@@ -0,0 +1,5 @@
+var TileXYToWorldY = function (tileX, tileY) {
+ // console.warn('Use board.tileXYToWorldXY instead of (board.tileXYToWorldX, board.tileXYToWorldY)');
+ return this.tileXYToWorldXY(tileX, tileY, true).y;
+}
+export default TileXYToWorldY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYSnapToGrid.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYSnapToGrid.js
new file mode 100644
index 000000000..effba5f95
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYSnapToGrid.js
@@ -0,0 +1,15 @@
+var WorldXYSnapToGrid = function (worldX, worldY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globWorldXY;
+ }
+
+ this.worldXYToTileXY(worldX, worldY, out);
+ this.tileXYToWorldXY(out.x, out.y, out);
+ return out;
+};
+
+var globWorldXY = {};
+
+export default WorldXYSnapToGrid;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChess.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChess.js
new file mode 100644
index 000000000..3c72ff567
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChess.js
@@ -0,0 +1,16 @@
+var WorldXYToChess = function (worldX, worldY, tileZ) {
+ var tileXY = this.worldXYToTileXY(worldX, worldY, true);
+ if (tileZ !== undefined) {
+ return this.tileXYZToChess(tileXY.x, tileXY.y, tileZ)
+ } else {
+ var tileZToUIDs = this.boardData.getUID(tileXY.x, tileXY.y);
+ if (tileZToUIDs == null) {
+ return null;
+ }
+ for (var tileZ in tileZToUIDs) {
+ return this.uidToChess(tileZToUIDs[tileZ]);
+ }
+ }
+}
+
+export default WorldXYToChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChessArray.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChessArray.js
new file mode 100644
index 000000000..a3ae5eeae
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToChessArray.js
@@ -0,0 +1,6 @@
+var WorldXYToChessArray = function (worldX, worldY, out) {
+ var tileXY = this.worldXYToTileXY(worldX, worldY, true);
+ return this.tileXYToChessArray(tileXY.x, tileXY.y, out)
+}
+
+export default WorldXYToChessArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileX.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileX.js
new file mode 100644
index 000000000..b425df136
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileX.js
@@ -0,0 +1,5 @@
+var WorldXYToTileX = function (worldX, worldY) {
+ // console.warn('Use board.worldXYToTileXY instead of (board.worldXYToTileX, board.worldXYToTileY)');
+ return this.worldXYToTileXY(worldX, worldY, true).x;
+}
+export default WorldXYToTileX;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileXY.js
new file mode 100644
index 000000000..a043996b3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileXY.js
@@ -0,0 +1,4 @@
+var WorldXYToTileXY = function (worldX, worldY, out) {
+ return this.grid.getTileXY(worldX, worldY, out);
+}
+export default WorldXYToTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileY.js b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileY.js
new file mode 100644
index 000000000..c881adfa8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/board/worldposition/WorldXYToTileY.js
@@ -0,0 +1,5 @@
+var WorldXYToTileY = function (worldX, worldY) {
+ // console.warn('Use board.worldXYToTileXY instead of (board.worldXYToTileX, board.worldXYToTileY)');
+ return this.worldXYToTileXY(worldX, worldY, true).y;
+}
+export default WorldXYToTileY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessBank.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessBank.js
new file mode 100644
index 000000000..a81c34f90
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessBank.js
@@ -0,0 +1,7 @@
+import Bank from '../../bank.js';
+
+var ChessBank = new Bank({
+ uidKey: '$uid',
+ remove: false, // remove uid manually
+});
+export default ChessBank;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.d.ts
new file mode 100644
index 000000000..a3872424c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.d.ts
@@ -0,0 +1,20 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+import Board from '../board/LogicBoard';
+
+export default ChessData;
+
+declare class ChessData extends ComponentBase {
+ readonly $uid: number;
+
+ readonly board: Board;
+
+ readonly tileXYZ: { x: number, y: number, z: number };
+
+ setTileZ(tileZ: number): this;
+
+ getTileDirection(tileX: number, tileY: number): this;
+
+ setBlocker(value?: boolean): this;
+
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.js
new file mode 100644
index 000000000..16f50f07c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/ChessData.js
@@ -0,0 +1,103 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import ChessBank from './ChessBank.js';
+import GetTileDirection from './GetTileDirection.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+
+const uidKey = ChessBank.uidKey;
+
+class Chess extends ComponentBase {
+ constructor(parent, uid) {
+ super(parent, { eventEmitter: false });
+ // this.parent
+
+ ChessBank.add(this, uid); // uid is stored in `this.$uid`
+ this.board = null;
+ this.blocker = false;
+ }
+
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ if (this.board) {
+ this.board.removeChess(this[uidKey]);
+ }
+ ChessBank.remove(this[uidKey]);
+ this.board = null;
+
+ super.shutdown(fromScene);
+ }
+
+ setBoard(board) {
+ this.board = board;
+ return this;
+ }
+
+ get tileXYZ() {
+ if (this.board == null) {
+ return null;
+ }
+ return this.board.chessToTileXYZ(this[uidKey]);
+ }
+
+ setTileZ(tileZ) {
+ if (this.board == null) {
+ return this;
+ }
+ this.board.setChessTileZ(this.parent, tileZ);
+ return this;
+ }
+
+ setBlocker(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.blocker = value;
+ return this;
+ }
+
+ setBlockEdge(direction, value) {
+ if (this.blocker === false) {
+ this.blocker = {};
+ }
+ var blocker = this.blocker;
+ if (IsPlainObject(direction)) {
+ var blockEdges = direction;
+ for (direction in blockEdges) {
+ blocker[direction] = blockEdges[direction];
+ }
+ } else {
+ if (value === undefined) {
+ value = true;
+ }
+ blocker[direction] = value;
+ }
+ return this;
+ }
+
+ getBlockEdge(direction) {
+ var blocker = this.blocker;
+ if (blocker === false) {
+ return false;
+ }
+
+ if (!blocker.hasOwnProperty(direction)) {
+ return false;
+ } else {
+ return blocker[direction];
+ }
+ }
+}
+
+var methods = {
+ getTileDirection: GetTileDirection
+};
+Object.assign(
+ Chess.prototype,
+ methods
+);
+
+export default Chess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessData.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessData.js
new file mode 100644
index 000000000..ecf42672f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessData.js
@@ -0,0 +1,18 @@
+import ChessBank from './ChessBank.js';
+import ChessData from './ChessData.js';
+import IsUID from './IsUID';
+
+var GetChessData = function (gameObject) {
+ // game object or uid
+ if (IsUID(gameObject)) {
+ // uid
+ return ChessBank.get(gameObject);
+ } else {
+ // game object
+ if (!gameObject.hasOwnProperty('rexChess')) {
+ gameObject.rexChess = new ChessData(gameObject);
+ }
+ return gameObject.rexChess;
+ }
+}
+export default GetChessData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessUID.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessUID.js
new file mode 100644
index 000000000..ba2837221
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetChessUID.js
@@ -0,0 +1,16 @@
+import GetChessData from './GetChessData.js';
+import ChessBank from './ChessBank.js';
+import IsUID from './IsUID.js';
+
+const uidKey = ChessBank.uidKey;
+var GetChessUID = function (gameObject) {
+ // Game object or uid
+ var uid;
+ if (IsUID(gameObject)) {
+ uid = gameObject;
+ } else {
+ uid = GetChessData(gameObject)[uidKey];
+ }
+ return uid;
+}
+export default GetChessUID;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/GetTileDirection.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetTileDirection.js
new file mode 100644
index 000000000..2b461edb4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/GetTileDirection.js
@@ -0,0 +1,15 @@
+var GetTileDirection = function(tileX, tileY) {
+ var board = this.board;
+ if (board === null) {
+ return null;
+ }
+ globTileXY.x = tileX;
+ globTileXY.y = tileY;
+ return board.getNeighborTileDirection(this.tileXYZ, globTileXY);
+}
+
+var globTileXY = {
+ x: 0,
+ y: 0
+};
+export default GetTileDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/IsChess.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/IsChess.js
new file mode 100644
index 000000000..f8d40eb06
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/IsChess.js
@@ -0,0 +1,11 @@
+import IsUID from './IsUID.js'
+
+var IsChess = function (chess) {
+ if (IsUID(chess)) { // Number or string
+ return false;
+ } else {
+ return chess && (!!chess.rexChess);
+ }
+}
+
+export default IsChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/chess/IsUID.js b/ui/src/phaser3-rex-plugins/plugins/board/chess/IsUID.js
new file mode 100644
index 000000000..1e16c936b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/chess/IsUID.js
@@ -0,0 +1,5 @@
+var IsUID = function (object) {
+ var type = typeof (object);
+ return (type === 'number') || (type === 'string');
+}
+export default IsUID;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.d.ts
new file mode 100644
index 000000000..bde84e939
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.d.ts
@@ -0,0 +1,6 @@
+import FieldOfView from './FieldOfView';
+
+export default function (
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: FieldOfView.IConfig
+): FieldOfView;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.js
new file mode 100644
index 000000000..0e1d55b09
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Factory.js
@@ -0,0 +1,11 @@
+import FieldOfView from './FieldOfView.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('fieldOfView', function (gameObject, config) {
+ return new FieldOfView(gameObject, config);
+});
+
+SetValue(window, 'RexPlugins.Board.FieldOfView', FieldOfView);
+
+export default FieldOfView;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.d.ts
new file mode 100644
index 000000000..66210bd92
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.d.ts
@@ -0,0 +1,137 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import { TileXYType } from '../types/Position';
+import Board from '../board/Board';
+
+export default FieldOfView;
+
+declare namespace FieldOfView {
+
+ type ConeModeType = 0 | 1 | 'direction' | 'angle';
+
+ type BLOCKER = null;
+ type INFINITY = undefined;
+
+ type PreTestCallbackType = (
+ tileXYArray: TileXYType[],
+ visiblePoints: number | INFINITY,
+ fieldOfView: FieldOfView,
+ ) => boolean;
+
+ type CostCallbackType = (
+ curTile: TileXYType,
+ fieldOfView: FieldOfView,
+ tileXYArray: TileXYType[]
+ ) => number | BLOCKER;
+
+
+ interface IConfig {
+ face?: number,
+ cone?: number | undefined,
+ coneMode?: ConeModeType,
+
+ // pre-test
+ occupiedTest?: boolean,
+ blockerTest?: boolean,
+ preTestCallback: PreTestCallbackType,
+ preTestCallbackScope?: object,
+
+ // cost
+ costCallback: CostCallbackType,
+ costCallbackScope?: object,
+ cost?: number,
+
+ perspective?: boolean,
+
+ debug?: {
+ graphics: Phaser.GameObjects.Graphics,
+ visibleLineColor?: number,
+ invisibleLineColor?: number,
+ log?: boolean,
+ }
+ }
+}
+
+declare class FieldOfView extends ComponentBase {
+ constructor(
+ gameObject: ChessType,
+ config?: FieldOfView.IConfig
+ );
+
+ constructor(
+ config?: FieldOfView.IConfig
+ );
+
+ readonly board: Board;
+
+ setPreTestFunction(
+ callback: FieldOfView.PreTestCallbackType,
+ scope?: object
+ ): this;
+
+ setCostFunction(cost: number): this;
+ setCostFunction(
+ callback: FieldOfView.CostCallbackType,
+ scope?: object
+ ): this;
+
+ isInLOS(
+ chess: ChessType | TileXYType,
+ visiblePoints?: number | FieldOfView.INFINITY,
+ originTileXY?: TileXYType
+ ): boolean;
+
+ findFOV(
+ visiblePoints?: number | FieldOfView.INFINITY,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ findFOV(
+ visiblePoints?: number | FieldOfView.INFINITY,
+ originTileXY?: TileXYType,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ LOS(
+ chess: ChessType | TileXYType,
+ visiblePoints?: number | FieldOfView.INFINITY,
+ originTileXY?: TileXYType
+ ): boolean;
+
+ LOS(
+ chess: (ChessType | TileXYType)[],
+ out?: (ChessType | TileXYType)[],
+ ): (ChessType | TileXYType)[];
+
+
+ LOS(
+ chess: (ChessType | TileXYType)[],
+ originTileXY?: TileXYType,
+ out?: (ChessType | TileXYType)[],
+ ): (ChessType | TileXYType)[];
+
+ LOS(
+ chess: (ChessType | TileXYType)[],
+ visiblePoints?: number | FieldOfView.INFINITY,
+ out?: (ChessType | TileXYType)[],
+ ): (ChessType | TileXYType)[];
+
+ LOS(
+ chess: (ChessType | TileXYType)[],
+ visiblePoints?: number | FieldOfView.INFINITY,
+ originTileXY?: TileXYType,
+ out?: (ChessType | TileXYType)[],
+ ): (ChessType | TileXYType)[];
+
+
+ setFace(direction: number): this;
+ face: number;
+
+ clearDebugGraphics(): this;
+ setDebugLineColor(
+ visibleLineColor?: number | undefined,
+ invisibleLineColor?: number | undefined
+ ): this;
+
+ readonly BLOCKER: FieldOfView.BLOCKER;
+ readonly INFINITY: FieldOfView.INFINITY;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.js
new file mode 100644
index 000000000..dc67af2f8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FieldOfView.js
@@ -0,0 +1,239 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import Methods from './Methods.js';
+import GetChessData from '../chess/GetChessData.js';
+import CONST from './const.js';
+import DegToRad from '../../utils/math/DegToRad.js';
+import AngleNormalize from '../../utils/math/angle/Normalize.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+import GetValue from '../../utils/object/GetValue.js';
+
+const BLOCKER = CONST.BLOCKER;
+const INFINITY = CONST.INFINITY;
+
+class FieldOfView extends ComponentBase {
+ constructor(gameObject, config) {
+ if (IsPlainObject(gameObject)) {
+ config = gameObject;
+ gameObject = undefined;
+ }
+
+ super(gameObject, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this.setChess(gameObject);
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ // Pre-test
+ var occupiedTest = GetValue(o, 'occupiedTest', false);
+ var blockerTest = GetValue(o, 'blockerTest', false);
+ var edgeBlockerTest = GetValue(o, 'edgeBlockerTest', false); // Unsupport now
+ var preTestCallback = GetValue(o, 'preTestCallback', undefined);
+ var preTestCallbackScope = GetValue(o, 'preTestCallbackScope', undefined);
+ // Cost of each tile
+ var costCallback = GetValue(o, 'costCallback', undefined);
+ var costCallbackScope = GetValue(o, 'costCallbackScope', undefined);
+ if (costCallback === undefined) {
+ costCallback = GetValue(o, 'cost', undefined);
+ }
+
+ this.setFace(GetValue(o, 'face', 0));
+ this.setConeMode(GetValue(o, 'coneMode', 0));
+ this.setCone(GetValue(o, 'cone', undefined));
+ this.setOccupiedTest(occupiedTest);
+ this.setBlockerTest(blockerTest);
+ this.setEdgeBlockerTest(edgeBlockerTest);
+ this.setPreTestFunction(preTestCallback, preTestCallbackScope);
+ this.setCostFunction(costCallback, costCallbackScope);
+ this.setPerspectiveEnable(GetValue(o, 'perspective', false));
+ this.setDebugGraphics(GetValue(o, 'debug.graphics', undefined));
+ this.setDebugLineColor(GetValue(o, 'debug.visibleLineColor', 0x00ff00), GetValue(o, 'debug.invisibleLineColor', 0xff0000));
+ this.setDebugLog(GetValue(o, 'debug.log', false));
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.debugGraphics = undefined;
+ this.chessData = undefined;
+
+ super.shutdown(fromScene);
+ }
+
+ setChess(gameObject) {
+ if (gameObject) {
+ this.chessData = GetChessData(gameObject);
+ if (this.parent !== gameObject) {
+ // Remove attatched event from previous gameObject
+ if (this.parent && this.parent.once) {
+ this.parent.off('destroy', this.onParentDestroy, this);
+ }
+ // Attach event
+ this.setParent(gameObject);
+ if (this.parent && this.parent.once) {
+ this.parent.once('destroy', this.onParentDestroy, this);
+ }
+ }
+ } else {
+ this.setParent();
+ this.chessData = undefined;
+ }
+ return this;
+ }
+
+ get face() {
+ return this._face;
+ }
+
+ set face(direction) {
+ if (!this.chessData) {
+ if (this._face === undefined) {
+ this._face = 0;
+ }
+ return;
+ }
+
+ direction = this.board.grid.directionNormalize(direction);
+ this._face = direction;
+ if (this.coneMode === 0) { // Direction
+ // Do nothing
+ } else { // Angle
+ var angle = this.board.angleToward(this.chessData.tileXYZ, direction); // -PI~PI
+ this.faceAngle = AngleNormalize(angle); // 0~2PI
+ }
+ }
+
+ setFace(direction) {
+ this.face = direction;
+ return this;
+ }
+
+ get cone() {
+ return this._cone;
+ }
+
+ set cone(value) {
+ this._cone = value;
+
+ if (value !== undefined) {
+ if (this.coneMode === 0) { // Direction
+ } else { // Angle
+ this.coneRad = DegToRad(value);
+ }
+ }
+ }
+
+ setConeMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = CONEMODE[mode];
+ }
+ this.coneMode = mode;
+ return this;
+ }
+
+ setCone(value) {
+ this.cone = value;
+ return this;
+ }
+
+ setOccupiedTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.occupiedTest = enable;
+ return this;
+ }
+
+ setBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.blockerTest = enable;
+ return this;
+ }
+
+ setEdgeBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.edgeBlockerTest = enable;
+ return this;
+ }
+
+ setCostFunction(callback, scope) {
+ this.costCallback = callback;
+ this.costCallbackScope = scope;
+ return this;
+ }
+
+ setPreTestFunction(callback, scope) {
+ this.preTestCallback = callback;
+ this.preTestCallbackScope = scope;
+ return this;
+ }
+
+ setPerspectiveEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ this.perspectiveEnable = enable;
+ return this;
+ }
+
+ setDebugGraphics(graphics) {
+ this.debugGraphics = graphics;
+ return this;
+ }
+
+ setDebugLineColor(visibleLineColor, invisibleLineColor) {
+ this.debugVisibleLineColor = visibleLineColor;
+ this.debugInvisibleLineColor = invisibleLineColor;
+ return this;
+ }
+
+ setDebugLog(enabled) {
+ if (enabled === undefined) {
+ enabled = true;
+ }
+ this.debugLog = enabled;
+ return this;
+ }
+
+ clearDebugGraphics() {
+ if (this.debugGraphics) {
+ this.debugGraphics.clear();
+ }
+ return this;
+ }
+
+ get BLOCKER() {
+ return BLOCKER;
+ }
+
+ get INFINITY() {
+ return INFINITY;
+ }
+
+ get board() {
+ return this.chessData.board;
+ }
+}
+
+const CONEMODE = {
+ direction: 0,
+ angle: 1,
+};
+
+Object.assign(
+ FieldOfView.prototype,
+ Methods
+);
+
+export default FieldOfView;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FindFOV.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FindFOV.js
new file mode 100644
index 000000000..6a12f3427
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/FindFOV.js
@@ -0,0 +1,63 @@
+import IsArray from '../../utils/object/IsArray.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+
+var FindFOV = function (visiblePoints, originTileXY, out) {
+ if (IsPlainObject(visiblePoints)) {
+ out = originTileXY;
+ originTileXY = visiblePoints;
+ visiblePoints = undefined;
+ } else if (IsArray(visiblePoints)) {
+ out = visiblePoints;
+ originTileXY = undefined;
+ visiblePoints = undefined;
+ }
+ if (IsArray(originTileXY)) {
+ out = originTileXY;
+ originTileXY = undefined;
+ }
+
+ if (out === undefined) {
+ out = [];
+ }
+
+ var board = this.board;
+ var myTileXYZ = this.chessData.tileXYZ,
+ targetTileXY;
+ var isAnyVisible, hasAnyTestingTileXY;
+ var radius = 1;
+
+ while (true) {
+ isAnyVisible = false;
+ hasAnyTestingTileXY = false;
+ board.ringToTileXYArray(myTileXYZ, radius, globRing);
+ for (var i = 0, cnt = globRing.length; i < cnt; i++) {
+ targetTileXY = globRing[i];
+ if (!board.contains(targetTileXY.x, targetTileXY.y)) {
+ continue;
+ }
+ hasAnyTestingTileXY = true;
+ if (this.isInLOS(targetTileXY, visiblePoints, originTileXY)) {
+ isAnyVisible = true;
+ out.push(targetTileXY);
+ }
+ }
+ radius++;
+ globRing.length = 0;
+
+ if (!this.perspectiveEnable && !isAnyVisible) {
+ if (!isAnyVisible) {
+ break;
+ }
+ } else {
+ if (!hasAnyTestingTileXY) {
+ break;
+ }
+ }
+ }
+
+ return out;
+}
+
+var globRing = [];
+
+export default FindFOV;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/GetCost.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/GetCost.js
new file mode 100644
index 000000000..6ba1f4b00
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/GetCost.js
@@ -0,0 +1,11 @@
+var GetCost = function (curTileXY, tileXYArray) {
+ if (typeof (this.costCallback) === 'number') {
+ return this.costCallback;
+ }
+ if (this.costCallbackScope) {
+ return this.costCallback.call(this.costCallbackScope, curTileXY, this, tileXYArray);
+ } else {
+ return this.costCallback(curTileXY, this, tileXYArray);
+ }
+}
+export default GetCost;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInCone.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInCone.js
new file mode 100644
index 000000000..1b4d72616
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInCone.js
@@ -0,0 +1,13 @@
+var IsInCone = function (targetTileXY) {
+ if (this.cone === undefined) {
+ return true;
+ }
+ var board = this.board;
+ var myTileXYZ = this.chessData.tileXYZ;
+ if (this.coneMode === 0) { // Direction
+ return board.isDirectionInCone(myTileXYZ, targetTileXY, this.face, this.cone);
+ } else { // Angle
+ return board.isAngleInCone(myTileXYZ, targetTileXY, this.faceAngle, this.coneRad);
+ }
+}
+export default IsInCone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInLOS.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInLOS.js
new file mode 100644
index 000000000..37f10474a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsInLOS.js
@@ -0,0 +1,98 @@
+import CONST from './const.js';
+import AngleBetween from '../../utils/math/angle/Between.js';
+import AreTileXYArrayEqual from '../utils/AreTileXYArrayEqual.js';
+
+const INFINITY = CONST.INFINITY;
+const LINEOFFSET = 0.001;
+
+var IsInLOS = function (chess, visiblePoints, originTileXY) {
+ // chess: chess object or tileXY
+ if ((visiblePoints !== INFINITY) && (visiblePoints <= 0)) {
+ return false;
+ }
+
+ var board = this.board;
+ var targetTileXY = board.chessToTileXYZ(chess);
+ if (!this.isInCone(targetTileXY)) {
+ return false;
+ }
+
+ if (originTileXY === undefined) {
+ originTileXY = this.chessData.tileXYZ;
+ }
+ if (this.debugLog) {
+ console.log('Visible test from (' + originTileXY.x + ',' + originTileXY.y + ') to (' + targetTileXY.x + ',' + targetTileXY.y + ')');
+ }
+
+ if (!globTileXYArray0) {
+ globTileXYArray0 = [];
+ globTileXYArray1 = [];
+ }
+
+ var out = board.tileXYToWorldXY(originTileXY.x, originTileXY.y, true);
+ var startX = out.x,
+ startY = out.y;
+ out = board.tileXYToWorldXY(targetTileXY.x, targetTileXY.y, true);
+ var endX = out.x,
+ endY = out.y;
+ var lineAngle = AngleBetween(startX, startY, endX, endY),
+ offsetX, offsetY, isVisivle;
+
+ // Shift a small distance
+ lineAngle += (Math.PI / 2);
+ offsetX = LINEOFFSET * Math.cos(lineAngle);
+ offsetY = LINEOFFSET * Math.sin(lineAngle);
+ var x0 = startX + offsetX,
+ y0 = startY + offsetY,
+ x1 = endX + offsetX,
+ y1 = endY + offsetY;
+ board.lineToTileXYArray(x0, y0, x1, y1, globTileXYArray0);
+ if (this.debugLog) {
+ console.log('Line 0: ' + JSON.stringify(globTileXYArray0));
+ }
+ isVisivle = this.isPathVisible(globTileXYArray0, visiblePoints);
+ if (isVisivle) {
+ globTileXYArray0.length = 0;
+ DrawLine(
+ this.debugGraphics,
+ this.debugVisibleLineColor,
+ startX, startY, endX, endY
+ );
+ return true;
+ }
+
+ // Shift a small distance
+ lineAngle += Math.PI;
+ offsetX = LINEOFFSET * Math.cos(lineAngle);
+ offsetY = LINEOFFSET * Math.sin(lineAngle);
+ var x0 = startX + offsetX,
+ y0 = startY + offsetY,
+ x1 = endX + offsetX,
+ y1 = endY + offsetY;
+ board.lineToTileXYArray(x0, y0, x1, y1, globTileXYArray1);
+ if (this.debugLog) {
+ console.log('Line 1: ' + JSON.stringify(globTileXYArray1));
+ }
+ // No need do visible checking if path is the same as previous one
+ if (!AreTileXYArrayEqual(globTileXYArray0, globTileXYArray1)) {
+ isVisivle = this.isPathVisible(globTileXYArray1, visiblePoints);
+ }
+ globTileXYArray0.length = 0;
+ globTileXYArray1.length = 0;
+ DrawLine(
+ this.debugGraphics,
+ ((isVisivle) ? this.debugVisibleLineColor : this.debugInvisibleLineColor),
+ startX, startY, endX, endY
+ );
+ return isVisivle;
+}
+
+var DrawLine = function (graphics, color, startX, startY, endX, endY) {
+ if (graphics && (color !== undefined)) {
+ graphics.lineStyle(1, color, 1).lineBetween(startX, startY, endX, endY);
+ }
+}
+
+var globTileXYArray0,
+ globTileXYArray1;
+export default IsInLOS;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsPathVisible.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsPathVisible.js
new file mode 100644
index 000000000..3ec26f7cd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/IsPathVisible.js
@@ -0,0 +1,42 @@
+import CONST from './const.js';
+import AreTileXYEqual from '../utils/AreTileXYEqual.js';
+
+const BLOCKER = CONST.BLOCKER;
+const INFINITY = CONST.INFINITY;
+
+var IsPathVisible = function (tileXYArray, visiblePoints) {
+ if (this.preTest(tileXYArray, visiblePoints) === false) {
+ return false;
+ }
+
+ if (this.costCallback === undefined) {
+ return true;
+ }
+ var myTileXYZ = this.chessData.tileXYZ;
+ var tileXY, cost, behindBlocker = false;
+ for (var i = 1, cnt = tileXYArray.length; i < cnt; i++) {
+ tileXY = tileXYArray[i];
+ if (AreTileXYEqual(myTileXYZ, tileXY)) {
+ continue;
+ }
+
+ if (behindBlocker) {
+ return false;
+ }
+
+ cost = this.getCost(tileXY, tileXYArray);
+ if (cost === BLOCKER) {
+ behindBlocker = true;
+ continue;
+ }
+
+ if (visiblePoints !== INFINITY) {
+ visiblePoints -= cost;
+ if (visiblePoints < 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+export default IsPathVisible;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/LOS.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/LOS.js
new file mode 100644
index 000000000..da30d16e6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/LOS.js
@@ -0,0 +1,40 @@
+import IsArray from '../../utils/object/IsArray.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+
+var LOS = function (chessArray, visiblePoints, originTileXY, out) {
+ // chessArray: array of chess object or tileXY
+ if (!IsArray(chessArray)) {
+ var chess = chessArray;
+ return this.isInLOS(chess, visiblePoints, originTileXY);
+ } else {
+ if (IsPlainObject(visiblePoints)) {
+ out = originTileXY;
+ originTileXY = visiblePoints;
+ visiblePoints = undefined;
+ } else if (IsArray(visiblePoints)) {
+ out = visiblePoints;
+ visiblePoints = undefined;
+ originTileXY = undefined;
+ }
+ if (IsArray(originTileXY)) {
+ out = originTileXY;
+ originTileXY = undefined;
+ }
+
+ if (out === undefined) {
+ out = [];
+ }
+
+ var chess;
+ for (var i = 0, cnt = chessArray.length; i < cnt; i++) {
+ chess = chessArray[i];
+ if (!this.isInLOS(chess, visiblePoints, originTileXY)) {
+ continue;
+ }
+ out.push(chess)
+ }
+ return out;
+ }
+}
+
+export default LOS;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Methods.js
new file mode 100644
index 000000000..d02480a83
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/Methods.js
@@ -0,0 +1,17 @@
+import PreTest from './PreTest.js';
+import GetCost from './GetCost.js';
+import IsInCone from './IsInCone.js';
+import IsPathVisible from './IsPathVisible.js';
+import IsInLOS from './IsInLOS.js';
+import LOS from './LOS.js';
+import FindFOV from './FindFOV.js';
+
+export default {
+ preTest: PreTest,
+ getCost: GetCost,
+ isInCone: IsInCone,
+ isPathVisible: IsPathVisible,
+ isInLOS: IsInLOS,
+ LOS: LOS,
+ findFOV: FindFOV,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/PreTest.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/PreTest.js
new file mode 100644
index 000000000..6d69254a7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/PreTest.js
@@ -0,0 +1,36 @@
+var PreTest = function (tileXYArray, visiblePoints) {
+ if (this.occupiedTest || this.blockerTest || this.edgeBlockerTest) {
+ var myTileZ = this.chessData.tileXYZ.z;
+ var tileXY;
+ for (var i = 1, cnt = tileXYArray.length; i < cnt; i++) {
+ tileXY = tileXYArray[i];
+ // Occupied test
+ if (this.occupiedTest) {
+ if (this.board.contains(tileXY.x, tileXY.y, myTileZ)) {
+ return false;
+ }
+ }
+ // Blocker test
+ if (this.blockerTest) {
+ if (this.board.hasBlocker(tileXY.x, tileXY.y)) {
+ return false;
+ }
+ }
+ // Edge-blocker test
+ if (this.edgeBlockerTest) {
+ // TODO
+ }
+ }
+ }
+
+ if (this.preTestCallback) {
+ if (this.preTestCallbackScope) {
+ return this.preTestCallback.call(this.preTestCallbackScope, tileXYArray, visiblePoints, this);
+ } else {
+ return this.preTestCallback(tileXYArray, visiblePoints, this);
+ }
+ }
+
+ return true;
+}
+export default PreTest;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/const.js b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/const.js
new file mode 100644
index 000000000..9bcee74f7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/fieldofview/const.js
@@ -0,0 +1,7 @@
+export default {
+ // special cost
+ 'BLOCKER': null,
+
+ // special moving point
+ 'INFINITY': undefined,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.d.ts
new file mode 100644
index 000000000..e252a074e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.d.ts
@@ -0,0 +1,5 @@
+import Hexagon from './Hexagon';
+
+export default function (
+ config?: Hexagon.IConfig
+): Hexagon;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.js
new file mode 100644
index 000000000..02998d80b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Factory.js
@@ -0,0 +1,8 @@
+import Hexagon from './Hexagon.js';
+import ObjectFactory from '../../ObjectFactory.js';
+
+ObjectFactory.register('hexagonGrid', function (config) {
+ return new Hexagon(config);
+});
+
+export default Hexagon;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetBounds.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetBounds.js
new file mode 100644
index 000000000..1f463afc7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetBounds.js
@@ -0,0 +1,21 @@
+import Rectangle from '../../../utils/geom/rectangle/Rectangle.js';
+
+var GetBounds = function (tileX, tileY, out) {
+ if (out === undefined) {
+ out = new Rectangle;
+ } else if (out === true) {
+ out = globalBounds;
+ }
+
+ var worldXY = this.getWorldXY(tileX, tileY, true);
+ out.x = worldXY.x - (this.width * 0.5);
+ out.y = worldXY.y - (this.height * 0.5);
+ out.width = this.width;
+ out.height = this.height;
+
+ return out;
+}
+
+var globalBounds = new Rectangle();
+
+export default GetBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetGridPoints.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetGridPoints.js
new file mode 100644
index 000000000..8dba9d18a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/GetGridPoints.js
@@ -0,0 +1,33 @@
+import SetPoints from '../../../geom/hexagon/SetPoints.js';
+import InitPoints from '../../../geom/utils/InitPoints.js';
+
+var GetGridPoints = function (tileX, tileY, points) {
+ if (points === undefined) {
+ points = InitPoints(6);
+ } else if (points === true) {
+ points = globPoints;
+ }
+
+ if (tileX === undefined) {
+ globWorldXY.x = 0;
+ globWorldXY.y = 0;
+ } else {
+ this.getWorldXY(tileX, tileY, globWorldXY);
+ }
+ var size;
+ if (this.size !== undefined) {
+ size = this.size;
+ } else {
+ size = globSize;
+ size.width = this.width;
+ size.height = this.height;
+ }
+ SetPoints(globWorldXY.x, globWorldXY.y, size, this.staggeraxis, points);
+ return points;
+}
+
+var globPoints = InitPoints(6);
+var globWorldXY = {};
+var globSize = {};
+
+export default GetGridPoints;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.d.ts
new file mode 100644
index 000000000..69db2fb26
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.d.ts
@@ -0,0 +1,72 @@
+import { WorldXYType, TileXYType } from '../../types/Position';
+import Rectangle from '../../../utils/geom/rectangle/Rectangle';
+
+export default Hexagon;
+
+declare namespace Hexagon {
+
+ type HexagonGridStaggerAxisTypes = 0 | 1 | 'y' | 'x';
+ type HexagonGridStaggerindexTypes = 0 | 1 | 'even' | 'odd';
+
+ interface IConfig {
+ x?: number, y?: number,
+ size?: number,
+ cellWidth?: number, cellHeight?: number,
+
+ staggeraxis?: HexagonGridStaggerAxisTypes,
+ staggerindex?: HexagonGridStaggerindexTypes
+ }
+
+}
+
+declare class Hexagon {
+ constructor(config?: Hexagon.IConfig);
+
+ setOriginPosition(
+ worldX: number,
+ worldY: number
+ ): this;
+ x: number;
+ y: number;
+
+ setCellSize(
+ width: number,
+ height: number
+ ): this;
+ width: number;
+ height: number;
+ setCellRadius(size: number): this;
+ readonly size: number;
+
+ setType(
+ staggeraxis: Hexagon.HexagonGridStaggerAxisTypes,
+ staggerindex: Hexagon.HexagonGridStaggerindexTypes
+ ): this;
+ readonly staggeraxis: number;
+ readonly staggerindex: number;
+ readonly mode: number;
+
+ getWorldXY(
+ tileX: number,
+ tileY: number,
+ out?: WorldXYType | true
+ ): WorldXYType;
+
+ getTileXY(
+ worldX: number,
+ worldY: number,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ getGridPoints(
+ tileX: number,
+ tileY: number,
+ points?: WorldXYType[]
+ ): WorldXYType[];
+
+ getBounds(
+ tileX: number,
+ tileY: number,
+ out?: Rectangle
+ ): Rectangle;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.js
new file mode 100644
index 000000000..a1e809ba1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/hexagon/Hexagon.js
@@ -0,0 +1,70 @@
+import Hexagon from '../../../utils/grid/hexagon/Hexagon.js';
+import SaveOrigin from '../utils/SaveOrigin.js';
+import RestoreOrigin from '../utils/RestoreOrigin.js';
+import GetTileXYAtDirection from '../../../utils/grid/hexagon/GetTileXYAtDirection.js';
+import GetNeighborTileXY from '../../../utils/grid/hexagon/GetNeighborTileXY.js';
+import GetNeighborTileDirection from '../../../utils/grid/hexagon/GetNeighborTileDirection.js';
+import GetOppositeDirection from '../../../utils/grid/hexagon/GetOppositeDirection.js';
+import Offset from '../../../utils/grid/hexagon/Offset.js';
+import Mirror from '../../../utils/grid/hexagon/Mirror.js';
+import Rotate from '../../../utils/grid/hexagon/Rotate.js';
+import GetDistance from '../../../utils/grid/hexagon/GetDistance.js';
+import DirectionBetween from '../../../utils/grid/hexagon/DirectionBetween.js';
+import DirectionNormalize from '../utils/DirectionNormalize.js';
+import GetGridPoints from './GetGridPoints.js';
+import GetBounds from './GetBounds.js';
+import RingToTileXYArray from '../../../utils/grid/hexagon/RingToTileXYArray.js';
+
+class HexagonGrid extends Hexagon {
+ constructor(config) {
+ super(config);
+ this.sides = 6;
+ }
+
+ // resetFromJSON(o) {
+ // super.resetFromJSON(o);
+ // }
+
+ // Direction of neighbors
+ get allDirections() {
+ return ALLDIR;
+ }
+
+ // Board-match
+ get halfDirections() {
+ return HALFDIR;
+ }
+
+ // setOriginPosition
+ // setCellSize
+ // setType
+ // getWorldXY
+ // getTileXY
+}
+
+const ALLDIR = [0, 1, 2, 3, 4, 5];
+const HALFDIR = [0, 1, 2];
+
+var methods = {
+ saveOrigin: SaveOrigin,
+ restoreOrigin: RestoreOrigin,
+ getTileXYAtDirection: GetTileXYAtDirection,
+ getNeighborTileXY: GetNeighborTileXY,
+ getNeighborTileDirection: GetNeighborTileDirection,
+ getOppositeDirection: GetOppositeDirection,
+ offset: Offset,
+ mirror: Mirror,
+ rotate: Rotate,
+ getDistance: GetDistance,
+ directionBetween: DirectionBetween,
+ directionNormalize: DirectionNormalize,
+ getGridPoints: GetGridPoints,
+ getBounds: GetBounds,
+ ringToTileXYArray: RingToTileXYArray,
+}
+Object.assign(
+ HexagonGrid.prototype,
+ methods
+);
+
+export default HexagonGrid;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/index.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/index.js
new file mode 100644
index 000000000..5d781f8cb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/index.js
@@ -0,0 +1,7 @@
+import QuadGrid from './quad/Quad.js';
+import HexagonGrid from './hexagon/Hexagon.js';
+
+export default {
+ quadGrid: QuadGrid,
+ hexagonGrid: HexagonGrid
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.d.ts
new file mode 100644
index 000000000..5dd771499
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.d.ts
@@ -0,0 +1,5 @@
+import Quad from './Quad';
+
+export default function (
+ config?: Quad.IConfig
+): Quad;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.js
new file mode 100644
index 000000000..fc1d23b2a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Factory.js
@@ -0,0 +1,8 @@
+import Quad from './Quad.js';
+import ObjectFactory from '../../ObjectFactory.js';
+
+ObjectFactory.register('quadGrid', function (config) {
+ return new Quad(config);
+});
+
+export default Quad;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetBounds.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetBounds.js
new file mode 100644
index 000000000..1f463afc7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetBounds.js
@@ -0,0 +1,21 @@
+import Rectangle from '../../../utils/geom/rectangle/Rectangle.js';
+
+var GetBounds = function (tileX, tileY, out) {
+ if (out === undefined) {
+ out = new Rectangle;
+ } else if (out === true) {
+ out = globalBounds;
+ }
+
+ var worldXY = this.getWorldXY(tileX, tileY, true);
+ out.x = worldXY.x - (this.width * 0.5);
+ out.y = worldXY.y - (this.height * 0.5);
+ out.width = this.width;
+ out.height = this.height;
+
+ return out;
+}
+
+var globalBounds = new Rectangle();
+
+export default GetBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetGridPoints.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetGridPoints.js
new file mode 100644
index 000000000..ca256f749
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/GetGridPoints.js
@@ -0,0 +1,25 @@
+import SetPoints from '../../../geom/quad/SetPoints.js';
+import InitPoints from '../../../geom/utils/InitPoints.js';
+
+var GetGridPoints = function (tileX, tileY, points) {
+ if (points === undefined) {
+ points = InitPoints(4);
+ } else if (points === true) {
+ points = globPoints;
+ }
+
+ if (tileX === undefined) {
+ globWorldXY.x = 0;
+ globWorldXY.y = 0;
+ } else {
+ this.getWorldXY(tileX, tileY, globWorldXY);
+ }
+ var quadType = (this.mode === 0) ? 0 : 1;
+ SetPoints(globWorldXY.x, globWorldXY.y, this.width, this.height, quadType, points);
+ return points;
+}
+
+var globWorldXY = {};
+var globPoints = InitPoints(4);
+
+export default GetGridPoints;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.d.ts
new file mode 100644
index 000000000..9364dcc16
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.d.ts
@@ -0,0 +1,67 @@
+import { WorldXYType, TileXYType } from '../../types/Position';
+import Rectangle from '../../../utils/geom/rectangle/Rectangle';
+
+export default Quad;
+
+declare namespace Quad {
+
+ type QuadGridTypes = 0 | 1 | 'orthogonal' | 'isometric';
+ type QuadGridDirTypes = 4 | 8 | '4dir' | '8dir';
+
+ interface IConfig {
+ x?: number, y?: number,
+ cellWidth?: number, cellHeight?: number,
+
+ type?: QuadGridTypes,
+
+ dir?: QuadGridDirTypes
+ }
+
+}
+
+declare class Quad {
+ constructor(config?: Quad.IConfig);
+
+ setOriginPosition(
+ worldX: number,
+ worldY: number
+ ): this;
+ x: number;
+ y: number;
+
+ setCellSize(
+ width: number,
+ height: number
+ ): this;
+ width: number;
+ height: number;
+
+ setType(
+ type: Quad.QuadGridTypes
+ ): this;
+ readonly mode: number;
+
+ getWorldXY(
+ tileX: number,
+ tileY: number,
+ out?: WorldXYType | true
+ ): WorldXYType;
+
+ getTileXY(
+ worldX: number,
+ worldY: number,
+ out?: TileXYType | true
+ ): TileXYType;
+
+ getGridPoints(
+ tileX: number,
+ tileY: number,
+ points?: WorldXYType[]
+ ): WorldXYType[];
+
+ getBounds(
+ tileX: number,
+ tileY: number,
+ out?: Rectangle
+ ): Rectangle;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.js
new file mode 100644
index 000000000..c0e6c66df
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/quad/Quad.js
@@ -0,0 +1,73 @@
+import Quad from '../../../utils/grid/quad/Quad.js';
+import SaveOrigin from '../utils/SaveOrigin.js';
+import RestoreOrigin from '../utils/RestoreOrigin.js';
+import GetTileXYAtDirection from '../../../utils/grid/quad/GetTileXYAtDirection.js';
+import GetNeighborTileXY from '../../../utils/grid/quad/GetNeighborTileXY.js';
+import GetNeighborTileDirection from '../../../utils/grid/quad/GetNeighborTileDirection.js';
+import GetOppositeDirection from '../../../utils/grid/quad/GetOppositeDirection.js';
+import Offset from '../../../utils/grid/quad/Offset.js';
+import Mirror from '../../../utils/grid/quad/Mirror.js';
+import Rotate from '../../../utils/grid/quad/Rotate.js';
+import GetDistance from '../../../utils/grid/quad/GetDistance.js';
+import DirectionBetween from '../../../utils/grid/quad/DirectionBetween.js';
+import DirectionNormalize from '../utils/DirectionNormalize.js';
+import GetGridPoints from './GetGridPoints.js';
+import GetBounds from './GetBounds.js';
+import RingToTileXYArray from '../../../utils/grid/quad/RingToTileXYArray.js';
+
+class QuadGrid extends Quad {
+ constructor(config) {
+ super(config);
+ this.sides = 4;
+ }
+
+ // resetFromJSON(o) {
+ // super.resetFromJSON(o);
+ // }
+
+ // Direction of neighbors
+ get allDirections() {
+ return (this.directions === 4) ? ALLDIR4 : ALLDIR8;
+ }
+
+ // Board-match
+ get halfDirections() {
+ return (this.directions === 4) ? HALFDIR4 : HALFDIR8;
+ }
+
+ // setOriginPosition
+ // setCellSize
+ // setType
+ // getWorldXY
+ // getTileXY
+ // getGridPolygon
+}
+
+const ALLDIR4 = [0, 1, 2, 3];
+const ALLDIR8 = [0, 1, 2, 3, 4, 5, 6, 7];
+const HALFDIR4 = [0, 1];
+const HALFDIR8 = [0, 1, 4, 5];
+
+var methods = {
+ saveOrigin: SaveOrigin,
+ restoreOrigin: RestoreOrigin,
+ getTileXYAtDirection: GetTileXYAtDirection,
+ getNeighborTileXY: GetNeighborTileXY,
+ getNeighborTileDirection: GetNeighborTileDirection,
+ getOppositeDirection: GetOppositeDirection,
+ offset: Offset,
+ mirror: Mirror,
+ rotate: Rotate,
+ getDistance: GetDistance,
+ directionBetween: DirectionBetween,
+ directionNormalize: DirectionNormalize,
+ getGridPoints: GetGridPoints,
+ getBounds: GetBounds,
+ ringToTileXYArray: RingToTileXYArray,
+}
+Object.assign(
+ QuadGrid.prototype,
+ methods
+);
+
+export default QuadGrid;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/DirectionNormalize.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/DirectionNormalize.js
new file mode 100644
index 000000000..c82ea09b9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/DirectionNormalize.js
@@ -0,0 +1,7 @@
+import Wrap from '../../../utils/math/Wrap.js';
+
+var DirectionNormalize = function (direction) {
+ return Wrap(direction, 0, this.directions);
+}
+
+export default DirectionNormalize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/RestoreOrigin.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/RestoreOrigin.js
new file mode 100644
index 000000000..a3d2c1bc2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/RestoreOrigin.js
@@ -0,0 +1,6 @@
+var RestoreOrigin = function () {
+ this.x = this._savedOriginX;
+ this.y = this._savedOriginY;
+ return this;
+}
+export default RestoreOrigin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/SaveOrigin.js b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/SaveOrigin.js
new file mode 100644
index 000000000..60b4b6f04
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/grid/utils/SaveOrigin.js
@@ -0,0 +1,6 @@
+var SaveOrigin = function () {
+ this._savedOriginX = this.x;
+ this._savedOriginY = this.y;
+ return this;
+}
+export default SaveOrigin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.d.ts
new file mode 100644
index 000000000..fd96e1626
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.d.ts
@@ -0,0 +1,8 @@
+import { TileXYType } from '../types/Position';
+import Board from '../board/LogicBoard';
+
+export default function GetHexagonMap(
+ board: Board,
+ radius: number,
+ out?: TileXYType[]
+): TileXYType[];
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.js b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.js
new file mode 100644
index 000000000..20f8e99d6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetHexagonMap.js
@@ -0,0 +1,21 @@
+import {
+ cube2cr
+} from '../../utils/grid/hexagon/CubeTransfer.js';
+
+var GetHexagonMap = function (board, radius, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var mode = board.grid.mode;
+ var r1, r2;
+ for (var q = -radius; q <= radius; q++) {
+ r1 = Math.max(-radius, -q - radius);
+ r2 = Math.min(radius, -q + radius);
+ for (var r = r1; r <= r2; r++) {
+ out.push(cube2cr(mode, q, r, -q - r));
+ }
+ }
+
+ return out;
+}
+export default GetHexagonMap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.d.ts
new file mode 100644
index 000000000..253e26ce6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.d.ts
@@ -0,0 +1,10 @@
+import { TileXYType } from '../types/Position';
+import Board from '../board/LogicBoard';
+
+export default function GetParallelogramMap(
+ board: Board,
+ type: 0 | 1 | 2,
+ width: number,
+ height: number,
+ out?: TileXYType[]
+): TileXYType[];
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.js b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.js
new file mode 100644
index 000000000..b8ed091de
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetParallelogramMap.js
@@ -0,0 +1,36 @@
+import {
+ cube2cr
+} from '../../utils/grid/hexagon/CubeTransfer.js';
+
+var GetParallelogramMap = function (board, type, width, height, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var mode = board.grid.mode;
+ switch (type) {
+ case 1:
+ for (var s = 0; s <= width; s++) {
+ for (var q = 0; q <= height; q++) {
+ out.push(cube2cr(mode, q, -q - s, s));
+ }
+ }
+ break;
+ case 2:
+ for (var r = 0; r <= width; r++) {
+ for (var s = 0; s <= height; s++) {
+ out.push(cube2cr(mode, -r - s, r, s));
+ }
+ }
+ break;
+ default: // case 0
+ for (var q = 0; q <= width; q++) {
+ for (var r = 0; r <= height; r++) {
+ out.push(cube2cr(mode, q, r, -q - r));
+ }
+ }
+ break;
+ }
+
+ return out;
+}
+export default GetParallelogramMap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.d.ts
new file mode 100644
index 000000000..026dd7f24
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.d.ts
@@ -0,0 +1,9 @@
+import { TileXYType } from '../types/Position';
+import Board from '../board/LogicBoard';
+
+export default function GetTriangleMap(
+ board: Board,
+ type: 0 | 1,
+ height: number,
+ out?: TileXYType[]
+): TileXYType[];
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.js b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.js
new file mode 100644
index 000000000..638c3de1d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/GetTriangleMap.js
@@ -0,0 +1,28 @@
+import {
+ cube2cr
+} from '../../utils/grid/hexagon/CubeTransfer.js';
+
+var GetTriangleMap = function (board, type, height, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ var mode = board.grid.mode;
+ var rStart, rEnd
+ for (var q = 0; q <= height; q++) {
+ if (type === 1) {
+ rStart = height - q;
+ rEnd = height;
+ } else {
+ rStart = 0;
+ rEnd = height - q;
+ }
+
+ for (var r = rStart; r <= rEnd; r++) {
+ out.push(cube2cr(mode, q, r, -q - r));
+ }
+ }
+
+ return out;
+}
+
+export default GetTriangleMap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.d.ts
new file mode 100644
index 000000000..7d08bfd59
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.d.ts
@@ -0,0 +1,11 @@
+import GetHexagonMap from './GetHexagonMap';
+import GetTriangleMap from './GetTriangleMap';
+import GetParallelogramMap from './GetParallelogramMap';
+
+type Methods = {
+ hexagon: typeof GetHexagonMap,
+ triangle: typeof GetTriangleMap,
+ parallelogram: typeof GetParallelogramMap
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.js b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.js
new file mode 100644
index 000000000..86325deda
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/hexagonmap/index.js
@@ -0,0 +1,9 @@
+import GetHexagonMap from './GetHexagonMap.js';
+import GetTriangleMap from './GetTriangleMap.js';
+import GetParallelogramMap from './GetParallelogramMap.js';
+
+export default {
+ hexagon: GetHexagonMap,
+ triangle: GetTriangleMap,
+ parallelogram: GetParallelogramMap,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.d.ts
new file mode 100644
index 000000000..b2c8c09db
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.d.ts
@@ -0,0 +1,5 @@
+import Match from './Match';
+
+export default function (
+ config?: Match.IConfig
+): Match;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.js
new file mode 100644
index 000000000..479224bf9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Factory.js
@@ -0,0 +1,11 @@
+import Match from './Match.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('match', function (config) {
+ return new Match(config);
+});
+
+SetValue(window, 'RexPlugins.Board.Match', Match);
+
+export default Match;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Group.js b/ui/src/phaser3-rex-plugins/plugins/board/match/Group.js
new file mode 100644
index 000000000..8931ea6c1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Group.js
@@ -0,0 +1,83 @@
+import Clear from '../../utils/object/Clear.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+import IsArray from '../../utils/object/IsArray.js';
+
+var Group = function (startTileX, startTileY, out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ var board = this.board;
+ var wildcard = this.wildcard;
+ var targetSymbol = this.getSymbol(startTileX, startTileY);
+ if ((targetSymbol == null) || (targetSymbol === wildcard)) {
+ return out;
+ }
+
+ if (globalQueue === undefined) {
+ globalQueue = new Queue();
+ }
+
+ var curTileXY, symbol;
+ globalQueue.push(startTileX, startTileY);
+ while (globalQueue.length) {
+ curTileXY = globalQueue.pop();
+ symbol = this.getSymbol(curTileXY.x, curTileXY.y);
+ if ((symbol === targetSymbol) || (symbol === wildcard)) {
+ out.push(curTileXY);
+ globalQueue.push(board.getNeighborTileXY(curTileXY));
+ }
+ }
+
+ globalQueue.clear();
+ return out;
+}
+
+class Queue {
+ constructor() {
+ this.data = [];
+ this.visited = {};
+ }
+
+ push(x, y) {
+ if (IsArray(x)) {
+ var xyArray = x;
+ for (var i = 0, cnt = xyArray.length; i < cnt; i++) {
+ this.push(xyArray[i]);
+ }
+ return this;
+ }
+
+ if (IsPlainObject(x)) {
+ var xy = x;
+ x = xy.x;
+ y = xy.y;
+ }
+ var key = `${x},${y}`;
+ if (this.visited.hasOwnProperty(key)) {
+ return this;
+ }
+
+ this.data.push({ x: x, y: y });
+ this.visited[key] = true;
+ return this;
+ }
+
+ pop() {
+ return this.data.pop();
+ }
+
+ get length() {
+ return this.data.length;
+ }
+
+ clear() {
+ Clear(this.data);
+ Clear(this.visited);
+ return this;
+ }
+}
+
+var globalQueue;
+
+export default Group;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Match.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/match/Match.d.ts
new file mode 100644
index 000000000..a69f224fe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Match.d.ts
@@ -0,0 +1,109 @@
+import Board from '../board/LogicBoard';
+import { TileXYType } from '../types/Position';
+
+
+export default Match;
+
+declare namespace Match {
+
+ interface IConfig {
+ board?: Board,
+ wildcard?: string | number,
+ dirMask?: { [dir: number]: boolean },
+ }
+
+ type MatchResultType = {
+ tileXY: TileXYType[],
+ direction: number,
+
+ pattern: string | number |
+ (string | number)[]
+ }
+
+}
+
+declare class Match {
+ constructor(config?: Match.IConfig);
+
+ setBoard(board: Board): this;
+ readonly board: Board;
+
+ refreshSymbols(
+ callback: (
+ tileXY: TileXYType,
+ board: Board
+ ) => string | number | null,
+ scope?: object
+ ): this;
+
+ setSymbol(
+ tileX: number,
+ tileY: number,
+ symbol: string | number | null
+ ): this;
+
+ getSymbol(
+ tileX: number,
+ tileY: number
+ ): string | number | null;
+
+ forEach(
+ callback: (
+ tileXY: TileXYType,
+ symbol: string | number | null,
+ board: Board
+ ) => void | boolean,
+ scope?: object
+ ): this;
+
+ setWildcard(
+ symbol: string | number
+ ): this;
+ wildcard: string | number;
+
+ setDirMask(
+ dir: number,
+ value: boolean
+ ): this;
+
+ match(
+ n: number,
+ callback: (
+ result: {
+ tileXY: TileXYType[],
+ direction: number,
+ pattern: string | number
+ },
+ board: Board
+ ) => void | boolean,
+ scope?: object
+ ): this;
+
+
+ match(
+ n: (string | number)[],
+ callback: (
+ result: {
+ tileXY: TileXYType[],
+ direction: number,
+ pattern: (string | number)[]
+ },
+ board: Board
+ ) => void | boolean,
+ scope?: object
+ ): this;
+
+ anyMatch(
+ n: number
+ ): boolean;
+
+ anyMatch(
+ n: (string | number)[]
+ ): boolean;
+
+ group(
+ startTileX: number,
+ startTileY: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Match.js b/ui/src/phaser3-rex-plugins/plugins/board/match/Match.js
new file mode 100644
index 000000000..320529654
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Match.js
@@ -0,0 +1,167 @@
+import Methods from './Methods.js';
+import IsFunction from '../../utils/object/IsFunction.js';
+import GetValue from '../../utils/object/GetValue.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+
+class Match {
+ constructor(config) {
+ this.symbols = []; // tileX+(tileY*board.width)
+ this.dirMask = {};
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.setBoard(GetValue(o, 'board', undefined));
+ this.setWildcard(GetValue(o, 'wildcard', undefined));
+
+ var dirMask = GetValue(o, 'dirMask', undefined);
+ if (dirMask !== undefined) {
+ this.setDirMask(dirMask);
+ }
+ return this;
+ }
+
+ boot() { }
+
+ shutdown() {
+ this.board = undefined;
+ this.symbols = undefined;
+ this.dirMask = undefined;
+ return this;
+ }
+
+ destroy() {
+ this.shutdown();
+ return this;
+ }
+
+ setBoard(board) {
+ this.board = board;
+ if (board) {
+ this.clearSymbols();
+ }
+ return this;
+ }
+
+ setDirMask(dir, value) {
+ if (IsPlainObject(dir)) {
+ var dirMask = dir;
+ for (dir in dirMask) {
+ this.dirMask[dir] = dirMask[dir];
+ }
+ } else {
+ this.dirMask[dir] = value;
+ }
+ return this;
+ }
+
+ setDirectionMode(mode) {
+ this.board.grid.setDirectionMode(mode);
+ return this;
+ }
+
+ clearSymbols() {
+ this.refreshSymbols(null);
+ return this;
+ }
+
+ setSymbol(tileX, tileY, symbol) {
+ var board = this.board;
+ if (!board.contains(tileX, tileY)) {
+ return this;
+ }
+
+ this.symbols[this.tileXYToKey(tileX, tileY)] = symbol;
+ return this;
+ }
+
+ getSymbol(tileX, tileY) {
+ return this.symbols[this.tileXYToKey(tileX, tileY)];
+ }
+
+ forEach(callback, scope) {
+ var board = this.board;
+ var tileXY, symbol;
+ var isBreak;
+ for (var i = 0, cnt = this.symbols.length; i < cnt; i++) {
+ symbol = this.symbols[i];
+ tileXY = this.keyToTileXY(i);
+ if (scope) {
+ isBreak = callback.call(scope, tileXY, symbol, board);
+ } else {
+ isBreak = callback(tileXY, symbol, board);
+ }
+ if (isBreak) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ refreshSymbols(callback, scope) {
+ var board = this.board;
+ var width = board.width,
+ height = board.height;
+ this.symbols.length = width * height;
+
+ var symbol, tileXY;
+ if (IsFunction(callback)) {
+ // Get symbol by callback
+ for (var i = 0, cnt = this.symbols.length; i < cnt; i++) {
+ tileXY = this.keyToTileXY(i, true);
+ if (scope) {
+ symbol = callback.call(scope, tileXY, board);
+ } else {
+ symbol = callback(tileXY, board);
+ }
+ this.symbols[i] = symbol;
+ }
+
+ } else {
+ // Fill a given symbol
+ symbol = callback;
+ for (var i = 0, cnt = this.symbols.length; i < cnt; i++) {
+ this.symbols[i] = symbol;
+ }
+ }
+ return this;
+ }
+
+ setWildcard(symbol) {
+ this.wildcard = symbol;
+ return this;
+ }
+
+ tileXYToKey(tileX, tileY) {
+ return tileX + (tileY * this.board.width);
+ }
+
+ keyToTileXY(key, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXY;
+ }
+ var width = this.board.width;
+ out.x = key % width;
+ out.y = Math.floor(key / width);
+ return out;
+ }
+
+ anyMatch(pattern) {
+ return this.match(pattern, null, null, true);
+ }
+}
+
+var globTileXY = {
+ x: 0,
+ y: 0
+};
+
+Object.assign(
+ Match.prototype,
+ Methods
+);
+
+export default Match;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAll.js b/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAll.js
new file mode 100644
index 000000000..8ea34483e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAll.js
@@ -0,0 +1,47 @@
+var MatchBoard = function (pattern, callback, scope, getFirst) {
+ // pattern: pattern list or repeat count
+ var board = this.board,
+ grid = board.grid;
+ var directions = grid.halfDirections,
+ dir,
+ dirMask = this.dirMask;
+ var width = board.width,
+ height = board.height;
+ var result, isBreak;
+ for (var i = 0, cnt = directions.length; i < cnt; i++) {
+ dir = directions[i];
+ if (dirMask[dir] === false) {
+ continue;
+ }
+
+ for (var tileY = 0; tileY < height; tileY++) {
+ for (var tileX = 0; tileX < width; tileX++) {
+ result = this.matchAtDir(pattern, tileX, tileY, dir);
+ if (result === false) {
+ continue;
+ }
+
+ if (callback) {
+ if (scope) {
+ isBreak = callback.call(scope, result, board);
+ } else {
+ isBreak = callback(result, board);
+ }
+ }
+ if (getFirst) {
+ return result;
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+ return this;
+}
+export default MatchBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAtDir.js b/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAtDir.js
new file mode 100644
index 000000000..a1791d1ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/MatchAtDir.js
@@ -0,0 +1,63 @@
+var MatchAtDir = function (pattern, startTileX, startTileY, direction) {
+ // pattern: pattern list or repeat count
+ var matchNMode = typeof (pattern) === 'number';
+ var patternLength;
+ if (matchNMode) {
+ patternLength = pattern;
+ pattern = null;
+ } else {
+ patternLength = pattern.length;
+ }
+
+ var symbol, wildcard = this.wildcard;
+ var curTileXY;
+ var board = this.board;
+ var matchedTileXY = result.tileXY;
+ matchedTileXY.length = 0;
+ for (var i = 0; i < patternLength; i++) {
+ if (curTileXY === undefined) {
+ curTileXY = {
+ x: startTileX,
+ y: startTileY
+ };
+ } else {
+ // get next tileXY
+ curTileXY = board.getNeighborTileXY(curTileXY, direction, curTileXY);
+ if (curTileXY === null) {
+ return false;
+ }
+ }
+
+ symbol = this.getSymbol(curTileXY.x, curTileXY.y);
+ if (symbol == null) {
+ return false;
+ }
+ if (symbol !== wildcard) {
+ if (matchNMode) {
+ if (pattern === null) {
+ pattern = symbol;
+ } else if (pattern !== symbol) {
+ return false;
+ }
+ } else if (pattern[i] !== symbol) { // pattern list mode
+ return false;
+ }
+ }
+
+ matchedTileXY.push({
+ x: curTileXY.x,
+ y: curTileXY.y
+ });
+ }
+
+ result.direction = direction;
+ result.pattern = pattern;
+ return result;
+};
+
+var result = {
+ tileXY: [],
+ direction: undefined,
+ pattern: undefined
+};
+export default MatchAtDir;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/match/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/match/Methods.js
new file mode 100644
index 000000000..789bf705d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/match/Methods.js
@@ -0,0 +1,9 @@
+import MatchAll from './MatchAll.js';
+import MatchAtDir from './MatchAtDir.js';
+import Group from './Group.js';
+
+export default {
+ match: MatchAll,
+ matchAtDir: MatchAtDir,
+ group: Group
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.d.ts
new file mode 100644
index 000000000..3d317edb0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.d.ts
@@ -0,0 +1,6 @@
+import MiniBoard from './MiniBoard';
+
+export default function (
+ x: number, y: number,
+ config?: MiniBoard.IConfig
+): MiniBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.js
new file mode 100644
index 000000000..02ac3a27d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Factory.js
@@ -0,0 +1,13 @@
+import MiniBoard from './MiniBoard.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('miniBoard', function (x, y, config) {
+ var gameObject = new MiniBoard(this.scene, x, y, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+});
+
+SetValue(window, 'RexPlugins.Board.MiniBoard', MiniBoard);
+
+export default MiniBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/IsMiniBoardObject.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/IsMiniBoardObject.js
new file mode 100644
index 000000000..8a7f8c017
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/IsMiniBoardObject.js
@@ -0,0 +1,5 @@
+var IsMiniBoardObject = function (object) {
+ return (object.type === 'rexMiniBoard');
+};
+
+export default IsMiniBoardObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Methods.js
new file mode 100644
index 000000000..a6dcd37af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/Methods.js
@@ -0,0 +1,49 @@
+import AddChess from './chess/AddChess.js';
+import RemoveChess from './chess/RemoveChess.js';
+import RemoveAllChess from './chess/RemoveAllChess.js';
+
+import SetMainBoard from './mainboard/SetMainboard.js';
+import CanPutOnMainBoard from './mainboard/CanPutOnMainBoard.js';
+import PutOnMainBoard from './mainboard/PutOnMainBoard.js';
+import PullOutFromMainBoard from './mainboard/PullOutFromMainBoard.js';
+import PutBack from './mainboard/PutBack.js';
+import IsOverlapping from './mainboard/IsOverlapping.js';
+import AlignToMainBoard from './mainboard/AlignToMainBoard.js';
+
+import SetInteractive from './input/SetInteractive.js';
+import SetDraggable from './input/SetDraggable.js';
+import DragEnd from './input/DragEnd.js';
+
+import CanMirror from './transform/CanMirror.js';
+import Mirror from './transform/Mirror.js';
+import CanRotate from './transform/CanRotate.js';
+import Rotate from './transform/Rotate.js';
+import CanRotateTo from './transform/CanRotateTo.js';
+import RotateTo from './transform/RotateTo.js';
+import SetOrigin from './transform/SetOrigin.js';
+
+export default {
+ addChess: AddChess,
+ removeChess: RemoveChess,
+ removeAllChess: RemoveAllChess,
+
+ pullOutFromMainBoard: PullOutFromMainBoard,
+ canPutOnMainBoard: CanPutOnMainBoard,
+ putOnMainBoard: PutOnMainBoard,
+ putBack: PutBack,
+ isOverlapping: IsOverlapping,
+ alignToMainBoard: AlignToMainBoard,
+
+ setInteractive: SetInteractive,
+ setDraggable: SetDraggable,
+ dragEnd: DragEnd,
+
+ setMainBoard: SetMainBoard,
+ canMirror: CanMirror,
+ mirror: Mirror,
+ canRotate: CanRotate,
+ rotate: Rotate,
+ canRotateTo: CanRotateTo,
+ rotateTo: RotateTo,
+ setOrigin: SetOrigin
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.d.ts
new file mode 100644
index 000000000..057740627
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.d.ts
@@ -0,0 +1,166 @@
+import Container from '../../gameobjects/container/containerlite/ContainerLite';
+import Quad from '../grid/quad/Quad';
+import Hexagon from '../grid/hexagon/Hexagon';
+import { TileXYZType } from '../types/Position';
+import Board from '../board/Board';
+
+export default MiniBoard;
+
+declare namespace MiniBoard {
+
+ type PutTestCallbackType = (
+ targetTileXY: TileXYZType,
+ mainBoard: Board,
+ chess: Phaser.GameObjects.GameObject
+ ) => boolean;
+
+ interface IConfig {
+ grid: Quad | Hexagon,
+
+ draggable?: boolean,
+ face?: number,
+
+ putTestCallback?: PutTestCallbackType,
+ putTestCallbackScpe?: unknown,
+ }
+
+ type MirrorModeType = 0 | 1 | 3 | 'x' | 'y' | 'x&y';
+
+ type TileXYZMapType = { [uid: number]: TileXYZType };
+
+ namespace Events {
+ type PointerDownCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ miniBoard: MiniBoard
+ ) => void;
+
+ type ChessDownCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerUpCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ miniBoard: MiniBoard
+ ) => void;
+
+ type ChessUpCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type PointerMoveCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ miniBoard: MiniBoard
+ ) => void;
+
+ type ChessMoveCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ gameObject: Phaser.GameObjects.GameObject
+ ) => void;
+
+ type DragCallbackType = (
+ pointer: Phaser.Input.Pointer,
+ dragX: number, dragY: number
+ ) => void;
+ }
+}
+
+declare class MiniBoard extends Container {
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ config?: MiniBoard.IConfig
+ );
+
+ setFace(direction: number): this;
+ face: number;
+
+ addChess(
+ chess: Phaser.GameObjects.GameObject,
+ tileX: number,
+ tileY: number,
+ tileZ: number | string
+ ): this;
+
+ removeChess(
+ chess: Phaser.GameObjects.GameObject,
+ tileX?: null,
+ tileY?: null,
+ tileZ?: null,
+ destroy?: boolean
+ ): this;
+ removeChess(
+ chess: null,
+ tileX: number,
+ tileY: number,
+ tileZ: number | string,
+ destroy?: boolean
+ ): this;
+
+ removeAllChess(
+ destroy?: boolean
+ ): this;
+
+ setOrigin(
+ originX: number,
+ originY?: number
+ ): this;
+ setOrigin(
+ origin: 'center' | 'top-left' | 'left-top'
+ ): this;
+
+ setPutTestCallback(
+ callback: MiniBoard.PutTestCallbackType,
+ scope?: object
+ ): this;
+
+ canPutOnMainBoard(
+ mainBoard: Board,
+ tileX?: number,
+ tileY?: number,
+ chessTileXYMap?: MiniBoard.TileXYZMapType,
+ ): boolean;
+ putOnMainBoard(
+ mainBoard: Board,
+ tileX?: number,
+ tileY?: number,
+ align?: boolean
+ ): this;
+ pullOutFromMainBoard(): this;
+ putBack(): this;
+
+ isOverlapping(
+ mainBoard: Board
+ ): boolean;
+
+ alignToMainBoard(
+ mainBoard: Board,
+ tileX?: number,
+ tileY?: number
+ ): this;
+
+ readonly mainBoard: Board;
+ readonly tileX: number;
+ readonly tileY: number;
+ readonly grid: Quad | Hexagon;
+
+ canRotate(n: number): boolean;
+ canRotateTo(direction: number): boolean;
+ rotate(n: number): this;
+ rotateTo(direction: number): this;
+
+ canMirror(
+ mode: MiniBoard.MirrorModeType
+ ): boolean;
+ mirror(
+ mode: MiniBoard.MirrorModeType
+ ): this;
+
+ readonly lastTransferResult: boolean;
+
+ setInteractive(enable?: boolean): this;
+
+ setDragEnable(enable?: boolean): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.js
new file mode 100644
index 000000000..250ef5e2a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/MiniBoard.js
@@ -0,0 +1,85 @@
+import Container from '../../gameobjects/container/containerlite/ContainerLite.js';
+import Methods from './Methods.js';
+import Board from '../board/LogicBoard.js';
+import MainBoardReference from './mainboard/MainBoardReference.js';
+import GetValue from '../../utils/object/GetValue.js';
+
+class MiniBoard extends Container {
+ constructor(scene, x, y, config) {
+ super(scene, x, y, 0, 0);
+ this.type = 'rexMiniBoard';
+ var boardConfig = {
+ isBoard: false,
+ grid: GetValue(config, 'grid', undefined),
+ infinity: true,
+ wrap: false
+ }
+ this.board = new Board(scene, boardConfig);
+ this.mainBoardRef = new MainBoardReference();
+ this.lastMainBoardRef = new MainBoardReference();
+
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.setFace(GetValue(o, 'face', 0));
+ var dragEnable = GetValue(o, 'draggable', undefined);
+ if (dragEnable !== undefined) {
+ this.setDraggable(dragEnable);
+ }
+ this.setPutTestCallback(GetValue(o, 'putTestCallback', undefined), GetValue(o, 'putTestCallbackScpe', undefined));
+ this.lastTransferResult = GetValue(o, 'lastTransferResult', undefined);
+ return this;
+ }
+
+ destroy(fromScene) {
+ if (!this.scene) {
+ return
+ }
+
+ this.clear(!fromScene);
+ this.board.shutdown(fromScene);
+ this.board = undefined;
+ this.setPutTestCallback(undefined, undefined);
+
+ super.destroy(fromScene);
+ }
+
+ setFace(direction) {
+ this.face = this.board.grid.directionNormalize(direction);
+ return this;
+ }
+
+ get mainBoard() {
+ return this.mainBoardRef.mainBoard;
+ }
+
+ get tileX() {
+ return this.mainBoardRef.tileX;
+ }
+
+ get tileY() {
+ return this.mainBoardRef.tileY;
+ }
+
+ get grid() {
+ return this.board.grid;
+ }
+
+ get tileXYZMap() {
+ return this.board.boardData.UIDToXYZ; // {uid:{x,y,z}}
+ }
+
+ setPutTestCallback(callback, scope) {
+ this.putTestCallback = callback;
+ this.putTestCallbackScpe = scope;
+ return this;
+ }
+}
+
+Object.assign(
+ MiniBoard.prototype,
+ Methods
+);
+
+export default MiniBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/AddChess.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/AddChess.js
new file mode 100644
index 000000000..2a500b0fb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/AddChess.js
@@ -0,0 +1,20 @@
+import IsUID from '../../chess/IsUID.js';
+
+var AddChess = function (gameObject, tileX, tileY, tileZ) {
+ var grid = this.grid;
+ grid.saveOrigin();
+ grid.setOriginPosition(this.x, this.y);
+
+ // Add chess to borad
+ this.board.addChess(gameObject, tileX, tileY, tileZ, true);
+ // Add chess to container
+ if (IsUID(gameObject)) {
+ gameObject = this.board.uidToChess(gameObject);
+ }
+ this.add(gameObject);
+
+ grid.restoreOrigin();
+ return this;
+}
+
+export default AddChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveAllChess.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveAllChess.js
new file mode 100644
index 000000000..a196a5445
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveAllChess.js
@@ -0,0 +1,6 @@
+var RemoveAllChess = function (destroy) {
+ this.board.removeAllChess(destroy);
+ return this;
+}
+
+export default RemoveAllChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveChess.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveChess.js
new file mode 100644
index 000000000..8d454a304
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/chess/RemoveChess.js
@@ -0,0 +1,6 @@
+var RemoveChess = function (gameObject, tileX, tileY, tileZ, destroy) {
+ this.board.removeChess(gameObject, tileX, tileY, tileZ, destroy);
+ return this;
+}
+
+export default RemoveChess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/DragEnd.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/DragEnd.js
new file mode 100644
index 000000000..cb69a238e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/DragEnd.js
@@ -0,0 +1,18 @@
+var DragEnd = function (pointer) {
+ var dragData = this.input.drag;
+ // Not dragging
+ if (dragData.state === 0) {
+ return;
+ }
+
+ if (pointer === undefined) {
+ pointer = this.input.pointer;
+ }
+ var dragPosition = dragData.position;
+ var dragX = pointer.x - dragPosition.x;
+ var dragY = pointer.y - dragPosition.y;
+ dragData.state = 0;
+ this.emit('dragend', pointer, dragX, dragY);
+ return this;
+}
+export default DragEnd;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerDown.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerDown.js
new file mode 100644
index 000000000..672a6b1e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerDown.js
@@ -0,0 +1,66 @@
+var OnPointerDown = function (pointer) {
+ if (!this.input.enable) {
+ return;
+ }
+ if (!pointer.isDown) {
+ return;
+ }
+
+ if (this.input.pointer === null) { // Catch new touch pointer
+ this.input.pointer = pointer;
+ }
+
+ var hitChess = OnTouchTileStart.call(this, pointer);
+ if (hitChess) {
+ OnDragStart.call(this, pointer);
+ }
+}
+
+var OnTouchTileStart = function (pointer) {
+ // Get touched tileX, tileY
+ var grid = this.grid;
+ grid.saveOrigin();
+ grid.setOriginPosition(this.x, this.y);
+ var out = this.board.worldXYToTileXY(pointer.x, pointer.y, true);
+ var tileX = out.x,
+ tileY = out.y;
+ grid.restoreOrigin();
+ this.input.tilePosition.x = tileX;
+ this.input.tilePosition.y = tileY;
+
+ // Get touched chess
+ var gameObjects = this.board.tileXYToChessArray(tileX, tileY, globChessArray);
+ var hitChess = (gameObjects.length > 0);
+ if (hitChess) {
+ // Fire events
+ var gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ gameObject = gameObjects[i];
+ if (gameObject.emit) {
+ gameObject.emit('miniboard.pointerdown', pointer);
+ }
+ this.emit('gameobjectdown', pointer, gameObject);
+ }
+ this.emit('pointerdown', pointer, this);
+ }
+ globChessArray.length = 0;
+ return hitChess;
+}
+
+var OnDragStart = function (pointer) {
+ var dragData = this.input.drag;
+ // Drag by another pointer
+ if (dragData.state === 1) {
+ return;
+ }
+
+ var dragPosition = dragData.position;
+ dragPosition.x = pointer.x - this.x;
+ dragPosition.y = pointer.y - this.y;
+ dragData.state = 1;
+ this.emit('dragstart', pointer, dragPosition.x, dragPosition.y);
+}
+
+var globChessArray = [];
+
+export default OnPointerDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerMove.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerMove.js
new file mode 100644
index 000000000..a36c3b288
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerMove.js
@@ -0,0 +1,76 @@
+var OnPointerMove = function (pointer) {
+ if (!this.input.enable) {
+ return;
+ }
+
+ OnTouchTileMove.call(this, pointer);
+ OnDrag.call(this, pointer);
+}
+
+var OnTouchTileMove = function (pointer) {
+ // Get touched tileX, tileY
+ var grid = this.grid;
+ grid.saveOrigin();
+ grid.setOriginPosition(this.x, this.y);
+ var out = this.board.worldXYToTileXY(pointer.x, pointer.y, true);
+ var tileX = out.x,
+ tileY = out.y;
+ grid.restoreOrigin();
+
+ if ((this.input.tilePosition.x === tileX) && (this.input.tilePosition.y === tileY)) {
+ // Tile position dose not change
+ return;
+ }
+ this.input.tilePosition.x = tileX;
+ this.input.tilePosition.y = tileY;
+
+ // Get touched chess
+ var gameObjects = this.board.tileXYToChessArray(tileX, tileY, globChessArray);
+ var hitChess = (gameObjects.length > 0);
+ if (hitChess) {
+ // Fire events
+ var gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ gameObject = gameObjects[i];
+ if (gameObject.emit) {
+ gameObject.emit('miniboard.pointermove', pointer);
+ }
+ this.emit('gameobjectmove', pointer, gameObject);
+ }
+ this.emit('pointermove', pointer, this);
+ } else {
+ // Move outside
+ if (this.input.pointer === pointer) { // Release touch pointer
+ this.input.pointer = null;
+ }
+ }
+ globChessArray.length = 0;
+
+ // Not dragging
+ if (this.input.drag.state === 0) {
+ if (this.input.pointer === pointer) {
+ if (!hitChess) {
+ this.input.pointer = null; // Release touch pointer
+ }
+ } else if (this.input.pointer === null) {
+ this.input.pointer = pointer; // Catch new touch pointer
+ }
+ }
+}
+
+var OnDrag = function (pointer) {
+ var dragData = this.input.drag;
+ // Not dragging
+ if (dragData.state === 0) {
+ return;
+ }
+
+ var dragPosition = dragData.position;
+ var dragX = pointer.x - dragPosition.x;
+ var dragY = pointer.y - dragPosition.y;
+ this.emit('drag', pointer, dragX, dragY);
+}
+
+var globChessArray = [];
+
+export default OnPointerMove;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerUp.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerUp.js
new file mode 100644
index 000000000..a4d50dacb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/OnPointerUp.js
@@ -0,0 +1,49 @@
+import OnDragEnd from './DragEnd.js';
+
+var OnPointerUp = function (pointer) {
+ if (!this.input.enable) {
+ return;
+ }
+
+ OnTouchTileEnd.call(this, pointer);
+ OnDragEnd.call(this, pointer);
+
+ if (this.input.pointer === pointer) { // Release touch pointer
+ this.input.pointer = null;
+ }
+}
+
+var OnTouchTileEnd = function (pointer) {
+ // Get touched tileX, tileY
+ var grid = this.grid;
+ grid.saveOrigin();
+ grid.setOriginPosition(this.x, this.y);
+ var out = this.board.worldXYToTileXY(pointer.x, pointer.y, true);
+ var tileX = out.x,
+ tileY = out.y;
+ grid.restoreOrigin();
+ this.input.tilePosition.x = tileX;
+ this.input.tilePosition.y = tileY;
+
+ // Get touched chess
+ var gameObjects = this.board.tileXYToChessArray(tileX, tileY, globChessArray);
+ var hitChess = (gameObjects.length > 0);
+ if (hitChess) {
+ // Fire events
+ var gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ gameObject = gameObjects[i];
+ if (gameObject.emit) {
+ gameObject.emit('miniboard.pointerup', pointer);
+ }
+ this.emit('gameobjectup', pointer, gameObject);
+ }
+ this.emit('pointerup', pointer, this);
+ }
+ globChessArray.length = 0;
+ return hitChess;
+}
+
+var globChessArray = [];
+
+export default OnPointerUp;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetDraggable.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetDraggable.js
new file mode 100644
index 000000000..72617124f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetDraggable.js
@@ -0,0 +1,12 @@
+var SetDraggable = function (enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.setInteractive();
+ this.input.drag.enable = enable;
+ if (!enable) {
+ this.input.drag.state = 0;
+ }
+ return this;
+}
+export default SetDraggable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetInteractive.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetInteractive.js
new file mode 100644
index 000000000..aeb50b1a1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/input/SetInteractive.js
@@ -0,0 +1,46 @@
+import OnPointerDown from './OnPointerDown.js';
+import OnPointerUp from './OnPointerUp.js';
+import OnPointerMove from './OnPointerMove.js';
+
+var SetInteractive = function (enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ if (!this.input) {
+ this.input = {
+ enable: true,
+ tilePosition: {
+ x: undefined,
+ y: undefined
+ },
+ pointer: undefined,
+ drag: {
+ enable: false,
+ state: 0,
+ position: {
+ x: undefined,
+ y: undefined
+ }
+ }
+ };
+ this.scene.input.on('pointerdown', OnPointerDown, this);
+ this.scene.input.on('pointerup', OnPointerUp, this);
+ this.scene.input.on('pointermove', OnPointerMove, this);
+
+ this.once('destroy', function () {
+ if (this.scene) {
+ this.scene.input.off('pointerdown', OnPointerDown, this);
+ this.scene.input.off('pointerup', OnPointerUp, this);
+ this.scene.input.off('pointermove', OnPointerMove, this);
+ }
+ }, this);
+ }
+
+ this.input.enable = enable;
+ if (!enable) {
+ this.input.pointer = null;
+ }
+ return this;
+};
+
+export default SetInteractive;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/AlignToMainBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/AlignToMainBoard.js
new file mode 100644
index 000000000..84ed761dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/AlignToMainBoard.js
@@ -0,0 +1,15 @@
+var AlignToMainBoard = function (mainBoard, tileX, tileY) {
+ if (!mainBoard) {
+ return this;
+ }
+
+ if (tileX === undefined) {
+ var out = mainBoard.worldXYToTileXY(this.x, this.y, true);
+ tileX = out.x;
+ tileY = out.y;
+ }
+ mainBoard.gridAlign(this, tileX, tileY);
+ return this;
+}
+
+export default AlignToMainBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/CanPutOnMainBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/CanPutOnMainBoard.js
new file mode 100644
index 000000000..31dec274e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/CanPutOnMainBoard.js
@@ -0,0 +1,41 @@
+var CanPutOnMainBoard = function (mainBoard, tileX, tileY, chessTileXYMap) {
+ if (!mainBoard) {
+ return false;
+ }
+ if (chessTileXYMap === undefined) {
+ chessTileXYMap = this.tileXYZMap; // {uid:{x,y,z}}
+ }
+
+ var chessTileXYZ, mappedTileXY, isOccupied;
+ for (var uid in chessTileXYMap) {
+ chessTileXYZ = chessTileXYMap[uid];
+ mappedTileXY = mainBoard.offset(chessTileXYZ, tileX, tileY, true);
+ if (!mainBoard.contains(mappedTileXY.x, mappedTileXY.y)) {
+ return false;
+ }
+
+ if (this.putTestCallback) {
+ // Custom test function
+ targetTileXY.x = mappedTileXY.x;
+ targetTileXY.y = mappedTileXY.x;
+ targetTileXY.z = chessTileXYZ.z;
+ var chess = this.board.uidToChess(uid);
+ if (this.putTestCallbackScpe) {
+ isOccupied = this.putTestCallback.call(this.putTestCallbackScpe, targetTileXY, mainBoard, chess);
+ } else {
+ isOccupied = this.putTestCallback(targetTileXY, mainBoard, chess);
+ }
+ } else {
+ // Default test function
+ isOccupied = mainBoard.contains(mappedTileXY.x, mappedTileXY.y, chessTileXYZ.z);
+ }
+ if (isOccupied) {
+ return false;
+ }
+ }
+ return true;
+}
+
+var targetTileXY = { x: 0, y: 0, z: 0, };
+
+export default CanPutOnMainBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/IsOverlapping.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/IsOverlapping.js
new file mode 100644
index 000000000..3df9788d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/IsOverlapping.js
@@ -0,0 +1,15 @@
+var IsOverlapping = function (mainBoard, tileZ) {
+ if (!mainBoard) {
+ return false;
+ }
+
+ var gameObject;
+ for (var uid in this.tileXYZMap) {
+ gameObject = this.board.uidToChess(uid);
+ if (mainBoard.isOverlappingPoint(gameObject.x, gameObject.y, tileZ)) {
+ return true;
+ }
+ }
+ return false;
+}
+export default IsOverlapping;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/MainBoardReference.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/MainBoardReference.js
new file mode 100644
index 000000000..7fb4c0adf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/MainBoardReference.js
@@ -0,0 +1,17 @@
+class MainBoardReference {
+ constructor(miniBoard) {
+ this.miniBoard = miniBoard;
+ this.set(null);
+ }
+ set(mainBoard, tileX, tileY) {
+ if (!mainBoard) {
+ mainBoard = null;
+ tileX = null;
+ tileY = null;
+ }
+ this.mainBoard = mainBoard;
+ this.tileX = tileX;
+ this.tileY = tileY;
+ }
+}
+export default MainBoardReference;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PullOutFromMainBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PullOutFromMainBoard.js
new file mode 100644
index 000000000..56156ac55
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PullOutFromMainBoard.js
@@ -0,0 +1,14 @@
+var PullOutFromMainBoard = function () {
+ var mainBoard = this.mainBoard;
+ if (mainBoard === null) {
+ return this;
+ }
+
+ var tileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ for (var uid in tileXYZMap) {
+ mainBoard.removeChess(parseInt(uid));
+ }
+ this.setMainBoard(null);
+ return this;
+}
+export default PullOutFromMainBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutBack.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutBack.js
new file mode 100644
index 000000000..eb112de82
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutBack.js
@@ -0,0 +1,8 @@
+var PutBack = function () {
+ var mainBoard = this.lastMainBoardRef.mainBoard;
+ var tileX = this.lastMainBoardRef.tileX;
+ var tileY = this.lastMainBoardRef.tileY;
+ this.putOnMainBoard(mainBoard, tileX, tileY, false);
+ return this;
+}
+export default PutBack;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutOnMainBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutOnMainBoard.js
new file mode 100644
index 000000000..e24cef2dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/PutOnMainBoard.js
@@ -0,0 +1,36 @@
+var PutOnMainBoard = function (mainBoard, tileX, tileY, align) {
+ if (!mainBoard) {
+ return this;
+ }
+
+ if (tileX === undefined) {
+ var out = mainBoard.worldXYToTileXY(this.x, this.y, true);
+ tileX = out.x;
+ tileY = out.y;
+ }
+ if (align === undefined) {
+ align = true;
+ }
+
+ this.pullOutFromMainBoard();
+ if (!this.canPutOnMainBoard(mainBoard, tileX, tileY)) {
+ return this;
+ }
+
+ this.setMainBoard(mainBoard, tileX, tileY);
+ var tileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ var chessTileXYZ, mappedTileXY;
+ for (var uid in tileXYZMap) {
+ chessTileXYZ = tileXYZMap[uid];
+ uid = parseInt(uid);
+
+ mappedTileXY = mainBoard.offset(chessTileXYZ, tileX, tileY, true);
+ mainBoard.addChess(uid, mappedTileXY.x, mappedTileXY.y, chessTileXYZ.z, false);
+ }
+ if (align) {
+ this.alignToMainBoard(mainBoard, tileX, tileY);
+ }
+
+ return this;
+}
+export default PutOnMainBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/SetMainboard.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/SetMainboard.js
new file mode 100644
index 000000000..a9b192ea3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/mainboard/SetMainboard.js
@@ -0,0 +1,8 @@
+var SetMainBoard = function (mainBoard, tileX, tileY) {
+ this.mainBoardRef.set(mainBoard, tileX, tileY);
+ if (mainBoard) {
+ this.lastMainBoardRef.set(mainBoard, tileX, tileY);
+ }
+ return this;
+}
+export default SetMainBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/CanMoveToTile.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/CanMoveToTile.js
new file mode 100644
index 000000000..8f8f08533
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/CanMoveToTile.js
@@ -0,0 +1,48 @@
+var CanMoveToTile = function (tileX, tileY, direction) {
+ var miniBoard = this.parent;
+ var mainBoard = miniBoard.mainBoard;
+ // Not on a mainBoard
+ if (mainBoard == null) {
+ return false;
+ }
+
+ myTileXYZ.x = miniBoard.tileX;
+ myTileXYZ.y = miniBoard.tileY;
+ targetTileXYZ.x = tileX;
+ targetTileXYZ.y = tileY;
+ // Move to current position
+ if ((targetTileXYZ.x === myTileXYZ.x) && (targetTileXYZ.y === myTileXYZ.y)) {
+ return true;
+ }
+
+ miniBoard.pullOutFromMainBoard();
+ // Can not put on main board
+ if (!miniBoard.canPutOnMainBoard(mainBoard, targetTileXYZ.x, targetTileXYZ.y)) {
+ miniBoard.putBack();
+ return false;
+ }
+
+ // Custom moveable test
+ if (this.moveableTestCallback) {
+ if (direction === undefined) {
+ direction = mainBoard.getNeighborTileDirection(myTileXYZ, targetTileXYZ);
+ }
+ if (this.moveableTestScope) {
+ var moveable = this.moveableTestCallback.call(this.moveableTestScope, myTileXYZ, targetTileXYZ, direction, mainBoard);
+ } else {
+ var moveable = this.moveableTestCallback(myTileXYZ, targetTileXYZ, direction, mainBoard);
+ }
+ if (!moveable) {
+ miniBoard.putBack();
+ return false;
+ }
+ }
+
+ miniBoard.putBack();
+ return true;
+}
+
+var myTileXYZ = { x: 0, y: 0, z: 0 };
+var targetTileXYZ = { x: 0, y: 0, z: 0 };
+
+export default CanMoveToTile;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveTo.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveTo.js
new file mode 100644
index 000000000..27cef9e58
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveTo.js
@@ -0,0 +1,135 @@
+import MoveToTask from '../../../behaviors/moveto/MoveTo.js';
+import TickTask from '../../../utils/componentbase/SceneUpdateTickTask.js';
+
+import CanMoveToTile from './CanMoveToTile.js';
+import MoveToTile from './MoveToTile.js';
+import MoveToward from './MoveToward.js';
+import MoveToRandomNeighbor from './MoveToRandomNeighbor.js';
+
+import GetValue from '../../../utils/object/GetValue.js';
+
+class MoveTo extends TickTask {
+ constructor(miniBoard, config) {
+ super(miniBoard, config);
+ // this.parent = miniBoard;
+
+ this.moveToTask = new MoveToTask(miniBoard, { tickingMode: 0 });
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.timeScale = GetValue(o, 'timeScale', 1);
+ this.setSpeed(GetValue(o, 'speed', 400));
+ this.destinationTileX = GetValue(o, 'destinationTileX', null);
+ this.destinationTileY = GetValue(o, 'destinationTileY', null);
+ this.destinationDirection = GetValue(o, 'destinationDirection', null);
+ this.lastMoveResult = GetValue(o, 'lastMoveResult', undefined);
+ return this;
+ }
+
+ toJSON() {
+ return {
+ isRunning: this.isRunning,
+ enable: this.enable,
+ timeScale: this.timeScale,
+ speed: this.speed,
+ moveableTest: this.moveableTestCallback,
+ moveableTestScope: this.moveableTestScope,
+ destinationTileX: this.destinationTileX,
+ destinationTileY: this.destinationTileY,
+ destinationDirection: this.destinationDirection,
+ lastMoveResult: this.lastMoveResult,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.moveToTask.shutdown(fromScene);
+ super.shutdown(fromScene);
+ }
+
+ set enable(value) {
+ this.moveToTask.setEnable(value);
+ }
+
+ get enable() {
+ return this.moveToTask.enable;
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ get timeScale() {
+ return this.moveToTask.timeScale;
+ }
+
+ set timeScale(value) {
+ this.moveToTask.timeScale = value;
+ }
+
+ set speed(value) {
+ this.moveToTask.setSpeed(value);
+ }
+
+ get speed() {
+ return this.moveToTask.speed;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ moveAlongLine(startX, startY, endX, endY) {
+ if (startX !== undefined) {
+ this.parent.x = startX;
+ }
+ if (startY !== undefined) {
+ this.parent.y = startY;
+ }
+ this.moveToTask.moveTo(endX, endY);
+ return this;
+ };
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var moveToTask = this.moveToTask;
+ moveToTask.update(time, delta);
+ if (!moveToTask.isRunning) {
+ this.complete();
+ return this;
+ }
+ return this;
+ }
+}
+
+var methods = {
+ canMoveTo: CanMoveToTile,
+ moveTo: MoveToTile,
+ moveToward: MoveToward,
+ moveToRandomNeighbor: MoveToRandomNeighbor,
+};
+Object.assign(
+ MoveTo.prototype,
+ methods
+);
+
+
+export default MoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToRandomNeighbor.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToRandomNeighbor.js
new file mode 100644
index 000000000..1010157c3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToRandomNeighbor.js
@@ -0,0 +1,28 @@
+import Clone from '../../../utils/object/Clone.js';
+import Shuffle from '../../../utils/array/Shuffle.js';
+
+var MoveToRandomNeighbor = function () {
+ var miniBoard = this.parent;
+ var mainBoard = miniBoard.mainBoard;
+ // Not on a mainBoard
+ if (mainBoard == null) {
+ this.lastMoveResult = false;
+ return this;
+ }
+
+ var directions = mainBoard.grid.allDirections;
+ if (globDirections.length !== directions.length) {
+ Clone(directions, globDirections);
+ }
+ Shuffle(globDirections);
+ for (var i = 0, cnt = globDirections.length; i < cnt; i++) {
+ this.moveToward(globDirections[i]);
+ if (this.lastMoveResult) {
+ return this;
+ }
+ }
+ return this;
+}
+
+var globDirections = [];
+export default MoveToRandomNeighbor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToTile.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToTile.js
new file mode 100644
index 000000000..035fddeb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToTile.js
@@ -0,0 +1,64 @@
+import GetValue from '../../../utils/object/GetValue.js';
+
+var MoveToTile = function (tileX, tileY, direction) {
+ var miniBoard = this.parent;
+ var mainBoard = miniBoard.mainBoard;
+ // Not on a mainBoard
+ if (mainBoard == null) {
+ this.lastMoveResult = false;
+ return this;
+ }
+
+ if ((tileX != null) && (typeof (tileX) !== 'number')) {
+ var config = tileX;
+ tileX = GetValue(config, 'x', undefined);
+ tileY = GetValue(config, 'y', undefined);
+ direction = GetValue(config, 'direction', undefined);
+ }
+ myTileXY.x = miniBoard.tileX;
+ myTileXY.y = miniBoard.tileY;
+ if ((direction !== undefined) &&
+ (tileX == null) || (tileY == null)) {
+ // Get neighbor tile position if direction is not undefined
+ var out = mainBoard.getNeighborTileXY(myTileXY, direction, true);
+ if (out !== null) {
+ tileX = out.x;
+ tileY = out.y;
+ } else {
+ tileX = null;
+ tileY = null;
+ }
+ }
+
+ // invalid tile position
+ if ((tileX == null) || (tileY == null)) {
+ this.lastMoveResult = false;
+ return this;
+ }
+ if (direction === undefined) {
+ targetTileXY.x = tileX;
+ targetTileXY.y = tileY;
+ direction = board.getNeighborTileDirection(myTileXY, targetTileXY);
+ }
+ if (!this.canMoveTo(tileX, tileY, direction)) {
+ this.lastMoveResult = false;
+ return this;
+ }
+ this.destinationTileX = tileX;
+ this.destinationTileY = tileY;
+ this.destinationDirection = direction;
+
+ // Not support wrap mode
+ var out = mainBoard.tileXYToWorldXY(tileX, tileY, true);
+ this.moveToTask.moveTo(out.x, out.y);
+ miniBoard.putOnMainBoard(mainBoard, tileX, tileY, false);
+
+ this.isRunning = true;
+ this.lastMoveResult = true;
+ return this;
+}
+
+var myTileXY = {};
+var targetTileXY = {};
+
+export default MoveToTile;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToward.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToward.js
new file mode 100644
index 000000000..aadd49649
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/moveto/MoveToward.js
@@ -0,0 +1,5 @@
+var MoveToward = function (direction) {
+ this.moveTo(undefined, undefined, direction);
+ return this;
+}
+export default MoveToward;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanMirror.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanMirror.js
new file mode 100644
index 000000000..c7e4091a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanMirror.js
@@ -0,0 +1,9 @@
+import MirrorTransfer from './transferfunctions/Mirror.js';
+var CanMirror = function(mode) {
+ if (this.mainBoard === null) {
+ return true;
+ }
+ var newTileXYZMap = MirrorTransfer.call(this, mode);
+ return this.canPutOnMainBoard(this.mainBoard, tileX, tileY, newTileXYZMap);
+}
+export default CanMirror;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotate.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotate.js
new file mode 100644
index 000000000..a4906e6ae
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotate.js
@@ -0,0 +1,10 @@
+import RotateTransfer from './transferfunctions/Rotate.js';
+
+var CanRotate = function (direction) {
+ if (this.mainBoard === null) {
+ return true;
+ }
+ var newTileXYZMap = RotateTransfer.call(this, direction);
+ return this.canPutOnMainBoard(this.mainBoard, tileX, tileY, newTileXYZMap);
+}
+export default CanRotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotateTo.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotateTo.js
new file mode 100644
index 000000000..e3fb3b6fb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/CanRotateTo.js
@@ -0,0 +1,5 @@
+var CanRotateTo = function(direction) {
+ direction -= this.face;
+ return this.canRotate(direction);
+}
+export default CanRotateTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Mirror.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Mirror.js
new file mode 100644
index 000000000..6a78ab8f9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Mirror.js
@@ -0,0 +1,27 @@
+import MirrorTransfer from './transferfunctions/Mirror.js';
+import ResetChessTileXYZ from './ResetChessTileXYZ.js';
+
+var Mirror = function (mode) {
+ var isOnMainBoard = (this.mainBoard != null);
+ if (isOnMainBoard) {
+ this.pullOutFromMainBoard();
+ }
+
+ var newTileXYZMap = MirrorTransfer.call(this, mode);
+
+ if (isOnMainBoard) {
+ var mainBoard = this.lastMainBoardRef.mainBoard;
+ var tileX = this.lastMainBoardRef.tileX;
+ var tileY = this.lastMainBoardRef.tileY;
+ this.lastTransferResult = this.canPutOnMainBoard(mainBoard, tileX, tileY, newTileXYZMap);
+ if (this.lastTransferResult) {
+ ResetChessTileXYZ.call(this, newTileXYZMap);
+ }
+ this.putBack();
+ } else {
+ this.lastTransferResult = true;
+ ResetChessTileXYZ.call(this, newTileXYZMap);
+ }
+ return this;
+}
+export default Mirror;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/ResetChessTileXYZ.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/ResetChessTileXYZ.js
new file mode 100644
index 000000000..10ea0f4b8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/ResetChessTileXYZ.js
@@ -0,0 +1,11 @@
+var ResetChessTileXYZ = function(newTileXYZMap) {
+ this.removeAllChess();
+ var newTileXYZ;
+ for(var uid in newTileXYZMap) {
+ newTileXYZ = newTileXYZMap[uid];
+ uid = parseInt(uid);
+ this.addChess(uid, newTileXYZ.x, newTileXYZ.y, newTileXYZ.z, false);
+ }
+ return this;
+}
+export default ResetChessTileXYZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Rotate.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Rotate.js
new file mode 100644
index 000000000..1c9e98f89
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/Rotate.js
@@ -0,0 +1,35 @@
+import RotateTransfer from './transferfunctions/Rotate.js';
+import ResetChessTileXYZ from './ResetChessTileXYZ.js';
+
+var Rotate = function (direction) {
+ if (direction === 0) {
+ return this;
+ }
+
+ var isOnMainBoard = (this.mainBoard != null);
+ if (isOnMainBoard) {
+ this.pullOutFromMainBoard();
+ }
+
+ var newTileXYZMap = RotateTransfer.call(this, direction);
+
+ if (isOnMainBoard) {
+ var mainBoard = this.lastMainBoardRef.mainBoard;
+ var tileX = this.lastMainBoardRef.tileX;
+ var tileY = this.lastMainBoardRef.tileY;
+ this.lastTransferResult = this.canPutOnMainBoard(mainBoard, tileX, tileY, newTileXYZMap);
+ if (this.lastTransferResult) {
+ ResetChessTileXYZ.call(this, newTileXYZMap);
+ }
+ this.putBack();
+ } else {
+ this.lastTransferResult = true;
+ ResetChessTileXYZ.call(this, newTileXYZMap);
+ }
+
+ if (this.lastTransferResult) {
+ this.setFace(this.face + direction);
+ }
+ return this;
+}
+export default Rotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/RotateTo.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/RotateTo.js
new file mode 100644
index 000000000..4b6e0ff8b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/RotateTo.js
@@ -0,0 +1,6 @@
+var RotateTo = function (direction) {
+ direction -= this.face;
+ this.rotate(direction);
+ return this;
+}
+export default RotateTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/SetOrigin.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/SetOrigin.js
new file mode 100644
index 000000000..9a81fae50
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/SetOrigin.js
@@ -0,0 +1,41 @@
+import GetMinMaxTileXY from '../utils/GetMinMaxTileXY.js';
+import Linear from '../../../utils/math/Linear.js';
+import OffsetTransfer from './transferfunctions/Offset.js';
+import ResetChessTileXYZ from './ResetChessTileXYZ.js';
+
+var SetOrigin = function (originX, originY) {
+ switch (originX) {
+ case 'center':
+ originX = 0.5;
+ originY = 0.5;
+ break;
+ case 'top-left':
+ case 'left-top':
+ originX = 0;
+ originY = 0;
+ break;
+ }
+ if (originX === undefined) {
+ originX = 0.5;
+ }
+ if (originY === undefined) {
+ originY = originX;
+ }
+ var minMaxTileXY = GetMinMaxTileXY.call(this, undefined, true);
+ var offsetX = -Math.floor(Linear(minMaxTileXY.minX, minMaxTileXY.maxX, originX));
+ var offsetY = -Math.floor(Linear(minMaxTileXY.minY, minMaxTileXY.maxY, originY));
+
+ if ((offsetX !== 0) || (offsetY !== 0)) {
+ var newTileXYZMap = OffsetTransfer.call(this, offsetX, offsetY);
+ ResetChessTileXYZ.call(this, newTileXYZMap);
+ var worldOffsetXY = this.board.tileXYToWorldXY(offsetX, offsetY);
+ var world0 = this.board.tileXYToWorldXY(0, 0);
+ this.setPosition(
+ (this.x + (world0.x - worldOffsetXY.x)),
+ (this.y + (world0.y - worldOffsetXY.y))
+ );
+ }
+ return this;
+}
+
+export default SetOrigin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Mirror.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Mirror.js
new file mode 100644
index 000000000..d0840f988
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Mirror.js
@@ -0,0 +1,28 @@
+var Mirror = function (mode, chessTileXYZMap, out) {
+ if (mode === undefined) {
+ mode = 1;
+ } else if (typeof (mode) === 'string') {
+ mode = MODE[mode];
+ }
+ if (chessTileXYZMap === undefined) {
+ chessTileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ }
+ if (out === undefined) {
+ out = {};
+ }
+ var chessTileXYZ, newTileXYZ;
+ for (var uid in chessTileXYZMap) {
+ chessTileXYZ = chessTileXYZMap[uid];
+ newTileXYZ = this.board.mirror(chessTileXYZ, mode);
+ newTileXYZ.z = chessTileXYZ.z;
+ out[uid] = newTileXYZ;
+ }
+ return out; // {uid:{x,y,z}}
+}
+
+const MODE = {
+ x: 1,
+ y: 2,
+ 'x&y': 3
+}
+export default Mirror;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Offset.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Offset.js
new file mode 100644
index 000000000..1e8e0cb9c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Offset.js
@@ -0,0 +1,18 @@
+var Offset = function (tileX, tileY, chessTileXYZMap, out) {
+ if (chessTileXYZMap === undefined) {
+ chessTileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ }
+ if (out === undefined) {
+ out = {};
+ }
+ var chessTileXYZ, newTileXYZ;
+ for (var uid in chessTileXYZMap) {
+ chessTileXYZ = chessTileXYZMap[uid];
+ newTileXYZ = this.board.offset(chessTileXYZ, tileX, tileY);
+ newTileXYZ.z = chessTileXYZ.z;
+ out[uid] = newTileXYZ;
+ }
+ return out; // {uid:{x,y,z}}
+}
+
+export default Offset;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Rotate.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Rotate.js
new file mode 100644
index 000000000..088e6001e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/transform/transferfunctions/Rotate.js
@@ -0,0 +1,21 @@
+var Rotate = function (direction, chessTileXYZMap, out) {
+ if (direction === undefined) {
+ direction = 0;
+ }
+ if (chessTileXYZMap === undefined) {
+ chessTileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ }
+ if (out === undefined) {
+ out = {};
+ }
+ var chessTileXYZ, newTileXYZ;
+ for (var uid in chessTileXYZMap) {
+ chessTileXYZ = chessTileXYZMap[uid];
+ newTileXYZ = this.board.rotate(chessTileXYZ, direction);
+ newTileXYZ.z = chessTileXYZ.z;
+ out[uid] = newTileXYZ;
+ }
+ return out; // {uid:{x,y,z}}
+}
+
+export default Rotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/miniboard/utils/GetMinMaxTileXY.js b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/utils/GetMinMaxTileXY.js
new file mode 100644
index 000000000..baae35404
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/miniboard/utils/GetMinMaxTileXY.js
@@ -0,0 +1,37 @@
+var GetMinMaxTileXY = function (chessTileXYZMap, out) {
+ if (chessTileXYZMap === undefined) {
+ chessTileXYZMap = this.tileXYZMap; // {uid:{x,y,z}}
+ }
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globResult;
+ }
+ var minX = Infinity, maxX = -Infinity;
+ var minY = Infinity, maxY = -Infinity;
+ var chessTileXYZ;
+ for (var uid in this.tileXYZMap) {
+ chessTileXYZ = this.tileXYZMap[uid];
+ if (chessTileXYZ.x < minX) {
+ minX = chessTileXYZ.x;
+ }
+ if (chessTileXYZ.x > maxX) {
+ maxX = chessTileXYZ.x;
+ }
+ if (chessTileXYZ.y < minY) {
+ minY = chessTileXYZ.y;
+ }
+ if (chessTileXYZ.y > maxY) {
+ maxY = chessTileXYZ.y;
+ }
+ }
+ out.minX = minX;
+ out.minY = minY;
+ out.maxX = maxX;
+ out.maxY = maxY;
+ return out;
+}
+
+var globResult = {};
+
+export default GetMinMaxTileXY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.d.ts
new file mode 100644
index 000000000..91a9ddcb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.d.ts
@@ -0,0 +1,6 @@
+import Monopoly from './Monopoly';
+
+export default function (
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Monopoly.IConfig
+): Monopoly;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.js
new file mode 100644
index 000000000..976627bea
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Factory.js
@@ -0,0 +1,11 @@
+import Monopoly from './Monopoly.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('monopoly', function (gameObject, config) {
+ return new Monopoly(gameObject, config);
+});
+
+SetValue(window, 'RexPlugins.Board.Monopoly', Monopoly);
+
+export default Monopoly;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetCost.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetCost.js
new file mode 100644
index 000000000..4a05cc7db
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetCost.js
@@ -0,0 +1,11 @@
+var GetCost = function (curTileXY, preTileXY) {
+ if (typeof (this.costCallback) === 'number') {
+ return this.costCallback;
+ }
+ if (this.costCallbackScope) {
+ return this.costCallback.call(this.costCallbackScope, curTileXY, preTileXY, this);
+ } else {
+ return this.costCallback(curTileXY, preTileXY, this);
+ }
+}
+export default GetCost;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetNextTile.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetNextTile.js
new file mode 100644
index 000000000..073f7ed55
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetNextTile.js
@@ -0,0 +1,66 @@
+import { CreateTileData } from './TileData.js';
+import AreTileXYEqual from '../utils/AreTileXYEqual.js';
+
+import GetRandom from '../../utils/array/GetRandom.js';
+
+var GetNextTile = function (curTileData, preTileData) {
+ var board = this.board;
+ var directions = board.grid.allDirections;
+ var forwardTileData = null,
+ backwardTileData = null;
+ var neighborTileXArray = []; // forward and other neighbors, exclude backward
+ var neighborTileXY, neighborTileData = null;
+ for (var i = 0, cnt = directions.length; i < cnt; i++) {
+ neighborTileXY = board.getNeighborTileXY(curTileData, directions[i], true);
+ if (neighborTileXY === null) {
+ continue;
+ }
+ if (!board.contains(neighborTileXY.x, neighborTileXY.y, this.pathTileZ)) {
+ continue;
+ }
+ neighborTileData = CreateTileData(neighborTileXY.x, neighborTileXY.y, directions[i]);
+
+ if (directions[i] === curTileData.direction) {
+ forwardTileData = neighborTileData;
+ }
+ if ((preTileData !== undefined) && (AreTileXYEqual(neighborTileXY, preTileData))) {
+ backwardTileData = neighborTileData;
+ } else {
+ neighborTileXArray.push(neighborTileData);
+ }
+ }
+
+ var nextTileData;
+ if ((backwardTileData === null) && (neighborTileXArray.length === 0)) {
+ // no valid neighbor
+ nextTileData = null;
+ } else if ((backwardTileData === null) && (neighborTileXArray.length === 1)) {
+ // 1 neighbor
+ nextTileData = neighborTileXArray[0];
+ } else if ((backwardTileData !== null) && (neighborTileXArray.length === 0)) {
+ // 1 backward neighbor
+ nextTileData = backwardTileData;
+ } else {
+ // 2 or more neighobrs
+ switch (this.pickMode) {
+ case 1: // random all
+ if (backwardTileData !== null) {
+ neighborTileXArray.push(backwardTileData);
+ }
+ nextTileData = GetRandom(neighborTileXArray);
+ break;
+
+ default: // case 0: forward first
+ if (forwardTileData !== null) {
+ nextTileData = forwardTileData;
+ } else {
+ nextTileData = GetRandom(neighborTileXArray);
+ }
+ break;
+ }
+ }
+
+ return nextTileData;
+}
+
+export default GetNextTile;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetPath.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetPath.js
new file mode 100644
index 000000000..0e26fbb4a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/GetPath.js
@@ -0,0 +1,47 @@
+import { CreateTileData } from './TileData.js';
+import CONST from './const.js';
+
+const STOP = CONST.STOP;
+
+var GetPath = function (movingPoints, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ if (this.board === null) { // chess is not in board
+ return out;
+ }
+ var curTileXYZ = this.chessData.tileXYZ,
+ curTileData = CreateTileData(curTileXYZ.x, curTileXYZ.y, this.face),
+ nextTileData;
+ var cost;
+ while (movingPoints > 0) {
+ nextTileData = this.getNextTile(curTileData, this.preTileXY);
+ if (nextTileData === null) {
+ break;
+ }
+ cost = this.getCost(nextTileData, curTileData);
+ if (cost === STOP) {
+ cost = movingPoints;
+ }
+ nextTileData.cost = cost;
+ if (movingPoints >= cost) {
+ out.push(nextTileData);
+ }
+ movingPoints -= cost;
+
+ this.preTileXY = curTileData;
+ curTileData = nextTileData;
+ }
+
+ // remove cost = 0 at tail
+ for (var i = out.length - 1; i >= 0; i--) {
+ if (out[i].cost === 0) {
+ out.length = i;
+ } else {
+ break;
+ }
+ }
+ return out;
+}
+
+export default GetPath;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Methods.js
new file mode 100644
index 000000000..5cd7f94cc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Methods.js
@@ -0,0 +1,9 @@
+import GetPath from './GetPath.js';
+import GetNextTile from './GetNextTile.js';
+import GetCost from './GetCost.js';
+
+export default {
+ getPath: GetPath,
+ getNextTile: GetNextTile,
+ getCost: GetCost,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.d.ts
new file mode 100644
index 000000000..8ca26ab2e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.d.ts
@@ -0,0 +1,58 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+import { TileXYType } from '../types/Position';
+import Board from '../board/Board';
+
+export default Monopoly;
+
+declare namespace Monopoly {
+
+ type STOP = -1;
+ type BLOCKER = null;
+
+ type NodeType = {
+ x: number, y: number,
+ direction: number
+ }
+
+ type CostCallbackType = (
+ curTile: NodeType | null, preTile: NodeType | null,
+ pathFinder: Monopoly
+ )
+ => number | STOP | BLOCKER;
+
+ interface IConfig {
+ face?: number,
+
+ pathTileZ?: number,
+ cost?: number,
+ costCallback?: CostCallbackType,
+ costCallbackScope?: object,
+ }
+}
+
+declare class Monopoly extends ComponentBase {
+ constructor(
+ gameObject: ChessType,
+ config?: Monopoly.IConfig
+ );
+
+ readonly gameObject: ChessType;
+ readonly board: Board;
+
+ setCostFunction(cost: number): this;
+ setCostFunction(
+ callback: Monopoly.CostCallbackType,
+ scope?: object
+ ): this;
+
+ setFace(direction: number): this;
+
+ getPath(
+ movingPoints: number,
+ out?: TileXYType[]
+ ): TileXYType[];
+
+ readonly STOP: Monopoly.STOP;
+ readonly BLOCKER: Monopoly.BLOCKER;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.js
new file mode 100644
index 000000000..7271ac3c4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/Monopoly.js
@@ -0,0 +1,95 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import Methods from './Methods.js';
+import GetChessData from '../chess/GetChessData.js';
+import CONST from './const.js';
+import GetValue from '../../utils/object/GetValue.js';
+
+const BLOCKER = CONST.BLOCKER;
+const STOP = CONST.STOP;
+
+class Monopoly extends ComponentBase {
+ constructor(gameObject, config) {
+ super(gameObject, { eventEmitter: false });
+ // No event emitter
+ // this.parent = gameObject;
+
+ this.chessData = GetChessData(gameObject);
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.preTileXY = GetValue(o, 'preTileXY', undefined);
+ var costCallback = GetValue(o, 'costCallback', undefined);
+ var costCallbackScope = GetValue(o, 'costCallbackScope', undefined);
+ if (costCallback === undefined) {
+ costCallback = GetValue(o, 'cost', 1);
+ }
+ this.setFace(GetValue(o, 'face', 0));
+ this.setPathMode(GetValue(o, 'pathMode', 0));
+ this.setPathTileZ(GetValue(o, 'pathTileZ', 0));
+ this.setCostFunction(costCallback, costCallbackScope);
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ this.chessData = undefined;
+
+ super.shutdown(fromScene);
+ }
+
+ setFace(direction) {
+ direction = this.board.grid.directionNormalize(direction);
+ this.face = direction;
+ return this;
+ }
+
+ setPathMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = PATHMODE[mode];
+ }
+ this.pathMode = mode;
+ return this;
+ }
+
+ setCostFunction(callback, scope) {
+ this.costCallback = callback;
+ this.costCallbackScope = scope;
+ return this;
+ }
+
+ setPathTileZ(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.pathTileZ = value;
+ return this;
+ }
+
+ get BLOCKER() {
+ return BLOCKER;
+ }
+
+ get STOP() {
+ return STOP;
+ }
+
+ get board() {
+ return this.chessData.board;
+ }
+}
+
+Object.assign(
+ Monopoly.prototype,
+ Methods
+);
+
+const PATHMODE = {
+ 'forward': 0,
+ 'random': 1
+}
+export default Monopoly;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/TileData.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/TileData.js
new file mode 100644
index 000000000..f8aae631e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/TileData.js
@@ -0,0 +1,5 @@
+var CreateTileData = function (x, y, direction) {
+ return { x: x, y: y, direction: direction };
+}
+
+export { CreateTileData };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/monopoly/const.js b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/const.js
new file mode 100644
index 000000000..e172005b9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/monopoly/const.js
@@ -0,0 +1,5 @@
+export default {
+ // special cost
+ 'BLOCKER': null,
+ 'STOP': -1,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/CanMoveToTile.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/CanMoveToTile.js
new file mode 100644
index 000000000..d7c570af5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/CanMoveToTile.js
@@ -0,0 +1,74 @@
+var CanMoveToTile = function (tileX, tileY, direction) {
+ var board = this.chessData.board;
+ // Chess is not in a board
+ if (board == null) {
+ return false;
+ }
+ var myTileXYZ = this.chessData.tileXYZ;
+ var myTileX = myTileXYZ.x,
+ myTileY = myTileXYZ.y,
+ myTileZ = myTileXYZ.z;
+
+ // Move to current position
+ if ((tileX === myTileX) && (tileY === myTileY)) {
+ return true;
+ }
+
+ // Target position is not in board
+ if (!board.contains(tileX, tileY)) {
+ return false;
+ }
+
+ // Blocker test
+ if (this.blockerTest) {
+ if (board.hasBlocker(tileX, tileY)) {
+ return false;
+ }
+ }
+
+ // Edge-blocker test
+ if (this.edgeBlockerTest) {
+ // // TODO
+ }
+
+ // Custom moveable test
+ if (this.moveableTestCallback) {
+ if (direction === undefined) {
+ direction = this.chessData.getTileDirection(tileX, tileY);
+ }
+ targetTileXY.x = tileX;
+ targetTileXY.y = tileY;
+ targetTileXY.z = myTileZ;
+ if (this.moveableTestScope) {
+ var moveable = this.moveableTestCallback.call(this.moveableTestScope, myTileXYZ, targetTileXY, direction, board);
+ } else {
+ var moveable = this.moveableTestCallback(myTileXYZ, targetTileXY, direction, board);
+ }
+ if (!moveable) {
+ return false;
+ }
+ }
+
+ // Sneak mode, change tileZ in MoveToTile method
+ // if (this.sneakMode) {
+ // }
+
+ // Occupied test
+ if (this.occupiedTest && !this.sneakMode) {
+ var occupiedChess = board.tileXYZToChess(tileX, tileY, myTileZ);
+ if (occupiedChess) {
+ this.emit('occupy', occupiedChess, this.parent, this);
+ // Try to move occupiedChess away in this event
+ // Still ooccupied?
+ if (board.contains(tileX, tileY, myTileZ)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+var targetTileXY = { x: 0, y: 0, z: 0, };
+
+export default CanMoveToTile;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.d.ts
new file mode 100644
index 000000000..fef3dda0a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.d.ts
@@ -0,0 +1,6 @@
+import MoveTo from './MoveTo';
+
+export default function (
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: MoveTo.IConfig
+): MoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.js
new file mode 100644
index 000000000..a60005bf0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Factory.js
@@ -0,0 +1,15 @@
+import MoveTo from './MoveTo.js';
+import MiniBoardMoveTo from '../miniboard/moveto/MoveTo.js';
+import IsMiniBoardObject from '../miniboard/IsMiniBoardObject.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('moveTo', function (gameObject, config) {
+ var klass = (IsMiniBoardObject(gameObject)) ? MiniBoardMoveTo : MoveTo;
+ return new klass(gameObject, config);
+});
+
+SetValue(window, 'RexPlugins.Board.MoveTo', MoveTo);
+SetValue(window, 'RexPlugins.Board.MiniBoardMoveTo', MiniBoardMoveTo);
+
+export default MoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/GetSneakTileZ.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/GetSneakTileZ.js
new file mode 100644
index 000000000..65a24f5e1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/GetSneakTileZ.js
@@ -0,0 +1,10 @@
+var GetSneakTileZ = function (moveTo, tileX, tileY, tileZ) {
+ var board = moveTo.chessData.board;
+ var sneakTileZ = tileZ.toString();
+ do {
+ sneakTileZ += '.';
+ } while (board.contains(tileX, tileY, sneakTileZ))
+ return sneakTileZ;
+}
+
+export default GetSneakTileZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Methods.js
new file mode 100644
index 000000000..0a8608b40
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/Methods.js
@@ -0,0 +1,15 @@
+import CanMoveToTile from './CanMoveToTile.js';
+import MoveToTile from './MoveToTile.js';
+import MoveToward from './MoveToward.js';
+import MoveToRandomNeighbor from './MoveToRandomNeighbor.js';
+import MoveAway from './MoveAway.js';
+import MoveCloser from './MoveCloser.js';
+
+export default {
+ canMoveTo: CanMoveToTile,
+ moveTo: MoveToTile,
+ moveToward: MoveToward,
+ moveToRandomNeighbor: MoveToRandomNeighbor,
+ moveAway: MoveAway,
+ moveCloser: MoveCloser,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveAway.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveAway.js
new file mode 100644
index 000000000..7ff45d237
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveAway.js
@@ -0,0 +1,110 @@
+var MoveAway = function (tileX, tileY, moveAwayMode) {
+ var board = this.chessData.board;
+ if (board === null) { // chess is not in a board
+ this.lastMoveResult = false;
+ return this;
+ }
+
+ if (typeof (tileX) !== 'number') {
+ var config = tileX;
+ tileX = config.x;
+ tileY = config.y;
+ }
+ targetTileXY.x = tileX;
+ targetTileXY.y = tileY;
+ if (moveAwayMode === undefined) {
+ moveAwayMode = true;
+ }
+
+ var myTileXYZ = this.chessData.tileXYZ;
+ var directions = board.grid.allDirections;
+
+ // Get tileXY and distance of each neighbor, and current tile position
+ for (var i = 0, cnt = directions.length + 1; i < cnt; i++) {
+ var chessInfo = globChessInfo[i];
+ if (!chessInfo) {
+ chessInfo = {};
+ globChessInfo.push(chessInfo);
+ }
+
+ if (i < (cnt - 1)) {
+ // Neighbors
+ var out = board.getNeighborTileXY(myTileXYZ, i, chessInfo);
+ if (out === null) { // Invalid neighbor tile position
+ chessInfo.x = undefined;
+ chessInfo.y = undefined;
+ chessInfo.distance = undefined;
+ } else {
+ chessInfo.distance = board.getDistance(chessInfo, targetTileXY, true);
+ }
+ } else {
+ // Current tile
+ chessInfo.direction = undefined;
+ chessInfo.x = myTileXYZ.x;
+ chessInfo.y = myTileXYZ.y;
+ chessInfo.distance = board.getDistance(chessInfo, targetTileXY, true);
+ }
+
+ }
+ globChessInfo.length = directions.length + 1;
+
+ // Sort chess info
+ var previousDirection = this.destinationDirection;
+ globChessInfo.sort(function (infoA, infoB) {
+ var distanceA = infoA.distance,
+ distanceB = infoB.distance;
+ // Invalid tile position
+ if (distanceA === undefined) {
+ return 1;
+ }
+ if (distanceB === undefined) {
+ return -1;
+ }
+
+ if (distanceA > distanceB) {
+ return (moveAwayMode) ? -1 : 1;
+ }
+ if (distanceA < distanceB) {
+ return (moveAwayMode) ? 1 : -1;
+ }
+
+ // Equal-to case
+ var directionA = infoA.direction,
+ directionB = infoB.direction;
+ // Diagonal
+ if (directionA === previousDirection) {
+ return 1;
+ }
+ if (directionB === previousDirection) {
+ return -1;
+ }
+ // Current tile position
+ if (directionA === undefined) {
+ return 1;
+ }
+ if (directionB === undefined) {
+ return -1;
+ }
+ return 0;
+ });
+
+ // Try move to neighbor, or current tile position
+ for (var i = 0, cnt = globChessInfo.length; i < cnt; i++) {
+ chessInfo = globChessInfo[i];
+ if (chessInfo.distance === null) { // Invalid tile position
+ return this;
+ }
+ this.moveTo(chessInfo);
+ if (this.lastMoveResult) {
+ return this;
+ }
+ }
+ return this;
+}
+
+var targetTileXY = {
+ x: 0,
+ y: 0
+}
+var globChessInfo = [];
+export default MoveAway;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveCloser.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveCloser.js
new file mode 100644
index 000000000..1f40c7c17
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveCloser.js
@@ -0,0 +1,5 @@
+var MoveCloser = function(tileX, tileY) {
+ this.moveAway(tileX, tileY, false);
+ return this;
+}
+export default MoveCloser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.d.ts
new file mode 100644
index 000000000..00e573e81
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.d.ts
@@ -0,0 +1,94 @@
+import TickTask from '../../utils/componentbase/TickTask';
+import { TileXYType, TileXYZType } from '../types/Position';
+import Board from '../board/Board';
+
+
+export default MoveTo;
+
+declare namespace MoveTo {
+
+ type MoveableTestCallbackType = (
+ fromTileXYZ: TileXYZType,
+ toTileXYZ: TileXYZType,
+ direction: number,
+ board: Board
+ ) => boolean;
+
+ interface IConfig {
+ speed?: number,
+ rotateToTarget?: boolean,
+
+ occupiedTest?: boolean,
+ blockerTest?: boolean,
+ moveableTest?: MoveableTestCallbackType,
+ moveableTestScope?: object,
+
+ sneak?: boolean,
+ }
+
+ namespace Events {
+ type CompleteCallbackType = (
+ gameObject: Phaser.GameObjects.GameObject,
+ moveTo: MoveTo
+ ) => void;
+
+ type OccupyCallbackType = (
+ occupiedGameObject: Phaser.GameObjects.GameObject,
+ gameObject: Phaser.GameObjects.GameObject,
+ moveTo: MoveTo
+ ) => void;
+ }
+}
+
+declare class MoveTo extends TickTask {
+ constructor(
+ gameObject: ChessType,
+ config?: MoveTo.IConfig
+ );
+
+ moveTo(tileX: number, tileY: number): this;
+ moveTo(tileXY: TileXYType): this;
+
+ moveToward(direction: number): this;
+
+ moveToRandomNeighbor(): this;
+
+ moveAway(tileX: number, tileY: number): this;
+ moveAway(tileXY: TileXYType): this;
+
+ moveCloser(tileX: number, tileY: number): this;
+ moveCloser(tileXY: TileXYType): this;
+
+ canMoveTo(tileX: number, tileY: number): boolean;
+
+ readonly lastMoveResult: boolean;
+
+ readonly destinationTileX: number;
+ readonly destinationTileY: number;
+ readonly destinationDirection: number;
+
+ setEnable(enable?: boolean): this;
+ enable: boolean;
+
+ setSpeed(speed: number): this;
+ speed: number;
+
+ timeScale: number;
+
+ setRotateToTarget(enable?: boolean): this;
+ rotateToTarget: boolean;
+
+ setSneakEnable(enable?: boolean): this;
+
+ setOccupiedTest(enable?: boolean): this;
+ occupiedTest: boolean;
+ setBlockerTest(enable?: boolean): this;
+ blockerTest: boolean;
+ setEdgeBlockerTest(enable?: boolean): this;
+ edgeBlockerTest: boolean;
+
+ setMoveableTestCallback(
+ callback: MoveTo.MoveableTestCallbackType,
+ scope?: object
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.js
new file mode 100644
index 000000000..830a520f3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveTo.js
@@ -0,0 +1,214 @@
+import TickTask from '../../utils/componentbase/SceneUpdateTickTask.js';
+import Methods from './Methods.js';
+import MoveToTask from '../../behaviors/moveto/MoveTo.js';
+import GetChessData from '../chess/GetChessData.js';
+import GetValue from '../../utils/object/GetValue.js';
+
+class MoveTo extends TickTask {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.chessData = GetChessData(gameObject);
+ this.scene = gameObject.scene;
+ this.moveToTask = new MoveToTask(gameObject, { tickingMode: 0 });
+
+ this.resetFromJSON(config);
+ this.boot();
+ }
+
+ resetFromJSON(o) {
+ this.isRunning = GetValue(o, 'isRunning', false);
+ this.setEnable(GetValue(o, 'enable', true));
+ this.timeScale = GetValue(o, 'timeScale', 1);
+ this.setSpeed(GetValue(o, 'speed', 400));
+ this.setRotateToTarget(GetValue(o, 'rotateToTarget', false));
+ this.setOccupiedTest(GetValue(o, 'occupiedTest', false));
+ this.setBlockerTest(GetValue(o, 'blockerTest', false));
+ this.setEdgeBlockerTest(GetValue(o, 'edgeBlockerTest', false));
+ this.setMoveableTestCallback(GetValue(o, 'moveableTest', undefined), GetValue(o, 'moveableTestScope', undefined));
+ this.setSneakEnable(GetValue(o, 'sneak', false));
+ this.destinationTileX = GetValue(o, 'destinationTileX', null);
+ this.destinationTileY = GetValue(o, 'destinationTileY', null);
+ this.destinationDirection = GetValue(o, 'destinationDirection', null);
+ this.lastMoveResult = GetValue(o, 'lastMoveResult', undefined);
+ return this;
+ }
+
+ toJSON() {
+ return {
+ isRunning: this.isRunning,
+ enable: this.enable,
+ timeScale: this.timeScale,
+ speed: this.speed,
+ occupiedTest: this.occupiedTest,
+ blockerTest: this.blockerTest,
+ edgeBlockerTest: this.edgeBlockerTest,
+ moveableTest: this.moveableTestCallback,
+ moveableTestScope: this.moveableTestScope,
+ rotateToTarget: this.rotateToTarget,
+ destinationTileX: this.destinationTileX,
+ destinationTileY: this.destinationTileY,
+ destinationDirection: this.destinationDirection,
+ lastMoveResult: this.lastMoveResult,
+ tickingMode: this.tickingMode
+ };
+ }
+
+ shutdown(fromScene) {
+ this.moveToTask.shutdown(fromScene);
+ super.shutdown(fromScene);
+ }
+
+ set enable(value) {
+ this.moveToTask.setEnable(value);
+ }
+
+ get enable() {
+ return this.moveToTask.enable;
+ }
+
+ setEnable(e) {
+ if (e == undefined) {
+ e = true;
+ }
+ this.enable = e;
+ return this;
+ }
+
+ get timeScale() {
+ return this.moveToTask.timeScale;
+ }
+
+ set timeScale(value) {
+ this.moveToTask.timeScale = value;
+ }
+
+ set speed(value) {
+ this.moveToTask.setSpeed(value);
+ }
+
+ get speed() {
+ return this.moveToTask.speed;
+ }
+
+ setSpeed(speed) {
+ this.speed = speed;
+ return this;
+ }
+
+ set rotateToTarget(value) {
+ this.moveToTask.setRotateToTarget(value);
+ }
+
+ get rotateToTarget() {
+ return this.moveToTask.rotateToTarget;
+ }
+
+ setRotateToTarget(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.rotateToTarget = enable;
+ return this;
+ }
+
+ setOccupiedTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.occupiedTest = enable;
+ return this;
+ }
+
+ setBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.blockerTest = enable;
+ return this;
+ }
+
+ setEdgeBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.edgeBlockerTest = enable;
+ return this;
+ }
+
+ setMoveableTestCallback(callback, scope) {
+ this.moveableTestCallback = callback;
+ this.moveableTestScope = scope;
+ return this;
+ }
+
+ setSneakEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ this.sneakMode = enable;
+ this.tileZSave = undefined;
+ return this;
+ }
+
+ moveAlongLine(startX, startY, endX, endY) {
+ if (startX !== undefined) {
+ this.parent.x = startX;
+ }
+ if (startY !== undefined) {
+ this.parent.y = startY;
+ }
+ this.moveToTask.moveTo(endX, endY);
+ return this;
+ };
+
+ addMoveLine(startX, startY, endX, endY) {
+ if (!this.moveToTask.hasOwnProperty('nextlines')) {
+ this.moveToTask.nextlines = [];
+ }
+ this.moveToTask.nextlines.push(
+ [startX, startY, endX, endY]
+ );
+ return this;
+ };
+
+ moveNextLine() {
+ var nextlines = this.moveToTask.nextlines;
+ if (!nextlines) {
+ return false;
+ }
+ if (nextlines.length === 0) {
+ return false;
+ }
+ // has next line
+ this.moveAlongLine.apply(this, nextlines[0]);
+ nextlines.length = 0;
+ return true;
+ }
+
+ update(time, delta) {
+ if ((!this.isRunning) || (!this.enable)) {
+ return this;
+ }
+
+ var moveToTask = this.moveToTask;
+ moveToTask.update(time, delta);
+ if (!moveToTask.isRunning) {
+ if (!this.moveNextLine()) {
+ this.complete();
+ }
+ return this;
+ }
+ return this;
+ }
+}
+
+Object.assign(
+ MoveTo.prototype,
+ Methods
+);
+
+
+export default MoveTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToRandomNeighbor.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToRandomNeighbor.js
new file mode 100644
index 000000000..ba5c76a57
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToRandomNeighbor.js
@@ -0,0 +1,26 @@
+import Clone from '../../utils/object/Clone.js';
+import Shuffle from '../../utils/array/Shuffle.js';
+
+var MoveToRandomNeighbor = function () {
+ var board = this.chessData.board;
+ if (board === null) { // chess is not in a board
+ this.lastMoveResult = false;
+ return this;
+ }
+
+ var directions = board.grid.allDirections;
+ if (globDirections.length !== directions.length) {
+ Clone(directions, globDirections);
+ }
+ Shuffle(globDirections);
+ for (var i = 0, cnt = globDirections.length; i < cnt; i++) {
+ this.moveToward(globDirections[i]);
+ if (this.lastMoveResult) {
+ return this;
+ }
+ }
+ return this;
+}
+
+var globDirections = [];
+export default MoveToRandomNeighbor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToTile.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToTile.js
new file mode 100644
index 000000000..dc80cffcf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToTile.js
@@ -0,0 +1,115 @@
+import GetValue from '../../utils/object/GetValue.js';
+import GetSneakTileZ from './GetSneakTileZ.js';
+
+var MoveToTile = function (tileX, tileY, direction) {
+ var board = this.chessData.board;
+ if (board === null) { // chess is not in a board
+ this.lastMoveResult = false;
+ return this;
+ }
+
+ if ((tileX != null) && (typeof (tileX) !== 'number')) {
+ var config = tileX;
+ tileX = GetValue(config, 'x', undefined);
+ tileY = GetValue(config, 'y', undefined);
+ direction = GetValue(config, 'direction', undefined);
+ }
+ var myTileXYZ = this.chessData.tileXYZ;
+ if ((direction !== undefined) &&
+ (tileX == null) || (tileY == null)) {
+ // Get neighbor tile position if direction is not undefined
+ var targetTileXY = board.getNeighborTileXY(myTileXYZ, direction, true);
+ if (targetTileXY !== null) {
+ tileX = targetTileXY.x;
+ tileY = targetTileXY.y;
+ } else {
+ tileX = null;
+ tileY = null;
+ }
+ }
+
+ // invalid tile position
+ if ((tileX == null) || (tileY == null)) {
+ this.lastMoveResult = false;
+ return this;
+ }
+ if (direction === undefined) {
+ globTileXYZ.x = tileX;
+ globTileXYZ.y = tileY
+ direction = board.getNeighborTileDirection(myTileXYZ, globTileXYZ);
+ }
+ if (!this.canMoveTo(tileX, tileY, direction)) {
+ this.lastMoveResult = false;
+ return this;
+ }
+ this.destinationTileX = tileX;
+ this.destinationTileY = tileY;
+ this.destinationDirection = direction;
+
+ if (board.wrapMode && (direction !== null)) {
+ board.grid.getNeighborTileXY(myTileXYZ.x, myTileXYZ.y, direction, neighborTileXY);
+ // wrap mode && neighbor
+ if ((neighborTileXY.x === tileX) && (neighborTileXY.y === tileY)) {
+ // not a wrapped neighbor
+ var out = board.tileXYToWorldXY(tileX, tileY, true);
+ this.moveAlongLine(undefined, undefined, out.x, out.y);
+ } else {
+ // wrapped neighbor
+ // line 0
+ var out = board.tileXYToWorldXY(neighborTileXY.x, neighborTileXY.y, true);
+ var originNeighborWorldX = out.x;
+ var originNeighborWorldY = out.y;
+ out = board.tileXYToWorldXY(myTileXYZ.x, myTileXYZ.y, true);
+ var startX = out.x;
+ var startY = out.y;
+ var endX = (startX + originNeighborWorldX) / 2;
+ var endY = (startY + originNeighborWorldY) / 2;
+ this.moveAlongLine(undefined, undefined, endX, endY);
+ // line 1
+ var oppositeDirection = board.getOppositeDirection(tileX, tileY, direction);
+ board.grid.getNeighborTileXY(tileX, tileY, oppositeDirection, neighborTileXY);
+ out = board.tileXYToWorldXY(neighborTileXY.x, neighborTileXY.y, true);
+ originNeighborWorldX = out.x;
+ originNeighborWorldY = out.y;
+ out = board.tileXYToWorldXY(tileX, tileY, true);
+ endX = out.x;
+ endY = out.y;
+ startX = (originNeighborWorldX + endX) / 2;
+ startY = (originNeighborWorldY + endY) / 2;
+ this.addMoveLine(startX, startY, endX, endY);
+ }
+ } else {
+ var out = board.tileXYToWorldXY(tileX, tileY, true);
+ this.moveAlongLine(undefined, undefined, out.x, out.y);
+ }
+
+ var tileZ = myTileXYZ.z;
+ if (this.sneakMode) {
+ if (this.tileZSave === undefined) {
+ if (board.contains(tileX, tileY, tileZ)) {
+ // Sneak
+ this.tileZSave = tileZ;
+ tileZ = GetSneakTileZ(this, tileX, tileY, this.tileZSave);
+ }
+ } else {
+ if (board.contains(tileX, tileY, this.tileZSave)) {
+ // Sneak
+ tileZ = GetSneakTileZ(this, tileX, tileY, this.tileZSave);
+ } else {
+ // Go back
+ tileZ = this.tileZSave;
+ this.tileZSave = undefined;
+ }
+ }
+ }
+ board.moveChess(this.parent, tileX, tileY, tileZ, false);
+
+ this.isRunning = true;
+ this.lastMoveResult = true;
+ return this;
+}
+
+var globTileXYZ = {};
+var neighborTileXY = {};
+
+export default MoveToTile;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToward.js b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToward.js
new file mode 100644
index 000000000..aadd49649
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/moveto/MoveToward.js
@@ -0,0 +1,5 @@
+var MoveToward = function (direction) {
+ this.moveTo(undefined, undefined, direction);
+ return this;
+}
+export default MoveToward;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.d.ts
new file mode 100644
index 000000000..73eac1b4f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.d.ts
@@ -0,0 +1,10 @@
+import PathFinder from './PathFinder';
+
+export default function (
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: PathFinder.IConfig
+): PathFinder;
+
+export default function(
+ config?: PathFinder.IConfig
+): PathFinder;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.js
new file mode 100644
index 000000000..4bd801766
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Factory.js
@@ -0,0 +1,11 @@
+import PathFinder from './PathFinder.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('pathFinder', function (gameObject, config) {
+ return new PathFinder(gameObject, config);
+});
+
+SetValue(window, 'RexPlugins.Board.PathFinder', PathFinder);
+
+export default PathFinder;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindArea.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindArea.js
new file mode 100644
index 000000000..952eb36d8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindArea.js
@@ -0,0 +1,54 @@
+import CONST from './const.js';
+
+const AREA_MODE = CONST.AREA_MODE;
+const INFINITY = CONST.INFINITY; // undefined
+
+var FindArea = function (movingPoints, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ if (this.board === null) { // chess is not in board
+ return out;
+ }
+ if ((movingPoints !== INFINITY) && (movingPoints <= 0)) {
+ return out;
+ }
+
+ var startTileXYZ = this.chessData.tileXYZ,
+ startTileX = startTileXYZ.x,
+ startTileY = startTileXYZ.y;
+ this.aStarSearch(startTileXYZ, null, movingPoints, AREA_MODE);
+ // output : this.nodeManager.getAllNodes()
+ var nodes = this.nodeManager.getAllNodes(),
+ node, nodesList = [];
+ for (var key in nodes) {
+ node = nodes[key];
+ // not include start node
+ if ((node.x === startTileX) && (node.y === startTileY)) {
+ continue;
+ }
+ // not include open node
+ if (!node.closed) {
+ continue;
+ }
+ nodesList.push(node);
+ }
+ // sort by sn (creating order)
+ nodesList.sort(function (nodeA, nodeB) {
+ var snA = nodeA.sn;
+ var snB = nodeB.sn;
+ return (snA > snB) ? 1 :
+ (snA < snB) ? -1 :
+ 0;
+ });
+ for (var i = 0, cnt = nodesList.length; i < cnt; i++) {
+ node = nodesList[i];
+ out.push({
+ x: node.x,
+ y: node.y,
+ cost: node.g
+ });
+ }
+ return out;
+}
+export default FindArea;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindPath.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindPath.js
new file mode 100644
index 000000000..b07fbe3b0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/FindPath.js
@@ -0,0 +1,29 @@
+import CONST from './const.js';
+
+const PATH_MODE = CONST.PATH_MODE;
+const INFINITY = CONST.INFINITY; // undefined
+
+var FindPath = function (endTileXY, movingPoints, isClosest, out) {
+ if (isClosest === undefined) {
+ isClosest = true;
+ }
+ if (out === undefined) {
+ out = [];
+ }
+ if (this.board === null) { // chess is not in board
+ return out;
+ }
+ if ((movingPoints !== INFINITY) && (movingPoints <= 0)) {
+ return out;
+ }
+
+ var startTileXYZ = this.chessData.tileXYZ;
+ this.aStarSearch(startTileXYZ, endTileXY, movingPoints, PATH_MODE);
+ var nodeManager = this.nodeManager;
+ var endNode = (isClosest) ? nodeManager.closestNode : nodeManager.getNode(endTileXY);
+ if (endNode === null) {
+ return out;
+ }
+ return this.getPath(endNode, out);
+}
+export default FindPath;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetCost.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetCost.js
new file mode 100644
index 000000000..f4c6a7079
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetCost.js
@@ -0,0 +1,38 @@
+import CONST from './const.js';
+
+const BLOCKER = CONST.BLOCKER;
+
+var GetCost = function (curNode, preNode) {
+ // Occupied test
+ if (this.occupiedTest) {
+ if (this.board.contains(curNode.x, curNode.y, this.chessData.tileXYZ.z)) {
+ return BLOCKER;
+ }
+ }
+ // Blocker test
+ if (this.blockerTest) {
+ if (this.board.hasBlocker(curNode.x, curNode.y)) {
+ return BLOCKER;
+ }
+ }
+ // Edge-blocker test
+ if (this.edgeBlockerTest) {
+ // TODO
+ }
+
+ if (typeof (this.costCallback) === 'number') {
+ return this.costCallback;
+ } else {
+ var cost;
+ if (this.costCallbackScope) {
+ cost = this.costCallback.call(this.costCallbackScope, curNode, preNode, this);
+ } else {
+ cost = this.costCallback(curNode, preNode, this);
+ }
+ if (cost === undefined) {
+ cost = BLOCKER;
+ }
+ return cost;
+ }
+}
+export default GetCost;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetPath.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetPath.js
new file mode 100644
index 000000000..b0dedd297
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/GetPath.js
@@ -0,0 +1,30 @@
+import GetNodePath from './astartsearch/GetNodePath.js';
+var GetPath = function (endTileXY, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ if (this.board === undefined) {
+ return out;
+ }
+ var nodeManager = this.nodeManager;
+ if (nodeManager === undefined) {
+ return out;
+ }
+ var startNode = nodeManager.getNode(this.chessData.tileXYZ, false);
+ var endNode = nodeManager.getNode(endTileXY, false);
+ if ((startNode === null) || (endNode === null)) {
+ return out;
+ }
+ var nodes = GetNodePath(startNode, endNode, this.pathMode);
+ var node;
+ for (var i = 0, cnt = nodes.length; i < cnt; i++) {
+ node = nodes[i];
+ out.push({
+ x: node.x,
+ y: node.y,
+ cost: node.g
+ });
+ }
+ return out;
+}
+export default GetPath;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Methods.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Methods.js
new file mode 100644
index 000000000..38ee9db00
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/Methods.js
@@ -0,0 +1,16 @@
+
+import AStarSearch from './astartsearch/AStarSearch.js';
+import GetCost from './GetCost.js';
+import FindArea from './FindArea.js';
+import GetPath from './GetPath.js';
+import FindPath from './FindPath.js';
+import TileXYToCost from './TileXYToCost.js';
+
+export default {
+ aStarSearch: AStarSearch,
+ getCost: GetCost,
+ findArea: FindArea,
+ getPath: GetPath,
+ findPath: FindPath,
+ tileXYToCost: TileXYToCost,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.d.ts
new file mode 100644
index 000000000..1bcc8d974
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.d.ts
@@ -0,0 +1,93 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase';
+import { TileXYType } from '../types/Position';
+import Board from '../board/Board';
+
+export default PathFinder;
+
+declare namespace PathFinder {
+
+ type PathModeTypes = 'random' | 'diagonal' | 'straight' | 'line' |
+ 'A*' | 'A*-random' | 'A*-line' |
+ 0 | 1 | 2 | 3 |
+ 10 | 11 | 12;
+
+ type NodeType = {
+ x: number, y: number,
+ pathCost: number,
+ preNodes: NodeType[]
+ }
+
+ type BLOCKER = null;
+ type INFINITY = undefined;
+
+ type CostCallbackType = (
+ curTile: NodeType, preTile: NodeType,
+ pathFinder: PathFinder
+ )
+ => number | BLOCKER | INFINITY;
+
+ interface IConfig {
+ occupiedTest?: boolean,
+ blockerTest?: boolean,
+
+ cost?: number,
+ costCallback?: CostCallbackType,
+ costCallbackScope?: object,
+ cacheCost?: boolean,
+
+ pathMode?: PathModeTypes,
+ weight?: number,
+ shuffleNeighbors?: boolean,
+ }
+
+}
+
+declare class PathFinder extends ComponentBase {
+ constructor(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: PathFinder.IConfig
+ );
+
+ constructor(
+ config?: PathFinder.IConfig
+ );
+
+ setChess(gameObject: Phaser.GameObjects.GameObject): this;
+ readonly gameObject: Phaser.GameObjects.GameObject;
+ readonly board: Board;
+
+ setCostFunction(cost: number): this;
+ setCostFunction(
+ callback: PathFinder.CostCallbackType,
+ scope?: object
+ ): this;
+
+ setPathMode(
+ pathMode: PathFinder.PathModeTypes
+ ): this;
+
+ findArea(
+ movingPoints?: number | PathFinder.INFINITY,
+ out?: PathFinder.NodeType[]
+ ): PathFinder.NodeType[];
+
+ getPath(
+ endTileXY: TileXYType
+ ): PathFinder.NodeType[];
+
+ findPath(
+ endTileXY: TileXYType,
+ movingPoints?: number | PathFinder.INFINITY,
+ isClosest?: boolean,
+ out?: PathFinder.NodeType[]
+ ): PathFinder.NodeType[];
+
+ tileXYToCost(
+ tileX: number,
+ tileY: number,
+ pathCost?: boolean
+ ): number;
+
+ readonly BLOCKER: PathFinder.BLOCKER;
+ readonly INFINITY: PathFinder.INFINITY;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.js
new file mode 100644
index 000000000..ae9433f14
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/PathFinder.js
@@ -0,0 +1,167 @@
+import ComponentBase from '../../utils/componentbase/ComponentBase.js';
+import GetChessData from '../chess/GetChessData.js';
+import Methods from './Methods.js';
+import CONST from './const.js';
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+import GetValue from '../../utils/object/GetValue.js';
+
+const BLOCKER = CONST.BLOCKER;
+const INFINITY = CONST.INFINITY;
+
+class PathFinder extends ComponentBase {
+ constructor(gameObject, config) {
+ if (IsPlainObject(gameObject)) {
+ config = gameObject;
+ gameObject = undefined;
+ }
+ super(gameObject, { eventEmitter: false });
+
+ this.setChess(gameObject);
+ this.nodeManager = undefined;
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ var costCallback = GetValue(o, 'costCallback', undefined);
+ var costCallbackScope = GetValue(o, 'costCallbackScope', undefined);
+ if (costCallback === undefined) {
+ costCallback = GetValue(o, 'cost', 1);
+ }
+ this.setOccupiedTest(GetValue(o, 'occupiedTest', false));
+ this.setBlockerTest(GetValue(o, 'blockerTest', false));
+ this.setEdgeBlockerTest(GetValue(o, 'edgeBlockerTest', false));
+ this.setCostFunction(costCallback, costCallbackScope);
+ this.setPathMode(GetValue(o, 'pathMode', 0));
+ this.setCacheCostMode(GetValue(o, 'cacheCost', true));
+ this.setWeight(GetValue(o, 'weight', 10));
+ this.setShuffleNeighborsMode(GetValue(o, 'shuffleNeighbors', false));
+ return this;
+ }
+
+ shutdown(fromScene) {
+ // Already shutdown
+ if (this.isShutdown) {
+ return;
+ }
+
+ if (this.nodeManager !== undefined) {
+ this.nodeManager.destroy();
+ }
+ this.chessData = undefined;
+
+ super.shutdown(fromScene);
+ }
+
+ get gameObject() {
+ return this.parent;
+ }
+
+ setChess(gameObject) {
+ if (gameObject) {
+ this.chessData = GetChessData(gameObject);
+ if (this.parent !== gameObject) {
+ // Remove attatched event from previous gameObject
+ if (this.parent && this.parent.once) {
+ this.parent.off('destroy', this.onParentDestroy, this);
+ }
+ // Attach event
+ this.setParent(gameObject);
+ if (this.parent && this.parent.once) {
+ this.parent.once('destroy', this.onParentDestroy, this);
+ }
+ }
+ } else {
+ this.setParent();
+ this.chessData = undefined;
+ }
+ return this;
+ }
+
+ setCostFunction(callback, scope) {
+ this.costCallback = callback;
+ this.costCallbackScope = scope;
+ return this;
+ }
+
+ setPathMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = CONST[mode];
+ }
+ this.pathMode = mode;
+ return this;
+ }
+
+ setCacheCostMode(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.cacheCost = value;
+ return this;
+ }
+
+ setOccupiedTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.occupiedTest = enable;
+ return this;
+ }
+
+ setBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.blockerTest = enable;
+ return this;
+ }
+
+ setEdgeBlockerTest(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.edgeBlockerTest = enable;
+ return this;
+ }
+
+ setWeight(value) {
+ this.weight = value;
+ return this;
+ }
+
+ setShuffleNeighborsMode(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.shuffleNeighbors = value;
+ return this;
+ }
+
+ get BLOCKER() {
+ return BLOCKER;
+ }
+
+ get INFINITY() {
+ return INFINITY;
+ }
+
+ get board() {
+ return this.chessData.board;
+ }
+}
+
+Object.assign(
+ PathFinder.prototype,
+ Methods
+);
+
+const PATHMODE = {
+ 'random': 0,
+ 'diagonal': 1,
+ 'straight': 2,
+ 'A*': 3,
+ 'line': 4,
+ 'A*-line': 5,
+ 'A*-random': 6
+}
+
+export default PathFinder;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/TileXYToCost.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/TileXYToCost.js
new file mode 100644
index 000000000..502554d1e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/TileXYToCost.js
@@ -0,0 +1,14 @@
+var TileXYToCost = function (tileX, tileY, pathCost) {
+ if (this.nodeManager === undefined) {
+ return null;
+ }
+ var node = this.nodeManager.getNode(tileX, tileY);
+ if (node === null) {
+ return null;
+ }
+ if (pathCost === undefined) {
+ pathCost = true;
+ }
+ return (pathCost)? node.g:node.cost;
+}
+export default TileXYToCost;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/AStarSearch.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/AStarSearch.js
new file mode 100644
index 000000000..c7ad6af52
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/AStarSearch.js
@@ -0,0 +1,152 @@
+/*
+
+javascript-astar 0.3.0
+http://github.com/bgrins/javascript-astar
+Freely distributable under the MIT License.
+Implements the astar search algorithm in javascript using a Binary Heap.
+Includes Binary Heap (with modifications) from Marijn Haverbeke.
+http://eloquentjavascript.net/appendix2.html
+
+*/
+
+import NodeManager from './NodeManager.js';
+import BinaryHeap from './BinaryHeap.js';
+import CONST from '../const.js';
+
+const AREA_MODE = CONST.AREA_MODE;
+const PATH_MODE = CONST.PATH_MODE;
+
+const ASTAR = CONST['A*'];
+const ASTAR_LINE = CONST['A*-line'];
+const ASTAR_RANDOM = CONST['A*-random'];
+
+const BLOCKER = CONST.BLOCKER;
+const INFINITY = CONST.INFINITY;
+
+// global object
+var gOpenHeap = new BinaryHeap(function (node) {
+ return node.f;
+});
+// global object
+
+var AStarSerach = function (startTileXYZ, endTileXY, movingPoints, mode) {
+ if (this.nodeManager === undefined) {
+ this.nodeManager = new NodeManager(this);
+ }
+ var nodeManager = this.nodeManager;
+ nodeManager.freeAllNodes();
+
+ // const isAreaSearch = (mode === AREA_MODE);
+ const isPathSearch = (mode === PATH_MODE);
+ const isAStarMode = (this.pathMode === ASTAR) || (this.pathMode === ASTAR_LINE) || (this.pathMode === ASTAR_RANDOM);
+ const astarHeuristicEnable = isPathSearch && isAStarMode;
+ const shortestPathEnable = isPathSearch && (!isAStarMode);
+ const astarHeuristicMode =
+ (!astarHeuristicEnable) ? null :
+ (this.pathMode == ASTAR) ? 0 :
+ (this.pathMode == ASTAR_LINE) ? 1 :
+ (this.pathMode == ASTAR_RANDOM) ? 2 :
+ null;
+
+ var end = (endTileXY !== null) ? nodeManager.getNode(endTileXY.x, endTileXY.y, true) : null;
+ var start = nodeManager.getNode(startTileXYZ.x, startTileXYZ.y, true);
+ start.h = start.heuristic(end, astarHeuristicMode);
+
+ // NEAREST NODE
+ var closestNode;
+ if (isPathSearch) {
+ closestNode = start;
+ closestNode.closerH = closestNode.h || closestNode.heuristic(end, 0);
+ }
+ // NEAREST NODE
+
+ gOpenHeap.push(start);
+ while (gOpenHeap.size() > 0) {
+ // Grab the lowest f(x) to process next. Heap keeps this sorted for us.
+ var curNode = gOpenHeap.pop();
+
+ // End case -- result has been found, return the traced path.
+ if (isPathSearch && (curNode === end)) {
+ closestNode = end;
+ break;
+ }
+
+ // Normal case -- move curNode from open to closed, process each of its neighbors.
+ curNode.closed = true;
+
+ // Find all neighbors for the current node.
+ var neighbors = curNode.getNeighborNodes();
+
+ var neighbor, neighborCost, isNeighborMoreCloser;
+ for (var i = 0, cnt = neighbors.length; i < cnt; ++i) {
+ neighbor = neighbors[i];
+ neighborCost = neighbor.getCost(curNode);
+ if (neighbor.closed || (neighborCost === BLOCKER)) {
+ // Not a valid node to process, skip to next neighbor.
+ //log("("+neighbor.x+","+neighbor.y+") is closed");
+ continue;
+ }
+
+ // The g score is the shortest distance from start to current node.
+ // We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
+ var gScore = curNode.g + neighborCost,
+ beenVisited = neighbor.visited;
+
+ //log("("+curNode.x+","+curNode.y+") -> ("+neighbor.x+","+neighbor.y+")="+neighborCost+" ,acc="+gScore);
+ if ((movingPoints != INFINITY) && (gScore > movingPoints)) {
+ //log("("+neighbor.x+","+neighbor.y+") out of range");
+ continue;
+ }
+
+ if ((!beenVisited) || (gScore < neighbor.g)) {
+
+ // Found an optimal (so far) path to this node. Take score for node to see how good it is.
+ neighbor.visited = true;
+ neighbor.preNodes.length = 0;
+ neighbor.preNodes.push(curNode);
+ neighbor.h = neighbor.h || neighbor.heuristic(end, astarHeuristicMode, start);
+ neighbor.g = gScore;
+ neighbor.f = neighbor.g + neighbor.h;
+
+ // NEAREST NODE
+ if (isPathSearch) {
+ neighbor.closerH = neighbor.h || neighbor.heuristic(end, 0);
+ isNeighborMoreCloser = (neighbor.closerH < closestNode.closerH) ||
+ ((neighbor.closerH === closestNode.closerH) && (neighbor.g < closestNode.g));
+
+ if (isNeighborMoreCloser) {
+ closestNode = neighbor;
+ }
+ }
+ // NEAREST NODE
+
+ if (!beenVisited) {
+ // Pushing to heap will put it in proper place based on the 'f' value.
+ gOpenHeap.push(neighbor);
+ //log("push ("+neighbor.x+","+neighbor.y+") ")
+ } else {
+ // Already seen the node, but since it has been rescored we need to reorder it in the heap
+ gOpenHeap.rescoreElement(neighbor);
+ //log("reorder ("+neighbor.x+","+neighbor.y+") ")
+ }
+ } else if (shortestPathEnable && (gScore == neighbor.g)) {
+ neighbor.preNodes.push(curNode);
+
+ //if (neighbor.preNodes.indexOf(curNode) == -1)
+ // neighbor.preNodes.push(curNode);
+ //else
+ // debugger;
+
+ //log("drop ("+neighbor.x+","+neighbor.y+") ")
+ } else {
+ //log("drop ("+neighbor.x+","+neighbor.y+") ")
+ }
+ }
+
+ }
+
+ nodeManager.closestNode = (isPathSearch) ? closestNode : null;
+ gOpenHeap.clear();
+ return this;
+}
+export default AStarSerach;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/BinaryHeap.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/BinaryHeap.js
new file mode 100644
index 000000000..2cf51e11b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/BinaryHeap.js
@@ -0,0 +1,130 @@
+class BinaryHeap {
+ constructor(scoreFunction) {
+ this.content = [];
+ this.scoreFunction = scoreFunction;
+ }
+
+ clear() {
+ this.content.length = 0;
+ }
+
+ push(element) {
+ // Add the new element to the end of the array.
+ this.content.push(element);
+
+ // Allow it to sink down.
+ this.sinkDown(this.content.length - 1);
+ }
+
+ pop() {
+ // Store the first element so we can return it later.
+ var result = this.content[0];
+ // Get the element at the end of the array.
+ var end = this.content.pop();
+ // If there are any elements left, put the end element at the
+ // start, and let it bubble up.
+ if (this.content.length > 0) {
+ this.content[0] = end;
+ this.bubbleUp(0);
+ }
+ return result;
+ }
+
+ remove(node) {
+ var i = this.content.indexOf(node);
+
+ // When it is found, the process seen in 'pop' is repeated
+ // to fill up the hole.
+ var end = this.content.pop();
+
+ if (i !== this.content.length - 1) {
+ this.content[i] = end;
+
+ if (this.scoreFunction(end) < this.scoreFunction(node)) {
+ this.sinkDown(i);
+ } else {
+ this.bubbleUp(i);
+ }
+ }
+ }
+
+ size() {
+ return this.content.length;
+ }
+
+ rescoreElement(node) {
+ this.sinkDown(this.content.indexOf(node));
+ }
+
+ sinkDown(n) {
+ // Fetch the element that has to be sunk.
+ var element = this.content[n];
+
+ // When at 0, an element can not sink any further.
+ while (n > 0) {
+
+ // Compute the parent element's index, and fetch it.
+ var parentN = ((n + 1) >> 1) - 1,
+ parent = this.content[parentN];
+ // Swap the elements if the parent is greater.
+ if (this.scoreFunction(element) < this.scoreFunction(parent)) {
+ this.content[parentN] = element;
+ this.content[n] = parent;
+ // Update 'n' to continue at the new position.
+ n = parentN;
+ }
+ // Found a parent that is less, no need to sink any further.
+ else {
+ break;
+ }
+ }
+ }
+
+ bubbleUp(n) {
+ // Look up the target element and its score.
+ var length = this.content.length,
+ element = this.content[n],
+ elemScore = this.scoreFunction(element);
+
+ while (true) {
+ // Compute the indices of the child elements.
+ var child2N = (n + 1) << 1,
+ child1N = child2N - 1;
+ // This is used to store the new position of the element, if any.
+ var swap = null,
+ child1Score;
+ // If the first child exists (is inside the array)...
+ if (child1N < length) {
+ // Look it up and compute its score.
+ var child1 = this.content[child1N];
+ child1Score = this.scoreFunction(child1);
+
+ // If the score is less than our element's, we need to swap.
+ if (child1Score < elemScore) {
+ swap = child1N;
+ }
+ }
+
+ // Do the same checks for the other child.
+ if (child2N < length) {
+ var child2 = this.content[child2N],
+ child2Score = this.scoreFunction(child2);
+ if (child2Score < (swap === null ? elemScore : child1Score)) {
+ swap = child2N;
+ }
+ }
+
+ // If the element needs to be moved, swap it, and continue.
+ if (swap !== null) {
+ this.content[n] = this.content[swap];
+ this.content[swap] = element;
+ n = swap;
+ }
+ // Otherwise, we are done.
+ else {
+ break;
+ }
+ }
+ }
+}
+export default BinaryHeap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/GetNodePath.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/GetNodePath.js
new file mode 100644
index 000000000..6c9b04377
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/GetNodePath.js
@@ -0,0 +1,89 @@
+import CONST from '../const.js';
+import RandomInt from '../../../utils/math/Between.js';
+
+const RANDOM = CONST['random'];
+const DIAGONAL = CONST['diagonal'];
+const STRAIGN = CONST['straight'];
+const LINE = CONST['line'];
+const ASTAR = CONST['A*'];
+const ASTAR_LINE = CONST['A*-line'];
+const ASTAR_RANDOM = CONST['A*-random'];
+
+
+var GetNodePath = function (startNode, endNode, pathMode) {
+ var board = startNode.board;
+
+ var curDir, preNodeDir; // DIAGONAL, STRAIGN
+ var targetAngle; // LINE
+
+ var curNode = endNode,
+ preNode, preNodeKeysCnt;
+ var path = [];
+ while (curNode.preNodes.length > 0) {
+ path.push(curNode);
+ preNodeKeysCnt = curNode.preNodes.length;
+
+ switch (pathMode) {
+ case ASTAR:
+ case ASTAR_LINE:
+ case ASTAR_RANDOM:
+ preNode = curNode.preNodes[0];
+ curNode = preNode;
+ break;
+
+ case RANDOM:
+ preNode = (preNodeKeysCnt === 1) ? curNode.preNodes[0] : curNode.preNodes[RandomInt(0, preNodeKeysCnt - 1)];
+ curNode = preNode;
+ break;
+
+ case DIAGONAL:
+ for (var i = 0; i < preNodeKeysCnt; i++) {
+ preNode = curNode.preNodes[i];
+ preNodeDir = board.getNeighborTileDirection(curNode, preNode);
+ if (preNodeDir !== curDir) {
+ curDir = preNodeDir;
+ break;
+ }
+ }
+ curNode = preNode;
+ break;
+
+ case STRAIGN:
+ for (i = 0; i < preNodeKeysCnt; i++) {
+ preNode = curNode.preNodes[i];
+ preNodeDir = board.getNeighborTileDirection(curNode, preNode);
+ if (preNodeDir === curDir) {
+ break;
+ }
+ }
+ curDir = preNodeDir;
+ curNode = preNode;
+ break;
+
+ case LINE:
+ if (targetAngle === undefined) {
+ targetAngle = endNode.angleTo(startNode);
+ }
+ if (preNodeKeysCnt === 1) {
+ preNode = curNode.preNodes[0];
+ curNode = preNode;
+ targetAngle = endNode.angleTo(curNode);
+ } else {
+ preNode = curNode.preNodes[0];
+ var deltaAngle = Math.abs(endNode.angleTo(preNode) - targetAngle);
+ var preNodeB, deltaAngleB;
+ for (var i = 1; i < preNodeKeysCnt; i++) {
+ preNodeB = curNode.preNodes[i];
+ deltaAngleB = Math.abs(endNode.angleTo(preNodeB) - targetAngle);
+ if (deltaAngleB < deltaAngle) {
+ preNode = preNodeB;
+ }
+ }
+ curNode = preNode;
+ }
+ break;
+ }
+ }
+ return path.reverse();
+}
+export default GetNodePath;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/Node.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/Node.js
new file mode 100644
index 000000000..27419d59e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/Node.js
@@ -0,0 +1,112 @@
+import Shuffle from '../../../utils/array/Shuffle.js';
+import AngleBetween from '../../../utils/math/angle/Between.js';
+
+class Node {
+ constructor() {
+ this.preNodes = [];
+ this.manager = undefined;
+ }
+
+ reset(manager) {
+ this.manager = manager;
+ // overwrite
+ this.sn = undefined; // for sorting by created order
+ this.key = undefined;
+ this.x = undefined;
+ this.y = undefined;
+ this.isTileXYZ = true;
+ // overwrite
+
+ this._px = undefined;
+ this._py = undefined;
+ this.cost = undefined; // cost cache
+ this.f = 0;
+ this.g = 0; // path cost
+ this.h = 0;
+ this.closerH = 0;
+ this.visited = false;
+ this.closed = false;
+ this.preNodes.length = 0;
+ }
+
+ destroy() {
+ this.preNodes.length = 0;
+ this.manager = undefined;
+ }
+
+ heuristic(endNode, pathMode, baseNode) {
+ if (pathMode === null) {
+ return 0;
+ }
+
+ var h, dist = this.board.getDistance(endNode, this, true) * this.pathFinder.weight;
+
+ if ((pathMode === 1) && (baseNode !== undefined)) {
+ var deltaAngle = endNode.angleTo(baseNode) - this.angleTo(baseNode);
+ h = dist + Math.abs(deltaAngle);
+ } else if (pathMode === 2) {
+ h = dist + Math.random();
+ } else {
+ h = dist;
+ }
+
+ return h;
+ }
+
+ getNeighborNodes() {
+ var neighborsTileXY = this.board.getNeighborTileXY(this);
+ if (this.pathFinder.shuffleNeighbors) {
+ Shuffle(neighborsTileXY);
+ }
+
+ var node, neighborNodes = [];
+ for (var i = 0, cnt = neighborsTileXY.length; i < cnt; i++) {
+ node = this.manager.getNode(neighborsTileXY[i], true);
+ neighborNodes.push(node)
+ }
+ return neighborNodes;
+ }
+
+ getCost(preNode) {
+ if (this.pathFinder.cacheCost) {
+ if (this.cost === undefined) {
+ this.cost = this.pathFinder.getCost(this, preNode);
+ }
+ } else {
+ this.cost = this.pathFinder.getCost(this, preNode);
+ }
+ return this.cost;
+ }
+
+ angleTo(endNode) {
+ return AngleBetween(this.worldX, this.wroldY, endNode.worldX, endNode.wroldY);
+ }
+
+ get pathFinder() {
+ return this.manager.pathFinder;
+ }
+
+ get board() {
+ return this.manager.pathFinder.board;
+ }
+
+ get worldX() {
+ if (this._px === undefined) {
+ this._px = this.board.tileXYToWroldX(this.x, this.y);
+ }
+ return this._px;
+ }
+
+ get wroldY() {
+ if (this._py === undefined) {
+ this._py = this.board.tileXYToWroldY(this.x, this.y);
+ }
+ return this._py;
+ }
+
+ get pathCost() {
+ return this.g;
+ }
+}
+
+export default Node;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/NodeManager.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/NodeManager.js
new file mode 100644
index 000000000..424c1f4b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/astartsearch/NodeManager.js
@@ -0,0 +1,86 @@
+import Pool from '../../../pool.js';
+import TileXYToKey from '../../utils/tilexyzkey/TileXYToKey.js';
+import Node from './Node.js';
+
+// global object
+var NodesPool = new Pool(); // recycle dead nodes
+// global object
+
+class NodeCache {
+ constructor(pathFinder) {
+ this.sn = 0;
+ this.pool = NodesPool;
+ this.nodes = {}; // {tileXYKey:node}
+ this.pathFinder = pathFinder;
+ this.closestNode = null;
+ }
+
+ destroy() {
+ this.freeAllNodes();
+ this.pathFinder = null;
+ this.pool = undefined;
+ return this;
+ }
+
+ getNode(tileX, tileY, createNewNode) {
+ var key;
+ switch (typeof (tileX)) {
+ case 'number': // (tileX, tileY, createNewNode)
+ key = TileXYToKey(tileX, tileY);
+ break;
+ case 'string': // (key, createNewNode)
+ key = tileX;
+ createNewNode = tileY;
+ break;
+ default: // (tileXY, createNewNode)
+ var tileXY = tileX;
+ createNewNode = tileY;
+ tileX = tileXY.x;
+ tileY = tileXY.y;
+ key = TileXYToKey(tileX, tileY);
+ break;
+ }
+ if (createNewNode === undefined) {
+ createNewNode = false;
+ }
+
+ this.sn++;
+ if (!this.nodes.hasOwnProperty(key)) {
+ if (!createNewNode) {
+ return null;
+ }
+
+ var node = this.pool.pop();
+ if (node === null) {
+ node = new Node();
+ }
+ node.reset(this);
+ node.sn = this.sn;
+ node.key = key;
+ node.x = tileX;
+ node.y = tileY;
+ this.nodes[key] = node;
+ }
+ return this.nodes[key];
+ }
+
+ freeAllNodes() {
+ this.closestNode = null;
+ var nodes = this.nodes,
+ pool = this.pool;
+ var node;
+ for (var key in nodes) {
+ node = nodes[key];
+ node.destroy();
+ pool.push(node);
+ delete nodes[key];
+ }
+ this.sn = 0;
+ return this;
+ }
+
+ getAllNodes() {
+ return this.nodes;
+ }
+}
+export default NodeCache;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/const.js b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/const.js
new file mode 100644
index 000000000..05e60a361
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/pathfinder/const.js
@@ -0,0 +1,20 @@
+export default {
+ // a* search mode
+ AREA_MODE: 16,
+ PATH_MODE: 0,
+
+ // path mode
+ 'random': 0,
+ 'diagonal': 1,
+ 'straight': 2,
+ 'line': 3,
+ 'A*': 10,
+ 'A*-random': 11,
+ 'A*-line': 12,
+
+ // special cost
+ 'BLOCKER': null,
+
+ // special moving point
+ 'INFINITY': undefined,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.d.ts
new file mode 100644
index 000000000..e1d342640
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.d.ts
@@ -0,0 +1,10 @@
+import Shape from './Shape';
+import Board from '../board/LogicBoard';
+import MiniBoard from '../miniboard/MiniBoard';
+
+export default function (
+ board: Board | MiniBoard,
+ tileX: number, tileY: number, tileZ?: number,
+ fillColor?: number | null, fillAlpha?: number | null,
+ addToBoard?: boolean
+): Shape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.js b/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.js
new file mode 100644
index 000000000..94bf77ec7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/shape/Factory.js
@@ -0,0 +1,13 @@
+import Shape from './Shape.js';
+import ObjectFactory from '../ObjectFactory.js';
+import SetValue from '../../utils/object/SetValue.js';
+
+ObjectFactory.register('shape', function (board, tileX, tileY, tileZ, fillColor, fillAlpha, addToBoard) {
+ var gameObject = new Shape(board, tileX, tileY, tileZ, fillColor, fillAlpha, addToBoard);
+ board.scene.add.existing(gameObject);
+ return gameObject;
+});
+
+SetValue(window, 'RexPlugins.Board.Shape', Shape);
+
+export default Shape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.d.ts
new file mode 100644
index 000000000..754e5974a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.d.ts
@@ -0,0 +1,12 @@
+// import * as Phaser from 'phaser';
+import Board from '../board/Board';
+import MiniBoard from '../miniboard/MiniBoard';
+
+export default class Shape extends Phaser.GameObjects.Polygon {
+ constructor(
+ board: Board | MiniBoard,
+ tileX: number, tileY: number, tileZ?: number,
+ fillColor?: number | null, fillAlpha?: number | null,
+ addToBoard?: boolean
+ );
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.js b/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.js
new file mode 100644
index 000000000..1f50a8251
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/shape/Shape.js
@@ -0,0 +1,64 @@
+import CreateChessData from '../chess/GetChessData.js';
+import IsMiniBoardObject from './../miniboard/IsMiniBoardObject.js';
+
+const Base = Phaser.GameObjects.Polygon;
+class Shape extends Base {
+ constructor(board, tileX, tileY, tileZ, fillColor, fillAlpha, addToBoard) {
+ if (addToBoard === undefined) {
+ addToBoard = true;
+ }
+
+ // Chess-Container
+ var isMiniBoard = IsMiniBoardObject(board),
+ miniBoard;
+ if (isMiniBoard) {
+ miniBoard = board;
+ board = miniBoard.board;
+ }
+
+ var scene = board.scene;
+ var worldX, worldY;
+ if (addToBoard) {
+ worldX = 0;
+ worldY = 0;
+ } else {
+ worldX = tileX;
+ worldY = tileY;
+ }
+ var points = board.getGridPoints(undefined, undefined, true);
+ ShiftToO(points);
+ super(scene, worldX, worldY, points, fillColor, fillAlpha);
+
+ if (addToBoard) {
+ if (isMiniBoard) { // Chess-Container
+ miniBoard.addChess(this, tileX, tileY, tileZ);
+ } else {
+ board.addChess(this, tileX, tileY, tileZ, true);
+ }
+ } else {
+ CreateChessData(this);
+ }
+ }
+}
+
+var ShiftToO = function (points) {
+ var minX = Infinity;
+ var minY = Infinity;
+ var point;
+ for (var i = 0, cnt = points.length; i < cnt; i++) {
+ point = points[i];
+ minX = Math.min(minX, point.x);
+ minY = Math.min(minY, point.y);
+ }
+ if ((minX === 0) && (minY === 0)) {
+ return points;
+ }
+ for (var i = 0, cnt = points.length; i < cnt; i++) {
+ point = points[i];
+ point.x -= minX;
+ point.y -= minY;
+ }
+ return points;
+}
+
+export default Shape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.d.ts
new file mode 100644
index 000000000..45dc4bd41
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.d.ts
@@ -0,0 +1,20 @@
+import Board from '../board/Board';
+
+export default function CreateTileTexture(
+ board: Board,
+ key: string,
+ fillStyle: number | string | undefined,
+ strokeStyle?: number | string | undefined,
+ lineWidth?: number,
+ lineJoin?: 'round' | 'bevel' | 'miter'
+): void;
+
+export default function CreateTileTexture(
+ board: Board,
+ key: string,
+ fillStyle: number | string | undefined,
+ strokeStyle?: number | string | undefined,
+ lineWidth?: number,
+ overlapGrid?: boolean,
+ lineJoin?: 'round' | 'bevel' | 'miter'
+): void;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.js b/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.js
new file mode 100644
index 000000000..c77f71e0e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/texture/CreateTileTexture.js
@@ -0,0 +1,27 @@
+import CreatePolygonTexture from '../../utils/texture/CreatePolygonTexture.js';
+
+var CreateTileTexture = function (board, key, fillStyle, strokeStyle, lineWidth, overlapGrid, lineJoin) {
+ if (typeof (overlapGrid) === 'string') {
+ lineJoin = overlapGrid;
+ overlapGrid = undefined;
+ }
+
+ if (overlapGrid === undefined) {
+ overlapGrid = true;
+ }
+ if (lineJoin === undefined) {
+ lineJoin = 'miter';
+ }
+
+ CreatePolygonTexture(
+ board.scene,
+ key,
+ board.getGridPoints(0, 0, true),
+ fillStyle,
+ strokeStyle, lineWidth,
+ overlapGrid,
+ lineJoin
+ );
+}
+
+export default CreateTileTexture;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/tilemap/AddLayers.js b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/AddLayers.js
new file mode 100644
index 000000000..8f69fd8ae
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/AddLayers.js
@@ -0,0 +1,35 @@
+import IsGameObject from '../../utils/system/IsGameObject.js';
+
+var AddLayers = function (board, tilemap, layers) {
+ if (layers === undefined) {
+ layers = tilemap.layers;
+ } else if (!Array.isArray(layers)) {
+ layers = [layers];
+ }
+
+ for (var i = 0, cnt = layers.length; i < cnt; i++) {
+ var layer = layers[i];
+ if (typeof (layer) === 'string') {
+ layer = tilemap.getLayer(layer);
+ }
+ if (IsGameObject(layer)) {
+ layer = layer.layer;
+ }
+
+ AddLayer(board, layer);
+ }
+}
+
+var AddLayer = function (board, layer) {
+ var tileZ = layer.name;
+ var layerData = layer.data;
+ for (var y = 0, ycnt = layerData.length; y < ycnt; y++) {
+ var layerRow = layerData[y];
+ for (var x = 0, xcnt = layerRow.length; x < xcnt; x++) {
+ var tile = layerRow[x];
+ board.addChess(tile, x, y, tileZ, false);
+ }
+ }
+}
+
+export default AddLayers;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoard.js b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoard.js
new file mode 100644
index 000000000..6fee4e5f4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoard.js
@@ -0,0 +1,51 @@
+import Board from '../board/Board.js';
+
+var CreateBoard = function (tilemap) {
+ var board = new Board(tilemap.scene, {
+ grid: CreateGridConfig(tilemap),
+ width: tilemap.width,
+ height: tilemap.height
+ })
+
+ return board;
+}
+
+var CreateGridConfig = function (tilemap) {
+ var grid = {
+ cellWidth: tilemap.tileWidth,
+ cellHeight: tilemap.tileHeight,
+ }
+
+ switch (tilemap.orientation) {
+ case 0: // ORTHOGONAL
+ grid.gridType = 'quadGrid';
+ grid.type = 'orthogonal';
+ break;
+
+ case 1: // ISOMETRIC
+ grid.gridType = 'quadGrid';
+ grid.type = 'isometric';
+ break;
+
+ case 3: // HEXAGONAL
+ grid.gridType = 'hexagonGrid';
+ grid.staggeraxis = 'y';
+ grid.staggerindex = 'odd';
+ break;
+
+ default: // ORTHOGONAL
+ grid.gridType = 'quadGrid';
+ grid.type = 'orthogonal';
+ break;
+ }
+
+ var layer = tilemap.layers[0];
+ if (layer) {
+ grid.x = layer.x;
+ grid.y = layer.y;
+ }
+
+ return grid;
+}
+
+export default CreateBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.d.ts
new file mode 100644
index 000000000..a5c12ec6c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.d.ts
@@ -0,0 +1,8 @@
+import Board from '../board/Board';
+
+export default CreateBoardFromTilemap;
+
+declare function CreateBoardFromTilemap(
+ tilemap: Phaser.Tilemaps.Tilemap,
+ layers?: Phaser.Tilemaps.TilemapLayer[] | Phaser.Tilemaps.TilemapLayer | string[] | string
+): Board;
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.js b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.js
new file mode 100644
index 000000000..f6eae795c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/tilemap/CreateBoardFromTilemap.js
@@ -0,0 +1,10 @@
+import CreateBoard from './CreateBoard.js';
+import AddLayers from './AddLayers.js';
+
+var CreateBoardFromTilemap = function (tilemap, layers) {
+ var board = CreateBoard(tilemap);
+ AddLayers(board, tilemap, layers);
+ return board;
+}
+
+export default CreateBoardFromTilemap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/types/Position.d.ts b/ui/src/phaser3-rex-plugins/plugins/board/types/Position.d.ts
new file mode 100644
index 000000000..2d9e5cd2f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/types/Position.d.ts
@@ -0,0 +1,5 @@
+export type TileXYZType = { x: number, y: number, z: (number | string) };
+export type TileXYType = { x: number, y: number, z?: (number | string) };
+export type TileXYDirectionType = { x: number, y: number, direction: number };
+
+export type WorldXYType = { x: number, y: number };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYArrayEqual.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYArrayEqual.js
new file mode 100644
index 000000000..e4ae827b4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYArrayEqual.js
@@ -0,0 +1,15 @@
+import AreTileXYEqual from './AreTileXYEqual.js';
+
+var AreTileXYArrayEqual = function (tileArrayA, tileArrayB) {
+ if (tileArrayA.length !== tileArrayB.length) {
+ return false;
+ } else {
+ for (var i = 0, cnt = tileArrayA.length; i < cnt; i++) {
+ if (!AreTileXYEqual(tileArrayA[i], tileArrayB[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+export default AreTileXYArrayEqual;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYEqual.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYEqual.js
new file mode 100644
index 000000000..c506efdbc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/AreTileXYEqual.js
@@ -0,0 +1,4 @@
+var AreTileXYEqual = function (tileA, tileB) {
+ return tileA && tileB && (tileA.x === tileB.x) && (tileA.y === tileB.y);
+}
+export default AreTileXYEqual;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYInArray.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYInArray.js
new file mode 100644
index 000000000..96f84eda2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYInArray.js
@@ -0,0 +1,12 @@
+import AreTileXYEqual from './AreTileXYEqual.js';
+
+var IsTileXYInArray = function (tile, arr) {
+ for (var i = 0, cnt = arr.length; i < cnt; i++) {
+ if (AreTileXYEqual(tile, arr[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export default IsTileXYInArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYZ.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYZ.js
new file mode 100644
index 000000000..2090e0ba9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/IsTileXYZ.js
@@ -0,0 +1,8 @@
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+
+var IsTileXYZ = function (obj) {
+ return (obj) &&
+ (IsPlainObject(obj) || obj.isTileXYZ);
+}
+
+export default IsTileXYZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/KeyToTileXYZ.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/KeyToTileXYZ.js
new file mode 100644
index 000000000..43cb8ccb0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/KeyToTileXYZ.js
@@ -0,0 +1,19 @@
+var KeyToTileXYZ = function (key, out, separator) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ out = globTileXYZ;
+ }
+
+ if (separator === undefined) {
+ separator = ',';
+ }
+ var items = key.split(separator);
+ out.x = items[0];
+ out.y = items[1];
+ out.z = items[2];
+ return out;
+}
+
+var globTileXYZ = {};
+export default KeyToTileXYZ;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYToKey.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYToKey.js
new file mode 100644
index 000000000..3a64f9c92
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYToKey.js
@@ -0,0 +1,7 @@
+var TileXYToKey = function (tileX, tileY, separator) {
+ if (separator === undefined) {
+ separator = ',';
+ }
+ return `${tileX}${separator}${tileY}`;
+}
+export default TileXYToKey;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYZToKey.js b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYZToKey.js
new file mode 100644
index 000000000..5508e2f2e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/board/utils/tilexyzkey/TileXYZToKey.js
@@ -0,0 +1,7 @@
+var TileXYZToKey = function (tileX, tileY, tileZ, separator) {
+ if (separator === undefined) {
+ separator = ',';
+ }
+ return `${tileX}${separator}${tileY}${separator}${tileZ}`;
+}
+export default TileXYZToKey;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/boids-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/boids-plugin.d.ts
new file mode 100644
index 000000000..f27110fe5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/boids-plugin.d.ts
@@ -0,0 +1,9 @@
+import Boids from './boids';
+
+export default class BoidsPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Boids.IConfig
+ ): Boids;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/boids-plugin.js b/ui/src/phaser3-rex-plugins/plugins/boids-plugin.js
new file mode 100644
index 000000000..5a26632e6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/boids-plugin.js
@@ -0,0 +1,18 @@
+import Boids from './boids.js';
+
+class BoidsPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Boids(gameObject, config);
+ }
+}
+export default BoidsPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/boids.d.ts b/ui/src/phaser3-rex-plugins/plugins/boids.d.ts
new file mode 100644
index 000000000..6070dffcb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/boids.d.ts
@@ -0,0 +1,2 @@
+import Boids from './behaviors/boids/Boids';
+export default Boids;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/boids.js b/ui/src/phaser3-rex-plugins/plugins/boids.js
new file mode 100644
index 000000000..ae1e000a8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/boids.js
@@ -0,0 +1,2 @@
+import Boids from './behaviors/boids/Boids.js';
+export default Boids;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bounds-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bounds-plugin.js
new file mode 100644
index 000000000..bed402497
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bounds-plugin.js
@@ -0,0 +1,20 @@
+import Bounds from './bounds.js';
+
+class BoundsPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Bounds(gameObject, config);
+ }
+
+}
+
+export default BoundsPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bounds.js b/ui/src/phaser3-rex-plugins/plugins/bounds.js
new file mode 100644
index 000000000..01a70f717
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bounds.js
@@ -0,0 +1,2 @@
+import Bounds from './behaviors/bounds/Bounds.js';
+export default Bounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.d.ts
new file mode 100644
index 000000000..d8e5cb502
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.d.ts
@@ -0,0 +1,8 @@
+import BracketParser from './bracketparser';
+
+export default class BracketParserPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ config?: BracketParser.IConfig
+ ): BracketParser;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.js
new file mode 100644
index 000000000..d05d583ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser-plugin.js
@@ -0,0 +1,18 @@
+import BracketParser from './bracketparser.js';
+
+class BracketParserPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(config) {
+ return new BracketParser(config);
+ }
+}
+
+export default BracketParserPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser.d.ts b/ui/src/phaser3-rex-plugins/plugins/bracketparser.d.ts
new file mode 100644
index 000000000..42c377ff0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser.d.ts
@@ -0,0 +1,2 @@
+import BracketParser from './logic/bracketparser/bracketparser/BracketParser';
+export default BracketParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser.js b/ui/src/phaser3-rex-plugins/plugins/bracketparser.js
new file mode 100644
index 000000000..528b159a8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser.js
@@ -0,0 +1,2 @@
+import BracketParser from './logic/bracketparser/bracketparser/BracketParser.js';
+export default BracketParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.d.ts
new file mode 100644
index 000000000..35c21e4e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.d.ts
@@ -0,0 +1,8 @@
+import BracketParser from './bracketparser2';
+
+export default class BracketParserPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ config?: BracketParser.IConfig
+ ): BracketParser;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.js
new file mode 100644
index 000000000..c1583f3d3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser2-plugin.js
@@ -0,0 +1,18 @@
+import BracketParser from './bracketparser2.js';
+
+class BracketParserPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(config) {
+ return new BracketParser(config);
+ }
+}
+
+export default BracketParserPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser2.d.ts b/ui/src/phaser3-rex-plugins/plugins/bracketparser2.d.ts
new file mode 100644
index 000000000..cf23a4005
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser2.d.ts
@@ -0,0 +1,2 @@
+import BracketParser from './logic/bracketparser/bracketparser2/BracketParser';
+export default BracketParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bracketparser2.js b/ui/src/phaser3-rex-plugins/plugins/bracketparser2.js
new file mode 100644
index 000000000..58aa5da06
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bracketparser2.js
@@ -0,0 +1,2 @@
+import BracketParser from './logic/bracketparser/bracketparser2/BracketParser.js';
+export default BracketParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.d.ts
new file mode 100644
index 000000000..359870cc7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.d.ts
@@ -0,0 +1,11 @@
+import DataManager from './buffdata';
+import Extend from './data/buff/Extend';
+
+export default class DataManagerPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ parent: object,
+ eventEmitter?: Phaser.Events.EventEmitter,
+ ): DataManager;
+
+ extend: typeof Extend
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.js b/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.js
new file mode 100644
index 000000000..549667dec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buffdata-plugin.js
@@ -0,0 +1,24 @@
+import DataManager from './data/buff/DataManager.js';
+import Extend from './data/buff/Extend.js';
+
+class DataManagerPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(parent, eventEmitter) {
+ return new DataManager(parent, eventEmitter);
+ }
+
+ extend(dataManager) {
+ return Extend(dataManager);
+ }
+}
+
+export default DataManagerPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buffdata.d.ts b/ui/src/phaser3-rex-plugins/plugins/buffdata.d.ts
new file mode 100644
index 000000000..a20e27628
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buffdata.d.ts
@@ -0,0 +1,2 @@
+import DataManager from './data/buff/DataManager';
+export default DataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buffdata.js b/ui/src/phaser3-rex-plugins/plugins/buffdata.js
new file mode 100644
index 000000000..e5e824262
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buffdata.js
@@ -0,0 +1,2 @@
+import DataManager from './data/buff/DataManager.js';
+export default DataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.d.ts
new file mode 100644
index 000000000..b5d71457f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.d.ts
@@ -0,0 +1,6 @@
+import BuildArcadeObject from './buildarcadeobject';
+
+export default class BuildArcadeObjectPlugin extends Phaser.Plugins.BasePlugin {
+ build: typeof BuildArcadeObject
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.js b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.js
new file mode 100644
index 000000000..6ce193fa9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject-plugin.js
@@ -0,0 +1,19 @@
+import BuildArcadeObject from './buildarcadeobject.js';
+
+class BuildArcadeObjectPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ build(gameObject, isStatic) {
+ return BuildArcadeObject(gameObject, isStatic);
+ }
+}
+
+export default BuildArcadeObjectPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.d.ts b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.d.ts
new file mode 100644
index 000000000..321651837
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.d.ts
@@ -0,0 +1,2 @@
+import BuildArcadeObject from './utils/arcade/BuildArcadeObject';
+export default BuildArcadeObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.js b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.js
new file mode 100644
index 000000000..65ac9df68
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/buildarcadeobject.js
@@ -0,0 +1,2 @@
+import BuildArcadeObject from './utils/arcade/BuildArcadeObject.js';
+export default BuildArcadeObject;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.d.ts
new file mode 100644
index 000000000..c61ee9f26
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.d.ts
@@ -0,0 +1,9 @@
+import Bullet from './bullet';
+
+export default class BulletPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Bullet.IConfig
+ ): Bullet;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.js b/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.js
new file mode 100644
index 000000000..a59149c5f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bullet-plugin.js
@@ -0,0 +1,20 @@
+import Bullet from './bullet.js';
+
+class BulletPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Bullet(gameObject, config);
+ }
+
+}
+
+export default BulletPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bullet.d.ts b/ui/src/phaser3-rex-plugins/plugins/bullet.d.ts
new file mode 100644
index 000000000..cefcd6ad8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bullet.d.ts
@@ -0,0 +1,2 @@
+import Bullet from './behaviors/bullet/Bullet';
+export default Bullet;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/bullet.js b/ui/src/phaser3-rex-plugins/plugins/bullet.js
new file mode 100644
index 000000000..e631247df
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/bullet.js
@@ -0,0 +1,2 @@
+import Bullet from './behaviors/bullet/Bullet.js';
+export default Bullet;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/button-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/button-plugin.d.ts
new file mode 100644
index 000000000..7e33447ff
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/button-plugin.d.ts
@@ -0,0 +1,9 @@
+import Button from './button';
+
+export default class ButtonPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Button.IConfig
+ ): Button;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/button-plugin.js b/ui/src/phaser3-rex-plugins/plugins/button-plugin.js
new file mode 100644
index 000000000..00c8dfe75
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/button-plugin.js
@@ -0,0 +1,20 @@
+import Button from './button.js';
+
+class ButtonPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Button(gameObject, config);
+ }
+
+}
+
+export default ButtonPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/button.d.ts b/ui/src/phaser3-rex-plugins/plugins/button.d.ts
new file mode 100644
index 000000000..5ec0cf4b3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/button.d.ts
@@ -0,0 +1,2 @@
+import Button from './input/button/Button';
+export default Button;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/button.js b/ui/src/phaser3-rex-plugins/plugins/button.js
new file mode 100644
index 000000000..737893eed
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/button.js
@@ -0,0 +1,2 @@
+import Button from './input/button/Button.js';
+export default Button;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvas-plugin.js b/ui/src/phaser3-rex-plugins/plugins/canvas-plugin.js
new file mode 100644
index 000000000..59802b274
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvas-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/canvas/canvas/Factory.js';
+import Creator from './gameobjects/canvas/canvas/Creator.js';
+import Canvas from './gameobjects/canvas/canvas/Canvas.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CanvasPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCanvas', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.Canvas', Canvas);
+
+export default CanvasPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvas.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvas.d.ts
new file mode 100644
index 000000000..832d2f676
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvas.d.ts
@@ -0,0 +1,2 @@
+import Canvas from './gameobjects/canvas/canvas/Canvas';
+export default Canvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvas.js b/ui/src/phaser3-rex-plugins/plugins/canvas.js
new file mode 100644
index 000000000..bfa9ce6fd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvas.js
@@ -0,0 +1,2 @@
+import Canvas from './gameobjects/canvas/canvas/Canvas.js';
+export default Canvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.d.ts
new file mode 100644
index 000000000..dbc0b119f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.d.ts
@@ -0,0 +1,8 @@
+import CanvasObjectToBitmap from './data/canvasdata/CanvasObjectToBitmap';
+import TextureTColorMap from './data/canvasdata/TextureToColormap';
+
+export default class CanvasDataPlugin extends Phaser.Plugins.BasePlugin {
+ textObjectToBitmap: typeof CanvasObjectToBitmap;
+ canvasObjectToBitmap: typeof CanvasObjectToBitmap;
+ textureTColorMap: typeof TextureTColorMap;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.js b/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.js
new file mode 100644
index 000000000..adeab6b04
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasdata-plugin.js
@@ -0,0 +1,34 @@
+import Methods from './canvasdata.js';
+
+const CanvasPool = Phaser.Display.Canvas.CanvasPool;
+
+class CanvasDataPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+
+ this._tmpCanvas = CanvasPool.create2D(this);
+ }
+
+ destroy() {
+ CanvasPool.remove(this._tmpCanvas);
+ this._tmpCanvas = undefined;
+ super.destroy();
+ }
+
+ get textureManager() {
+ return this.game.textures;
+ }
+}
+
+Object.assign(
+ CanvasDataPlugin.prototype,
+ Methods
+);
+
+export default CanvasDataPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasdata.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvasdata.d.ts
new file mode 100644
index 000000000..8e83fa61c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasdata.d.ts
@@ -0,0 +1,10 @@
+import CanvasObjectToBitmap from './data/canvasdata/CanvasObjectToBitmap';
+import TextureTColorMap from './data/canvasdata/TextureToColormap';
+
+declare var Methods: {
+ textObjectToBitmap: typeof CanvasObjectToBitmap,
+ canvasObjectToBitmap: typeof CanvasObjectToBitmap,
+ textureTColorMap: typeof TextureTColorMap,
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasdata.js b/ui/src/phaser3-rex-plugins/plugins/canvasdata.js
new file mode 100644
index 000000000..d37c960f3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasdata.js
@@ -0,0 +1,2 @@
+import Methods from './data/canvasdata/Methods.js';
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.d.ts
new file mode 100644
index 000000000..0e149c73a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.d.ts
@@ -0,0 +1,19 @@
+import CanvasFrameManager from './canvasframemanager';
+
+export default class CanvasFrameManagerPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ scene: Phaser.Scene,
+ key: string,
+ width?: number,
+ height?: number,
+ cellWidth?: number,
+ cellHeight?: number,
+ fillColor?: string
+ ): CanvasFrameManager;
+
+ add(
+ scene: Phaser.Scene,
+ config: CanvasFrameManager.IConfig
+ ): CanvasFrameManager;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.js b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.js
new file mode 100644
index 000000000..b540b03c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager-plugin.js
@@ -0,0 +1,19 @@
+import CanvasFrameManager from './canvasframemanager.js';
+
+class CanvasFrameManagerPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, key, width, height, cellWidth, cellHeight, fillColor) {
+ return new CanvasFrameManager(scene, key, width, height, cellWidth, cellHeight, fillColor);
+ }
+}
+
+export default CanvasFrameManagerPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.d.ts
new file mode 100644
index 000000000..aac6907ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.d.ts
@@ -0,0 +1,2 @@
+import CanvasFrameManager from './texture/canvasframemanager/CanvasFrameManager';
+export default CanvasFrameManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.js b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.js
new file mode 100644
index 000000000..5f1dbe0a3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasframemanager.js
@@ -0,0 +1,2 @@
+import CanvasFrameManager from './texture/CanvasFrameManager/CanvasFrameManager.js';
+export default CanvasFrameManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasinput-plugin.js b/ui/src/phaser3-rex-plugins/plugins/canvasinput-plugin.js
new file mode 100644
index 000000000..22d4ff704
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasinput-plugin.js
@@ -0,0 +1,24 @@
+import CanvasInputFactory from './gameobjects/dynamictext/canvasinput/Factory';
+import CanvasInputCreator from './gameobjects/dynamictext/canvasinput/Creator.js';
+import CanvasInput from './gameobjects/dynamictext/canvasinput/CanvasInput.js';
+
+import SetValue from './utils/object/SetValue.js';
+
+class CanvasInputPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCanvasInput', CanvasInputFactory, CanvasInputCreator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CanvasInput', CanvasInput);
+
+export default CanvasInputPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasinput.d.ts b/ui/src/phaser3-rex-plugins/plugins/canvasinput.d.ts
new file mode 100644
index 000000000..f9a8b7915
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasinput.d.ts
@@ -0,0 +1,2 @@
+import CanvasInput from './gameobjects/dynamictext/canvasinput/CanvasInput';
+export default CanvasInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/canvasinput.js b/ui/src/phaser3-rex-plugins/plugins/canvasinput.js
new file mode 100644
index 000000000..721c242b4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/canvasinput.js
@@ -0,0 +1,2 @@
+import CanvasInput from './gameobjects/dynamictext/canvasinput/CanvasInput.js';
+export default CanvasInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/carousel-plugin.js b/ui/src/phaser3-rex-plugins/plugins/carousel-plugin.js
new file mode 100644
index 000000000..f60d8ab7c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/carousel-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/container/carousel/Factory.js';
+import Creator from './gameobjects/container/carousel/Creator.js';
+import Carousel from './gameobjects/container/carousel/Carousel.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CarouselPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCarousel', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.Carousel', Carousel);
+
+export default CarouselPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/carousel.js b/ui/src/phaser3-rex-plugins/plugins/carousel.js
new file mode 100644
index 000000000..315cf76da
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/carousel.js
@@ -0,0 +1,2 @@
+import Carousel from './gameobjects/container/carousel/Carousel.js';
+export default Carousel;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.d.ts
new file mode 100644
index 000000000..74673f76f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.d.ts
@@ -0,0 +1,9 @@
+import CharacterCache from './charactercache';
+
+export default class CharacterCachePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ scene: Phaser.Scene,
+ config: CharacterCache.IConfig
+ ): CharacterCache;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.js b/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.js
new file mode 100644
index 000000000..b4856ac25
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/charactercache-plugin.js
@@ -0,0 +1,19 @@
+import CharacterCache from './charactercache.js';
+
+class CharacterCachePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new CharacterCache(scene, config);
+ }
+}
+
+export default CharacterCachePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/charactercache.d.ts b/ui/src/phaser3-rex-plugins/plugins/charactercache.d.ts
new file mode 100644
index 000000000..da95671f8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/charactercache.d.ts
@@ -0,0 +1,2 @@
+import CharacterCache from './texture/charactercache/CharacterCache';
+export default CharacterCache;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/charactercache.js b/ui/src/phaser3-rex-plugins/plugins/charactercache.js
new file mode 100644
index 000000000..90db3abd5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/charactercache.js
@@ -0,0 +1,2 @@
+import CharacterCache from './texture/charactercache/CharacterCache.js';
+export default CharacterCache;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/checkbox-plugin.js b/ui/src/phaser3-rex-plugins/plugins/checkbox-plugin.js
new file mode 100644
index 000000000..ad39adf6a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/checkbox-plugin.js
@@ -0,0 +1,28 @@
+import Factory from './gameobjects/shape/checkbox/Factory.js';
+import Creator from './gameobjects/shape/checkbox/Creator.js';
+import Checkbox from './gameobjects/shape/checkbox/Checkbox.js';
+import CheckboxShapeFactory from './gameobjects/shape/checkbox/CheckboxShapeFactory.js';
+import CheckboxShapeCreator from './gameobjects/shape/checkbox/CheckboxShapeCreator.js';
+import CheckboxShape from './gameobjects/shape/checkbox/CheckboxShape.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CheckboxPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCheckbox', Factory, Creator);
+ pluginManager.registerGameObject('rexCheckboxShape', CheckboxShapeFactory, CheckboxShapeCreator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.Checkbox', Checkbox);
+SetValue(window, 'RexPlugins.GameObjects.CheckboxShape', CheckboxShape);
+
+export default CheckboxPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/checkbox.d.ts b/ui/src/phaser3-rex-plugins/plugins/checkbox.d.ts
new file mode 100644
index 000000000..12dc7ce2c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/checkbox.d.ts
@@ -0,0 +1,2 @@
+import Checkbox from './gameobjects/shape/checkbox/Checkbox';
+export default Checkbox;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/checkbox.js b/ui/src/phaser3-rex-plugins/plugins/checkbox.js
new file mode 100644
index 000000000..dfda9bdd4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/checkbox.js
@@ -0,0 +1,2 @@
+import Checkbox from './gameobjects/shape/checkbox/Checkbox.js';
+export default Checkbox;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/checkboxshape.d.ts b/ui/src/phaser3-rex-plugins/plugins/checkboxshape.d.ts
new file mode 100644
index 000000000..c18f1180c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/checkboxshape.d.ts
@@ -0,0 +1,2 @@
+import CheckboxShape from './gameobjects/shape/checkbox/CheckboxShape';
+export default CheckboxShape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/checkboxshape.js b/ui/src/phaser3-rex-plugins/plugins/checkboxshape.js
new file mode 100644
index 000000000..8fd1f63e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/checkboxshape.js
@@ -0,0 +1,2 @@
+import CheckboxShape from './gameobjects/shape/checkbox/CheckboxShape.js';
+export default CheckboxShape;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circlemaskimage-plugin.js b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage-plugin.js
new file mode 100644
index 000000000..22fd9b671
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/canvas/circlemaskimage/Factory.js';
+import Creator from './gameobjects/canvas/circlemaskimage/Creator.js';
+import CircleMaskImage from './gameobjects/canvas/circlemaskimage/CircleMaskImage.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CircleMaskImagePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCircleMaskImage', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CircleMaskImage', CircleMaskImage);
+
+export default CircleMaskImagePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.d.ts b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.d.ts
new file mode 100644
index 000000000..c749f37be
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.d.ts
@@ -0,0 +1,2 @@
+import CircleMaskImage from './gameobjects/canvas/circlemaskimage/CircleMaskImage';
+export default CircleMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.js b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.js
new file mode 100644
index 000000000..fb90716b9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circlemaskimage.js
@@ -0,0 +1,2 @@
+import CircleMaskImage from './gameobjects/canvas/circlemaskimage/CircleMaskImage.js';
+export default CircleMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogress-plugin.js b/ui/src/phaser3-rex-plugins/plugins/circularprogress-plugin.js
new file mode 100644
index 000000000..0677893f0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogress-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shape/circularprogress/Factory.js';
+import Creator from './gameobjects/shape/circularprogress/Creator.js';
+import CircularProgress from './gameobjects/shape/circularprogress/CircularProgress.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CircularProgressPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCircularProgress', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CircularProgress', CircularProgress);
+
+export default CircularProgressPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogress.d.ts b/ui/src/phaser3-rex-plugins/plugins/circularprogress.d.ts
new file mode 100644
index 000000000..f604f5db8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogress.d.ts
@@ -0,0 +1,2 @@
+import CircularProgress from './gameobjects/shape/circularprogress/CircularProgress';
+export default CircularProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogress.js b/ui/src/phaser3-rex-plugins/plugins/circularprogress.js
new file mode 100644
index 000000000..d0d4b8029
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogress.js
@@ -0,0 +1,2 @@
+import CircularProgress from './gameobjects/shape/circularprogress/CircularProgress.js';
+export default CircularProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas-plugin.js b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas-plugin.js
new file mode 100644
index 000000000..92d5e13f3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/canvas/circularprogress/Factory.js';
+import Creator from './gameobjects/canvas/circularprogress/Creator.js';
+import CircularProgressCanvas from './gameobjects/canvas/circularprogress/CircularProgress.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CircularProgressCanvasPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCircularProgressCanvas', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CircularProgressCanvas', CircularProgressCanvas);
+
+export default CircularProgressCanvasPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.d.ts b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.d.ts
new file mode 100644
index 000000000..51b3c3ed6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.d.ts
@@ -0,0 +1,2 @@
+import CircularProgressCanvas from './gameobjects/canvas/circularprogress/CircularProgress';
+export default CircularProgressCanvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.js b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.js
new file mode 100644
index 000000000..78cdca507
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/circularprogresscanvas.js
@@ -0,0 +1,2 @@
+import CircularProgressCanvas from './gameobjects/canvas/circularprogress/CircularProgress.js';
+export default CircularProgressCanvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.d.ts
new file mode 100644
index 000000000..3e14c9772
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.d.ts
@@ -0,0 +1,9 @@
+import ClickOutside from './clickoutside';
+
+export default class ClickOutsidePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ClickOutside.IConfig
+ ): ClickOutside;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.js b/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.js
new file mode 100644
index 000000000..35178b742
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clickoutside-plugin.js
@@ -0,0 +1,20 @@
+import ClickOutside from './clickoutside';
+
+class ClickOutsidePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new ClickOutside(gameObject, config);
+ }
+
+}
+
+export default ClickOutsidePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clickoutside.d.ts b/ui/src/phaser3-rex-plugins/plugins/clickoutside.d.ts
new file mode 100644
index 000000000..fcbb03cb5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clickoutside.d.ts
@@ -0,0 +1,2 @@
+import ClickOutside from './input/clickoutside/ClickOutside';
+export default ClickOutside;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clickoutside.js b/ui/src/phaser3-rex-plugins/plugins/clickoutside.js
new file mode 100644
index 000000000..b033f5f79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clickoutside.js
@@ -0,0 +1,2 @@
+import ClickOutside from './input/clickoutside/ClickOutside.js';
+export default ClickOutside;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clock-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/clock-plugin.d.ts
new file mode 100644
index 000000000..1c6d503ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clock-plugin.d.ts
@@ -0,0 +1,9 @@
+import Clock from './clock';
+
+export default class ClockPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ parent: Phaser.Scene | Phaser.GameObjects.GameObject,
+ config?: Clock.IConfig
+ ): Clock;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clock-plugin.js b/ui/src/phaser3-rex-plugins/plugins/clock-plugin.js
new file mode 100644
index 000000000..4b5cb0671
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clock-plugin.js
@@ -0,0 +1,20 @@
+import Clock from './clock.js';
+
+class ClockPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new Clock(scene, config);
+ }
+
+}
+
+export default ClockPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clock.d.ts b/ui/src/phaser3-rex-plugins/plugins/clock.d.ts
new file mode 100644
index 000000000..f1575f4e1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clock.d.ts
@@ -0,0 +1,2 @@
+import Clock from './time/clock/Clock';
+export default Clock;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/clock.js b/ui/src/phaser3-rex-plugins/plugins/clock.js
new file mode 100644
index 000000000..edf6c603a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/clock.js
@@ -0,0 +1,2 @@
+import Clock from './time/clock/Clock.js';
+export default Clock;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.d.ts
new file mode 100644
index 000000000..736db5fe2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.d.ts
@@ -0,0 +1,30 @@
+// import * as Phaser from 'phaser';
+import ColorReplacePostFxPipeline from './colorreplacepipeline';
+
+
+export default ColorReplacePipelinePlugin;
+
+declare namespace ColorReplacePipelinePlugin {
+
+ interface IConfig extends ColorReplacePostFxPipeline.IConfig {
+ name?: string,
+ }
+
+}
+
+declare class ColorReplacePipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: ColorReplacePipelinePlugin.IConfig
+ ): ColorReplacePostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): ColorReplacePostFxPipeline | ColorReplacePostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.js
new file mode 100644
index 000000000..882977c15
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline-plugin.js
@@ -0,0 +1,14 @@
+import ColorReplacePostFxPipeline from './colorreplacepipeline.js';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class ColorReplacePipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(ColorReplacePostFxPipeline, 'rexColorReplacePostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.ColorReplacePostFx', ColorReplacePostFxPipeline);
+
+export default ColorReplacePipelinePlugin;
diff --git a/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.d.ts
new file mode 100644
index 000000000..ddcfc1f0c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.d.ts
@@ -0,0 +1,2 @@
+import ColorReplacePostFxPipeline from './shaders/colorreplace/ColorReplacePostFxPipeline';
+export default ColorReplacePostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.js b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.js
new file mode 100644
index 000000000..9f770a18f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/colorreplacepipeline.js
@@ -0,0 +1,2 @@
+import ColorReplacePostFxPipeline from './shaders/colorreplace/ColorReplacePostFxPipeline.js';
+export default ColorReplacePostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.d.ts
new file mode 100644
index 000000000..3afa74fce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.d.ts
@@ -0,0 +1,6 @@
+import ConditionsTable from './conditionstable';
+
+export default class ConditionsTablePlugin extends Phaser.Plugins.BasePlugin {
+ add(): ConditionsTable;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.js b/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.js
new file mode 100644
index 000000000..7b6fca46f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/conditionstable-plugin.js
@@ -0,0 +1,18 @@
+import ConditionsTable from './conditionstable.js'
+
+class ConditionsTablePlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add() {
+ return new ConditionsTable();
+ }
+}
+
+export default ConditionsTablePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/conditionstable.d.ts b/ui/src/phaser3-rex-plugins/plugins/conditionstable.d.ts
new file mode 100644
index 000000000..63181a014
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/conditionstable.d.ts
@@ -0,0 +1,2 @@
+import ConditionsTable from './logic/conditionstable/csvconditiontable/ConditionsTable';
+export default ConditionsTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/conditionstable.js b/ui/src/phaser3-rex-plugins/plugins/conditionstable.js
new file mode 100644
index 000000000..4ead37c12
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/conditionstable.js
@@ -0,0 +1,2 @@
+import ConditionsTable from './logic/conditionstable/csvconditiontable/ConditionsTable.js';
+export default ConditionsTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/containerlite-plugin.js b/ui/src/phaser3-rex-plugins/plugins/containerlite-plugin.js
new file mode 100644
index 000000000..bac1f94a2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/containerlite-plugin.js
@@ -0,0 +1,27 @@
+import Factory from './gameobjects/container/containerlite/Factory.js';
+import Creator from './gameobjects/container/containerlite/Creator.js';
+import ContainerLite from './gameobjects/container/containerlite/ContainerLite.js';
+import SetValue from './utils/object/SetValue.js';
+
+class ContainerLitePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexContainerLite', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ getParent(child) {
+ return ContainerLite.GetParent(child);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.ContainerLite', ContainerLite);
+
+export default ContainerLitePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/containerlite.d.ts b/ui/src/phaser3-rex-plugins/plugins/containerlite.d.ts
new file mode 100644
index 000000000..e8416ad9b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/containerlite.d.ts
@@ -0,0 +1,2 @@
+import ContainerLite from './gameobjects/container/containerlite/ContainerLite';
+export default ContainerLite;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/containerlite.js b/ui/src/phaser3-rex-plugins/plugins/containerlite.js
new file mode 100644
index 000000000..59a8eed73
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/containerlite.js
@@ -0,0 +1,2 @@
+import ContainerLite from './gameobjects/container/containerlite/ContainerLite.js';
+export default ContainerLite;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cover-plugin.js b/ui/src/phaser3-rex-plugins/plugins/cover-plugin.js
new file mode 100644
index 000000000..f9d9a37b7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cover-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shape/cover/Factory.js';
+import Creator from './gameobjects/shape/cover/Creator.js';
+import Cover from './gameobjects/shape/cover/Cover.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CoverPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCover', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.Cover', Cover);
+
+export default CoverPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cover.d.ts b/ui/src/phaser3-rex-plugins/plugins/cover.d.ts
new file mode 100644
index 000000000..55ab3b3ed
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cover.d.ts
@@ -0,0 +1,2 @@
+import Cover from './gameobjects/shape/cover/Cover.js';
+export default Cover;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cover.js b/ui/src/phaser3-rex-plugins/plugins/cover.js
new file mode 100644
index 000000000..55ab3b3ed
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cover.js
@@ -0,0 +1,2 @@
+import Cover from './gameobjects/shape/cover/Cover.js';
+export default Cover;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.d.ts
new file mode 100644
index 000000000..9a0e70b63
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.d.ts
@@ -0,0 +1,30 @@
+// import * as Phaser from 'phaser';
+import CrossStitchingPostFxPipeline from './crossstitchingpipeline';
+
+
+export default CrossStitchingPipelinePlugin;
+
+declare namespace CrossStitchingPipelinePlugin {
+
+ interface IConfig extends CrossStitchingPostFxPipeline.IConfig {
+ name?: string,
+ }
+
+}
+
+declare class CrossStitchingPipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: CrossStitchingPipelinePlugin.IConfig
+ ): CrossStitchingPostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): CrossStitchingPostFxPipeline | CrossStitchingPostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.js
new file mode 100644
index 000000000..1c8ead1b0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline-plugin.js
@@ -0,0 +1,14 @@
+import CrossStitchingPostFxPipeline from './crossstitchingpipeline.js';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CrossStitchingPipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(CrossStitchingPostFxPipeline, 'rexCrossStitchingPostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.CrossStitchingPostFx', CrossStitchingPostFxPipeline);
+
+export default CrossStitchingPipelinePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.d.ts
new file mode 100644
index 000000000..c9051a0df
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.d.ts
@@ -0,0 +1,2 @@
+import CrossStitchingPostFxPipeline from './shaders/crossstitching/CrossStitchingPostFxPipeline.js';
+export default CrossStitchingPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.js b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.js
new file mode 100644
index 000000000..c9051a0df
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/crossstitchingpipeline.js
@@ -0,0 +1,2 @@
+import CrossStitchingPostFxPipeline from './shaders/crossstitching/CrossStitchingPostFxPipeline.js';
+export default CrossStitchingPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.d.ts
new file mode 100644
index 000000000..c4a2482bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.d.ts
@@ -0,0 +1,9 @@
+import CSVScenario from './csvscenario';
+
+export default class CSVScenarioPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ scene: Phaser.Scene,
+ config?: CSVScenario.IConfig
+ ): CSVScenario;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.js b/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.js
new file mode 100644
index 000000000..b3a2b87e2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvscenario-plugin.js
@@ -0,0 +1,18 @@
+import CSVScenario from './csvscenario.js';
+
+class CSVScenarioPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new CSVScenario(scene, config);
+ }
+}
+
+export default CSVScenarioPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvscenario.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvscenario.d.ts
new file mode 100644
index 000000000..b5cbb262e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvscenario.d.ts
@@ -0,0 +1,2 @@
+import CSVScenario from './logic/runcommands/csvscenario/CSVScenario';
+export default CSVScenario;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvscenario.js b/ui/src/phaser3-rex-plugins/plugins/csvscenario.js
new file mode 100644
index 000000000..f1fbead09
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvscenario.js
@@ -0,0 +1,2 @@
+import CSVScenario from './logic/runcommands/csvscenario/CSVScenario.js';
+export default CSVScenario;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.d.ts
new file mode 100644
index 000000000..471bd0b93
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.d.ts
@@ -0,0 +1,9 @@
+import CSVToArray from './csvtoarray';
+
+export default class CSVToArrayPlugin extends Phaser.Plugins.BasePlugin {
+ convert(
+ csvString: string,
+ config?: CSVToArray.IConfig
+ ): any[][];
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.js b/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.js
new file mode 100644
index 000000000..d1f439fef
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtoarray-plugin.js
@@ -0,0 +1,18 @@
+import CSVToArray from './csvtoarray.js'
+
+class CSVToArrayPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ convert(csvString, config) {
+ return CSVToArray(csvString, config);
+ }
+}
+
+export default CSVToArrayPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtoarray.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvtoarray.d.ts
new file mode 100644
index 000000000..f99d1e698
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtoarray.d.ts
@@ -0,0 +1,2 @@
+import CSVToArray from './data/csvtoarray/CSVToArray';
+export default CSVToArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtoarray.js b/ui/src/phaser3-rex-plugins/plugins/csvtoarray.js
new file mode 100644
index 000000000..69aa8cff4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtoarray.js
@@ -0,0 +1,2 @@
+import CSVToArray from './data/csvtoarray/CSVToArray.js';
+export default CSVToArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.d.ts
new file mode 100644
index 000000000..17aae8624
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.d.ts
@@ -0,0 +1,6 @@
+import CSVToHashTable from './csvtohashtable';
+
+export default class CSVToHashTablePlugin extends Phaser.Plugins.BasePlugin {
+ add(): CSVToHashTable;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.js b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.js
new file mode 100644
index 000000000..97aa88109
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable-plugin.js
@@ -0,0 +1,18 @@
+import CSVToHashTable from './csvtohashtable.js';
+
+class CSVToHashTablePlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(config) {
+ return new CSVToHashTable(config);
+ }
+}
+
+export default CSVToHashTablePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.d.ts b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.d.ts
new file mode 100644
index 000000000..6deb1d487
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.d.ts
@@ -0,0 +1,2 @@
+import CSVToHashTable from './data/csvtohashtable/CsvToHashTable';
+export default CSVToHashTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.js b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.js
new file mode 100644
index 000000000..748db3a03
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/csvtohashtable.js
@@ -0,0 +1,2 @@
+import CSVToHashTable from './data/csvtohashtable/CsvToHashTable.js';
+export default CSVToHashTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.d.ts
new file mode 100644
index 000000000..f7f8342c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.d.ts
@@ -0,0 +1,9 @@
+import CursorAtBound from './cursoratbound';
+
+export default class CursorAtBoundPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: CursorAtBound.IConfig
+ ): CursorAtBound;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.js b/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.js
new file mode 100644
index 000000000..de774dc06
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cursoratbound-plugin.js
@@ -0,0 +1,20 @@
+import CursorAtBound from './cursoratbound.js';
+
+class CursorAtBoundPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new CursorAtBound(scene, config);
+ }
+
+}
+
+export default CursorAtBoundPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cursoratbound.d.ts b/ui/src/phaser3-rex-plugins/plugins/cursoratbound.d.ts
new file mode 100644
index 000000000..bfbdcd41f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cursoratbound.d.ts
@@ -0,0 +1,2 @@
+import CursorAtBound from './input/cursoratbound/CursorAtBound';
+export default CursorAtBound;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/cursoratbound.js b/ui/src/phaser3-rex-plugins/plugins/cursoratbound.js
new file mode 100644
index 000000000..8ebc08c13
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/cursoratbound.js
@@ -0,0 +1,2 @@
+import CursorAtBound from './input/cursoratbound/CursorAtBound.js';
+export default CursorAtBound;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.d.ts b/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.d.ts
new file mode 100644
index 000000000..18d1bc989
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.d.ts
@@ -0,0 +1,63 @@
+export default SpiralCurve;
+
+declare namespace SpiralCurve {
+ interface IConfig {
+ // Origin point
+ // Ease origin point
+ startX?: number, endX?: number, easeX?: string,
+ startY?: number, endY?: number, easeY?: string,
+ // Fixed point
+ x?: number, y?: number,
+
+ // x-radius
+ startXRadius?: number, endXRadius?: number, easeXRadius?: string,
+ // y-radius
+ startYRadiu?: number, endYRadius?: number, easeYRadius?: string,
+ // start-end radius
+ startRadius?: number, endRadiux?: number
+
+ // angle
+ startAngle?: number, endAngle?: number, easeAngle?: string,
+
+ rotation?: number
+ }
+}
+
+declare class SpiralCurve extends Phaser.Curves.Curve {
+ constructor(
+ config?: SpiralCurve.IConfig
+ );
+
+ constructor(
+ x?: number, y?: number,
+ startRadius?: number, endRadius?: number,
+ startAngle?: number, endAngle?: number,
+ rotation?: number
+ );
+
+ setStartX(x: number): this;
+ setStartY(x: number): this;
+ startX: number;
+ startY: number;
+ readonly p0: { x: number, y: number };
+
+ setEndX(x: number): this;
+ setEndY(x: number): this;
+ endX: number;
+ endY: number;
+ readonly p1: { x: number, y: number };
+
+ setStartXRadius(radius: number): this;
+ setStartYRadius(radius: number): this;
+ startXRadius: number;
+ startYRadius: number;
+ setEndXRadius(radius: number): this;
+ setEndYRadius(radius: number): this;
+ endXRadius: number;
+ endYRadius: number;
+
+ setStartAngle(degrees: number): this;
+ setEndAngle(degrees: number): this;
+ startAngle: number;
+ endAngle: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.js b/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.js
new file mode 100644
index 000000000..621eab6c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/curve/SpiralCurve.js
@@ -0,0 +1,469 @@
+const Base = Phaser.Curves.Curve;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const DegToRad = Phaser.Math.DegToRad;
+const RadToDeg = Phaser.Math.RadToDeg;
+const Vector2 = Phaser.Math.Vector2;
+const GetEaseFunction = Phaser.Tweens.Builders.GetEaseFunction;
+const Linear = Phaser.Math.Linear;
+
+class SpiralCurve extends Base {
+ constructor(x, y, startRadius, endRadius, startAngle, endAngle, rotation) {
+ var startX, endX, easeX;
+ var startY, endY, easeY;
+ var startXRadius, endXRadius, easeXRadius;
+ var startYRadius, endYRadius, easeYRadius;
+ var easeAngle;
+
+ if (typeof(x) === 'object') {
+ var config = x;
+ if (config.hasOwnProperty('x')) {
+ startX = config.x;
+ endX = startX;
+ } else {
+ startX = GetValue(config, 'startX', 0);
+ endX = GetValue(config, 'endX', startX);
+ }
+ easeX = GetValue(config, 'easeX', 'Linear');
+
+ if (config.hasOwnProperty('y')) {
+ startY = config.y;
+ endY = startY;
+ } else {
+ startY = GetValue(config, 'startY', 0);
+ endY = GetValue(config, 'endY', startY);
+ }
+ easeY = GetValue(config, 'easeY', 'Linear');
+
+ if (config.hasOwnProperty('startRadius')) {
+ startXRadius = config.startRadius;
+ startYRadius = startXRadius;
+ endXRadius = GetValue(config, 'endRadius', startXRadius);
+ endYRadius = endXRadius;
+ } else {
+ if (config.hasOwnProperty('xRadius')) {
+ startXRadius = config.xRadius;
+ endXRadius = startXRadius;
+ } else {
+ startXRadius = GetValue(config, 'startXRadius', 0);
+ endXRadius = GetValue(config, 'endXRadius', startXRadius);
+ }
+ if (config.hasOwnProperty('yRadius')) {
+ startYRadius = config.yRadius;
+ endYRadius = startYRadius;
+ } else {
+ startYRadius = GetValue(config, 'startYRadius', startXRadius);
+ endYRadius = GetValue(config, 'endYRadius', endXRadius);
+ }
+ }
+ easeXRadius = GetValue(config, 'easeXRadius', 'Linear');
+ easeYRadius = GetValue(config, 'easeXRadius', easeXRadius);
+
+ startAngle = GetValue(config, 'startAngle', 0);
+ endAngle = GetValue(config, 'endAngle', 360);
+ easeAngle = GetValue(config, 'easeAngle', 'Linear');
+ rotation = GetValue(config, 'rotation', 0);
+
+ } else {
+ if (x === undefined) { x = 0; }
+ if (y === undefined) { y = 0; }
+ if (startRadius === undefined) { startRadius = 0; }
+ if (endRadius === undefined) { endRadius = 0; }
+ if (startAngle === undefined) { startAngle = 0; }
+ if (endAngle === undefined) { endAngle = 360; }
+ if (rotation === undefined) { rotation = 0; }
+
+ startX = x;
+ endX = x;
+ easeX = 'Linear';
+ startY = y;
+ endY = y;
+ easeY = 'Linear';
+ startXRadius = startRadius;
+ endXRadius = endRadius;
+ easeXRadius = 'Linear';
+ startYRadius = startRadius;
+ endYRadius = endRadius;
+ easeYRadius = 'Linear';
+ easeAngle = 'Linear';
+ }
+
+ super('SpiralCurve');
+
+ this.p0 = new Vector2(startX, startY);
+ this.p1 = new Vector2(endX, endY);
+ this._easeX = easeX;
+ this._easeXFunction = GetEaseFunction(easeX);
+ this._easeY = easeY;
+ this._easeYFunction = GetEaseFunction(easeY);
+
+ this._startXRadius = startXRadius;
+ this._endXRadius = endXRadius;
+ this._easeXRadius = easeXRadius;
+ this._easeXRadiusFunction = GetEaseFunction(easeXRadius);
+ this._startYRadius = startYRadius;
+ this._endYRadius = endYRadius;
+ this._easeYRadius = easeYRadius;
+ this._easeYRadiusFunction = GetEaseFunction(easeYRadius);
+ this._startAngle = DegToRad(startAngle);
+ this._endAngle = DegToRad(endAngle);
+ this._easeAngle = easeAngle;
+ this._easeAngleFunction = GetEaseFunction(easeAngle);
+ this._rotation = DegToRad(rotation);
+ }
+
+ getResolution(divisions) {
+ return divisions * 2;
+ }
+
+ getStartPoint(out) {
+ return this.getPoint(0, out);
+ }
+
+ getPoint(t, out) {
+ if (out === undefined) {
+ out = new Vector2();
+ }
+
+ var ox = Linear(this.p0.x, this.p1.x, this._easeXFunction(t));
+ var oy = Linear(this.p0.y, this.p1.y, this._easeYFunction(t));
+ var angle = Linear(this._startAngle, this._endAngle, this._easeAngleFunction(t));
+ var xRadius = Linear(this._startXRadius, this._endXRadius, this._easeXRadiusFunction(t));
+ var yRadius = Linear(this._startYRadius, this._endYRadius, this._easeYRadiusFunction(t));
+ var x = ox + xRadius * Math.cos(angle);
+ var y = oy + yRadius * Math.sin(angle);
+
+ if (this._rotation !== 0) {
+ var cos = Math.cos(this._rotation);
+ var sin = Math.sin(this._rotation);
+
+ var tx = x - ox;
+ var ty = y - oy;
+
+ // Rotate the point about the center of the ellipse.
+ x = tx * cos - ty * sin + ox;
+ y = tx * sin + ty * cos + oy;
+ }
+
+ return out.set(x, y);
+ }
+
+ get x() {
+ return this.p0.x;
+ }
+
+ set x(value) {
+ var dx = value - this.p0.x;
+ this.p0.x += dx;
+ this.p1.x += dx;
+ }
+
+ get y() {
+ return this.p0.y;
+ }
+
+ set y(value) {
+ var dy = value - this.p0.y;
+ this.p0.y += dy;
+ this.p1.y += dy;
+ }
+
+ setStartX(value) {
+ this.startX = value;
+ return this;
+ }
+
+ get startX() {
+ return this.p0.x;
+ }
+
+ set startX(value) {
+ this.p0.x = value;
+ }
+
+ setEndX(value) {
+ this.endX = value;
+ return this;
+ }
+
+ get endX() {
+ return this.p1.x;
+ }
+
+ set endX(value) {
+ this.p1.x = value;
+ }
+
+ setEaseX(value) {
+ this.easeX = value;
+ return this;
+ }
+
+ get easeX() {
+ return this._easeX;
+ }
+
+ set easeX(value) {
+ this._easeX = value;
+ this._easeXFunction = GetEaseFunction(value);
+ }
+
+ get y() {
+ return this.p0.y;
+ }
+
+ set y(value) {
+ var dy = value - this.p0.y;
+ this.p0.y += dy;
+ this.p1.y += dy;
+ }
+
+ setStartY(value) {
+ this.startY = value;
+ return this;
+ }
+
+ get startY() {
+ return this.p0.y;
+ }
+
+ set startY(value) {
+ this.p0.y = value;
+ }
+
+ setEndY(value) {
+ this.endY = value;
+ return this;
+ }
+
+ get endY() {
+ return this.p1.y;
+ }
+
+ set endY(value) {
+ this.p1.y = value;
+ }
+
+ setEaseY(value) {
+ this.easeY = value;
+ return this;
+ }
+
+ get easeY() {
+ return this._easeY;
+ }
+
+ set easeY(value) {
+ this._easeY = value;
+ this._easeYFunction = GetEaseFunction(value);
+ }
+
+ setXRadius(value) {
+ this.xRadius = value;
+ return this;
+ }
+
+ get xRadius() {
+ return Math.max(this._startXRadius, this._endXRadius);
+ }
+
+ set xRadius(value) {
+ this._startXRadius = value;
+ this._endXRadius = value;
+ }
+
+ setStartXRadius(value) {
+ this.startXRadius = value;
+ return this;
+ }
+
+ get startXRadius() {
+ return this._startXRadius;
+ }
+
+ set startXRadius(value) {
+ this._startXRadius = value;
+ }
+
+ setEndXRadius(value) {
+ this.endXRadius = value;
+ return this;
+ }
+
+ get endXRadius() {
+ return this._endXRadius;
+ }
+
+ set endXRadius(value) {
+ this._endXRadius = value;
+ }
+
+ setEaseXRadius(value) {
+ this.easeXRadius = value;
+ return this;
+ }
+
+ get easeXRadius() {
+ return this._easeXRadius;
+ }
+
+ set easeXRadius(value) {
+ this._easeXRadius = value;
+ this._easeXRadiusFunction = GetEaseFunction(value);
+ }
+
+ setYRadius(value) {
+ this.startYRadius = value;
+ this.endYRadius = value;
+ return this;
+ }
+
+ get yRadius() {
+ return Math.max(this._startYRadius, this._endYRadius);
+ }
+
+ set yRadius(value) {
+ this._startYRadius = value;
+ this._endYRadius = value;
+ }
+
+ setStartYRadius(value) {
+ this.startYRadius = value;
+ return this;
+ }
+
+ get startYRadius() {
+ return this._startYRadius;
+ }
+
+ set startYRadius(value) {
+ this._startYRadius = value;
+ }
+
+ setEndYRadius(value) {
+ this.endYRadius = value;
+ return this;
+ }
+
+ get endYRadius() {
+ return this._endYRadius;
+ }
+
+ set endYRadius(value) {
+ this._endYRadius = value;
+ }
+
+ setEaseYRadius(value) {
+ this.easeYRadius = value;
+ return this;
+ }
+
+ get easeYRadius() {
+ return this._easeYRadius;
+ }
+
+ set easeYRadius(value) {
+ this._easeYRadius = value;
+ this._easeYRadiusFunction = GetEaseFunction(value);
+ }
+
+ setWidth(value) {
+ var ratio = this.xRadius / value;
+ this._startXRadius *= ratio;
+ this._endXRadius *= ratio;
+ return this;
+ }
+
+ setHeight(value) {
+ var ratio = this.yRadius / value;
+ this._startYRadius *= ratio;
+ this._endYRadius *= ratio;
+ return this;
+ }
+
+ setStartAngle(value) {
+ this.startAngle = value;
+ return this;
+ }
+
+ get startAngle() {
+ return RadToDeg(this._startAngle);
+ }
+
+ set startAngle(value) {
+ this._startAngle = DegToRad(value);
+ }
+
+ setEndAngle(value) {
+ this.endAngle = value;
+ return this;
+ }
+
+ get endAngle() {
+ return RadToDeg(this._endAngle);
+ }
+
+ set endAngle(value) {
+ this._endAngle = DegToRad(value);
+ }
+
+ setEaseAngle(value) {
+ this.easeAngle = value;
+ return this;
+ }
+
+ get easeAngle() {
+ return this._easeAngle;
+ }
+
+ set easeAngle(value) {
+ this._easeAngle = value;
+ this._easeAngleFunction = GetEaseFunction(value);
+ }
+
+ setRotation(value) {
+ this.rotation = value;
+ return this;
+ }
+
+ get angle() {
+ return RadToDeg(this._rotation);
+ }
+
+ set angle(value) {
+ this._rotation = DegToRad(value);
+ }
+
+ get rotation() {
+ return this._rotation;
+ }
+
+ set rotation(value) {
+ this._rotation = value;
+ }
+
+ toJSON() {
+ return {
+ type: this.type,
+ startX: this.p0.x,
+ startY: this.p0.y,
+ endX: this.p1.x,
+ endY: this.p1.y,
+ startXRadius: this._startXRadius,
+ endXRadius: this._endXRadius,
+ easeXRadius: this._easeXRadius,
+ startYRadius: this._startYRadius,
+ endYRadius: this._endYRadius,
+ easeYRadius: this._easeYRadius,
+ startAngle: RadToDeg(this._startAngle),
+ endAngle: RadToDeg(this._endAngle),
+ easeAngle: this._easeAngle,
+ rotation: RadToDeg(this._rotation)
+ };
+ }
+
+ static fromJSON(data) {
+ return new SpiralCurve(data);
+ }
+}
+
+export default SpiralCurve;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customprogress-plugin.js b/ui/src/phaser3-rex-plugins/plugins/customprogress-plugin.js
new file mode 100644
index 000000000..063dfa285
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customprogress-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shape/customprogress/Factory.js';
+import Creator from './gameobjects/shape/customprogress/Creator.js';
+import CustomProgress from './gameobjects/shape/customprogress/CustomProgress.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CustomProgressPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCustomProgress', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CustomProgress', CustomProgress);
+
+export default CustomProgressPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customprogress.d.ts b/ui/src/phaser3-rex-plugins/plugins/customprogress.d.ts
new file mode 100644
index 000000000..1754041d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customprogress.d.ts
@@ -0,0 +1,2 @@
+import CustomProgress from './gameobjects/shape/customprogress/CustomProgress';
+export default CustomProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customprogress.js b/ui/src/phaser3-rex-plugins/plugins/customprogress.js
new file mode 100644
index 000000000..548012a16
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customprogress.js
@@ -0,0 +1,2 @@
+import CustomProgress from './gameobjects/shape/customprogress/CustomProgress.js';
+export default CustomProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customshapes-plugin.js b/ui/src/phaser3-rex-plugins/plugins/customshapes-plugin.js
new file mode 100644
index 000000000..09a798805
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customshapes-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shape/customshapes/Factory.js';
+import Creator from './gameobjects/shape/customshapes/Creator.js';
+import CustomShapes from './gameobjects/shape/customshapes/CustomShapes.js';
+import SetValue from './utils/object/SetValue.js';
+
+class CustomShapesPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexCustomShapes', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.CustomShapes', CustomShapes);
+
+export default CustomShapesPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customshapes.d.ts b/ui/src/phaser3-rex-plugins/plugins/customshapes.d.ts
new file mode 100644
index 000000000..fdcb4897e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customshapes.d.ts
@@ -0,0 +1,2 @@
+import CustomShapes from './gameobjects/shape/customshapes/CustomShapes';
+export default CustomShapes;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/customshapes.js b/ui/src/phaser3-rex-plugins/plugins/customshapes.js
new file mode 100644
index 000000000..789996672
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/customshapes.js
@@ -0,0 +1,2 @@
+import CustomShapes from './gameobjects/shape/customshapes/CustomShapes.js';
+export default CustomShapes;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/bank/Bank.js b/ui/src/phaser3-rex-plugins/plugins/data/bank/Bank.js
new file mode 100644
index 000000000..5335584b9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/bank/Bank.js
@@ -0,0 +1,99 @@
+import GetValue from '../../utils/object/GetValue.js';
+
+class Bank {
+ constructor(config) {
+ this.nextId = GetValue(config, 'start', 1); // start index
+ this.uidKey = GetValue(config, 'uidKey', '$uid');
+ this.autoRemove = GetValue(config, 'remove', true);
+ this.refs = {};
+ this.count = 0;
+ }
+
+ add(gameObject, uid) {
+ var refs = this.refs;
+ var uidKey = this.uidKey;
+ if (uidKey) {
+ var uid = gameObject[uidKey];
+ if (uid != null) {
+ return this;
+ }
+ }
+
+ if (uid == null) {
+ do {
+ uid = this.nextId;
+ this.nextId++;
+ } while (refs.hasOwnProperty(uid))
+ }
+
+ if (!refs.hasOwnProperty(uid)) {
+ refs[uid] = gameObject;
+ this.count++;
+ if (uidKey) {
+ gameObject[uidKey] = uid;
+ }
+ if (this.autoRemove && gameObject.on) {
+ gameObject.once('destroy', function () {
+ this.remove(uid);
+ }, this)
+ }
+ } else {
+ uid = null;
+ }
+
+ if (uidKey) {
+ return this;
+ } else {
+ return uid;
+ }
+ }
+
+ addMultiple(objects) {
+ for (var i = 0, cnt = objects.length; i < cnt; i++) {
+ this.add(objects[i]);
+ }
+ return this;
+ }
+
+ get(uid) {
+ return this.refs[uid];
+ }
+
+ has(uid) {
+ return this.refs.hasOwnProperty(uid);
+ }
+
+ remove(uid) {
+ var refs = this.refs;
+ if (refs.hasOwnProperty(uid)) {
+ if (this.uidKey) {
+ var gameObject = refs[uid];
+ gameObject[this.uidKey] = undefined;
+ }
+ delete refs[uid];
+ this.count--;
+ }
+ return this;
+ }
+
+ forEach(callback, scope) {
+ var refs = this.refs,
+ gameObject;
+ for (var uid in refs) {
+ gameObject = refs[uid];
+ if (scope) {
+ callback.call(scope, gameObject, uid);
+ } else {
+ callback(gameObject, uid);
+ }
+ }
+ }
+
+ clear() {
+ this.forEach(function (gameObject) {
+ this.remove(gameObject);
+ }, this);
+ }
+}
+
+export default Bank;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.d.ts
new file mode 100644
index 000000000..399850115
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.d.ts
@@ -0,0 +1,33 @@
+export default class DataManager extends Phaser.Data.DataManager {
+ constructor(
+ parent: object,
+ eventEmitter?: Phaser.Events.EventEmitter,
+ );
+
+ setBaseValue(key: string, value: number): this;
+
+ removeBaseValue(key: string): this;
+
+ getBaseValue(key: string): number;
+
+ setBuff(
+ key: string,
+ buffKey: string,
+ value: number | string
+ ): this;
+
+ removeBuff(key: string, buffKey: string,): this;
+
+ getBuffValue(key: string, buffKey: string,): number;
+
+ setMin(key: string, min: number): this;
+
+ setMax(key: string, max: number): this;
+
+ setBounds(key: string, min: number, max: number): this;
+
+ getMinBound(key: string): number;
+
+ getMaxBound(key: string): number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.js b/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.js
new file mode 100644
index 000000000..93a5675d1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/buff/DataManager.js
@@ -0,0 +1,32 @@
+import methods from './Methods.js';
+import Extend from './Extend.js';
+
+const Base = Phaser.Data.DataManager;
+const EventEmitterKlass = Phaser.Events.EventEmitter;
+
+class DataManager extends Base {
+ constructor(parent, eventEmitter) {
+ var useDefaultEventEmitter = (eventEmitter === undefined);
+ if (useDefaultEventEmitter) {
+ eventEmitter = new EventEmitterKlass();
+ }
+
+ super(parent, eventEmitter);
+
+ if (useDefaultEventEmitter) {
+ var parentEventEmitter = (parent.events) ? parent.events : parent;
+ if (parentEventEmitter) {
+ parentEventEmitter.once('destroy', this.destroy, this);
+ }
+ }
+
+ Extend(this);
+ }
+}
+
+Object.assign(
+ DataManager.prototype,
+ methods
+);
+
+export default DataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.d.ts
new file mode 100644
index 000000000..a41266e82
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.d.ts
@@ -0,0 +1,5 @@
+import DataManager from './DataManager';
+
+export default function Extend(
+ dataManager: Phaser.Data.DataManager
+): DataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.js b/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.js
new file mode 100644
index 000000000..70bd74c95
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/buff/Extend.js
@@ -0,0 +1,15 @@
+import methods from './Methods.js';
+
+var Extend = function (dataManager) {
+ if (dataManager.buffs === undefined) {
+ dataManager.baseValues = {};
+ dataManager.buffs = {};
+ dataManager.bounds = {};
+ }
+ if (dataManager.addBuff === undefined) {
+ Object.assign(dataManager, methods);
+ }
+ return dataManager;
+}
+
+export default Extend
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/buff/Methods.js b/ui/src/phaser3-rex-plugins/plugins/data/buff/Methods.js
new file mode 100644
index 000000000..eef9a9ee4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/buff/Methods.js
@@ -0,0 +1,137 @@
+import Buff from '../../utils/buff/Buff.js';
+import MinMaxBounds from '../../utils/minmaxbounds/MinMaxBounds.js';
+
+export default {
+ setBaseValue(key, value) {
+ this.baseValues[key] = value;
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ removeBaseValue(key) {
+ if (this.baseValues.hasOwnProperty(key)) {
+ delete this.baseValues[key];
+ this.remove(key);
+ }
+ return this;
+ },
+
+ setBuff(key, buffKey, value) {
+ if (!this.buffs.hasOwnProperty(key)) {
+ this.buffs[key] = new Buff();
+ }
+ this.buffs[key].set(buffKey, value);
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ enableBuff(key, buffKey, enable) {
+ if (!this.buffs.hasOwnProperty(key)) {
+ this.buffs[key] = new Buff();
+ }
+ this.buffs[key].setEnable(buffKey, enable);
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ removeBuff(key, buffKey) {
+ if (this.buffs.hasOwnProperty(key)) {
+ if (buffKey === undefined) {
+ delete this.buffs[key];
+ } else {
+ this.buffs[key].remove(buffKey);
+ }
+ }
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ setMin(key, min) {
+ if (!this.bounds.hasOwnProperty(key)) {
+ this.bounds[key] = new MinMaxBounds();
+ }
+ this.bounds[key].setMin(min);
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ setMax(key, max) {
+ if (!this.bounds.hasOwnProperty(key)) {
+ this.bounds[key] = new MinMaxBounds();
+ }
+ this.bounds[key].setMax(max);
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ setBounds(key, min, max) {
+ if (!this.bounds.hasOwnProperty(key)) {
+ this.bounds[key] = new MinMaxBounds();
+ }
+ this.bounds[key].setMin(min).setMax(max);
+ this.set(key, this.getBuffResult(key));
+ return this;
+ },
+
+ getBuffResult(key) {
+ return this.clamp(key, this.buff(key));
+ },
+
+ buff(key, baseValue) {
+ if (baseValue === undefined) {
+ baseValue = this.getBaseValue(key);
+ }
+ if (!this.buffs.hasOwnProperty(key)) {
+ return baseValue;
+ }
+ return this.buffs[key].buff(baseValue);
+ },
+
+ clamp(key, value) {
+ if (value === undefined) {
+ value = this.list[key];
+ }
+ if (!this.bounds.hasOwnProperty(key)) {
+ return value;
+ }
+ return this.bounds[key].clamp(value);
+ },
+
+ getBaseValue(key) {
+ if (!this.baseValues.hasOwnProperty(key)) {
+ this.baseValues[key] = 0;
+ }
+ return this.baseValues[key];
+ },
+
+ getBuffs(key, buffKey) {
+ var buffs = this.buffs[key];
+ if (buffKey === undefined) {
+ return buffs;
+ }
+ if (buffs && buffs.hasOwnProperty(buffKey)) {
+ return buffs[buffKey];
+ }
+
+ return undefined;
+ },
+
+ getBuffValue(key, buffKey) {
+ return this.getBuffs(key, buffKey).value
+ },
+
+ getBounds(key) {
+ if (!this.bounds.hasOwnProperty(key)) {
+ this.bounds[key] = new MinMaxBounds();
+ }
+ return this.bounds[key];
+ },
+
+ getMinBound(key) {
+ return this.getBounds(key).min;
+ },
+
+ getMaxBound(key) {
+ return this.getBounds(key).max;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.d.ts
new file mode 100644
index 000000000..73c2f5d91
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.d.ts
@@ -0,0 +1,21 @@
+import CanvasData from "./canvasdata/CanvasData";
+
+export default CanvasObjectToBitmap;
+
+declare namespace CanvasObjectToBitmap {
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+ }
+}
+
+declare function CanvasObjectToBitmap(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: CanvasObjectToBitmap.IConfig,
+ out?: CanvasData
+): CanvasData;
+
+declare function CanvasObjectToBitmap(
+ gameObject: Phaser.GameObjects.GameObject,
+ out?: CanvasData
+): CanvasData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.js
new file mode 100644
index 000000000..5f0c7f369
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/CanvasObjectToBitmap.js
@@ -0,0 +1,26 @@
+import CanvasData from './canvasdata/CanvasData.js';
+import CanvasToData from './canvasdata/CanvasToData.js';
+import BooleanBuffer from '../../utils/arraybuffers/BooleanBuffer.js';
+import FillAlpha from './fillcallbacks/alpha.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var CanvasObjectToBitmap = function (canvasObject, config, out) {
+ if (config instanceof CanvasData) {
+ out = config;
+ config = undefined;
+ }
+
+ var x = GetValue(config, 'x', undefined);
+ var y = GetValue(config, 'y', undefined);
+ var width = GetValue(config, 'width', undefined);
+ var height = GetValue(config, 'height', undefined);
+
+ return CanvasToData(
+ canvasObject.canvas, // canvas
+ x, y, width, height, // x, y, width, height
+ BooleanBuffer, FillAlpha, undefined, // BufferClass, fillCallback, fillCallbackScope
+ out);
+};
+
+export default CanvasObjectToBitmap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/Methods.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/Methods.js
new file mode 100644
index 000000000..facc656fc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/Methods.js
@@ -0,0 +1,8 @@
+import CanvasObjectToBitmap from './CanvasObjectToBitmap.js';
+import TextureTColorMap from './TextureToColormap.js';
+
+export default {
+ textObjectToBitmap: CanvasObjectToBitmap,
+ canvasObjectToBitmap: CanvasObjectToBitmap,
+ textureTColorMap: TextureTColorMap,
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.d.ts
new file mode 100644
index 000000000..6d3494aa7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.d.ts
@@ -0,0 +1,45 @@
+import CanvasData from "./canvasdata/CanvasData";
+
+export default TextureTColorMap;
+
+declare namespace TextureTColorMap {
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+ }
+}
+
+declare function TextureTColorMap(
+ gameObject: Phaser.GameObjects.GameObject | Phaser.Textures.Frame,
+ config?: TextureTColorMap.IConfig,
+ out?: CanvasData
+): CanvasData;
+
+declare function TextureTColorMap(
+ gameObject: Phaser.GameObjects.GameObject | Phaser.Textures.Frame,
+ out?: CanvasData
+): CanvasData;
+
+declare function TextureTColorMap(
+ key: string,
+ frameName: string | null,
+ config?: TextureTColorMap.IConfig,
+ out?: CanvasData
+): CanvasData;
+
+declare function TextureTColorMap(
+ key: string,
+ frameName: string | null,
+ out?: CanvasData
+): CanvasData;
+
+declare function TextureTColorMap(
+ key: string,
+ config?: TextureTColorMap.IConfig,
+ out?: CanvasData
+): CanvasData;
+
+declare function TextureTColorMap(
+ key: string,
+ out?: CanvasData
+): CanvasData;
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.js
new file mode 100644
index 000000000..b999bde3c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/TextureToColorMap.js
@@ -0,0 +1,56 @@
+import CanvasData from './canvasdata/CanvasData.js';
+import CanvasToData from './canvasdata/CanvasToData.js';
+import ColorBuffer from '../../utils/arraybuffers/FourBytesBuffer.js';
+import FillColor from './fillcallbacks/color32.js';
+import IsGameObject from '../../utils/system/IsGameObject.js';
+import DrawFrame from '../../utils/texture/DrawFrameToCanvas.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const CanvasPool = Phaser.Display.Canvas.CanvasPool;
+
+var TextureTColorMap = function (key, frameName, config, out) {
+ var frame;
+ if (typeof (key) === 'string') {
+ if (typeof (frameName) !== 'string') {
+ out = config;
+ config = frameName;
+ frameName = undefined;
+ }
+ frame = this.textureManager.getFrame(key, frameName);
+ } else {
+ frame = (IsGameObject(key)) ? key.frame : key;
+ out = config;
+ config = frameName;
+ }
+
+ if (config instanceof CanvasData) {
+ out = config;
+ config = undefined;
+ }
+
+ var hasDefaultCanvas = (this._tmpCanvas !== undefined);
+ var canvas = (hasDefaultCanvas) ?
+ this._tmpCanvas :
+ CanvasPool.create2D(this, undefined, undefined, undefined, true);
+
+ var x = GetValue(config, 'x', undefined);
+ var y = GetValue(config, 'y', undefined);
+ var width = GetValue(config, 'width', undefined);
+ var height = GetValue(config, 'height', undefined);
+
+ out = CanvasToData(
+ DrawFrame(frame, canvas), // canvas
+ x, y, width, height, // x, y, width, height
+ ColorBuffer, FillColor, undefined, // BufferClass, fillCallback, fillCallbackScope
+ out);
+
+ if (!hasDefaultCanvas) {
+ CanvasPool.remove(canvas);
+ } else {
+ canvas.width = 1;
+ canvas.height = 1;
+ }
+ return out;
+};
+
+export default TextureTColorMap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.d.ts
new file mode 100644
index 000000000..ef1187bf5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.d.ts
@@ -0,0 +1,37 @@
+export default CanvasData;
+
+declare namespace CanvasData {
+ type ForEachCallbackType = (
+ value: number,
+ x: number, y: number,
+ canvasData: CanvasData
+ ) => void;
+
+}
+
+declare class CanvasData {
+
+ readonly width: number;
+ readonly height: number;
+
+ destroy(): void;
+
+ color32ToColorInt(value: number): number;
+
+ color32ToAlpha(value: number): number;
+
+ forEach(
+ callback: CanvasData.ForEachCallbackType,
+ scope?: object
+ ): this;
+
+ forEachNonZero(
+ callback: CanvasData.ForEachCallbackType,
+ scope?: object
+ ): this;
+
+ get(
+ x: number, y: number
+ ): boolean | number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.js
new file mode 100644
index 000000000..e0f767116
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasData.js
@@ -0,0 +1,136 @@
+import Color32Methods from '../../../utils/math/color/Color32Methods.js';
+
+class CanvasData {
+ constructor(BufferClass, width, height) {
+ if (width === undefined) {
+ width = 0;
+ }
+ if (height === undefined) {
+ height = width;
+ }
+
+ this.width = width;
+ this.height = height;
+ this.buffer = new BufferClass(width * height);
+ }
+
+ destroy() {
+ this.buffer.destroy();
+ this.buffer = undefined;
+ }
+
+ getOffset(x, y) {
+ return y * this.width + x;
+ }
+
+ get(x, y) {
+ var offset
+ if (arguments.length === 2) {
+ offset = this.getOffset(x, y);
+ } else {
+ offset = x;
+ }
+ return this.buffer.get(offset);
+ }
+
+ set(x, y, value) {
+ var offset
+ if (arguments.length === 3) {
+ offset = this.getOffset(x, y);
+ } else {
+ offset = x;
+ value = y;
+ }
+ this.buffer.set(offset, value);
+ return this;
+ }
+
+ fill(canvas, x, y, width, height, callback, scope) {
+ if (typeof (canvas) === 'number') {
+ var value = canvas;
+ this.buffer.fill(value);
+
+ } else {
+ if (x === undefined) {
+ x = 0;
+ }
+ if (y === undefined) {
+ y = 0;
+ }
+ if (width === undefined) {
+ width = canvas.width - x;
+ }
+ if (height === undefined) {
+ height = canvas.height - y;
+ }
+ this.resize(width, height);
+ var context = canvas.getContext('2d', { willReadFrequently: true });
+ var imgData = context.getImageData(x, y, width, height).data;
+ var pixels = imgData.length, imgDataIndex;
+ var value;
+ for (var i = 0, cnt = pixels / 4; i < cnt; i++) {
+ imgDataIndex = i * 4;
+ if (scope) {
+ value = callback.call(scope, imgData, imgDataIndex);
+ } else {
+ value = callback(imgData, imgDataIndex);
+ }
+ this.set(i, value);
+ }
+ }
+
+ return this;
+ }
+
+ clear() {
+ this.fill(0);
+ return this;
+ }
+
+ resize(width, height) {
+ if ((this.width === width) && (this.height === height)) {
+ return this;
+ }
+
+ this.width = width;
+ this.height = height;
+ this.buffer.resize(width * height);
+ return this;
+ }
+
+ forEach(callback, scope, skipZero) {
+ if (skipZero === undefined) {
+ skipZero = false;
+ }
+ var value;
+ for (var y = 0, h = this.height; y < h; y++) {
+ for (var x = 0, w = this.width; x < w; x++) {
+ value = this.get(x, y);
+ if (skipZero &&
+ ((value === 0) || (value === false))
+ ) {
+ continue;
+ }
+
+ if (scope) {
+ callback.call(scope, value, x, y, this);
+ } else {
+ callback(value, x, y, this);
+ }
+ }
+ }
+ return this;
+ }
+
+ forEachNonZero(callback, scope) {
+ this.forEach(callback, scope, true);
+ return this;
+ }
+};
+
+Object.assign(
+ CanvasData.prototype,
+ Color32Methods
+);
+
+export default CanvasData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasToData.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasToData.js
new file mode 100644
index 000000000..96e5ee5c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/canvasdata/CanvasToData.js
@@ -0,0 +1,23 @@
+import CanvasData from './CanvasData.js';
+
+var CanvasToData = function (canvas, x, y, width, height, BufferClass, fillCallback, fillCallbackScope, out) {
+ if (x === undefined) {
+ x = 0;
+ }
+ if (y === undefined) {
+ y = 0;
+ }
+ if (width === undefined) {
+ width = canvas.width - x;
+ }
+ if (height === undefined) {
+ height = canvas.height - y;
+ }
+ if (out === undefined) {
+ out = new CanvasData(BufferClass, width, height);
+ }
+
+ out.fill(canvas, x, y, width, height, fillCallback, fillCallbackScope);
+ return out;
+}
+export default CanvasToData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/alpha.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/alpha.js
new file mode 100644
index 000000000..5c33b7db5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/alpha.js
@@ -0,0 +1,5 @@
+var FillCallback = function (imgData, imgDataIndex) {
+ return (imgData[imgDataIndex + 3] > 0);
+}
+
+export default FillCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/color32.js b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/color32.js
new file mode 100644
index 000000000..1bf5ce0cd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/canvasdata/fillcallbacks/color32.js
@@ -0,0 +1,8 @@
+var FillCallback = function (imgData, imgDataIndex) {
+ return (imgData[imgDataIndex + 3] << 24) |
+ (imgData[imgDataIndex + 0] << 16) |
+ (imgData[imgDataIndex + 1] << 8) |
+ imgData[imgDataIndex + 2];
+};
+
+export default FillCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.d.ts
new file mode 100644
index 000000000..8bf55443a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.d.ts
@@ -0,0 +1,13 @@
+export default CSVToArray;
+
+declare namespace CSVToArray {
+ interface IConfig {
+ delimiter?: string,
+ convert?: boolean
+ }
+}
+
+declare function CSVToArray(
+ csvString: string,
+ config?: CSVToArray.IConfig
+): any[][];
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.js b/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.js
new file mode 100644
index 000000000..512af23e8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/csvtoarray/CSVToArray.js
@@ -0,0 +1,16 @@
+import CSVParser from 'papaparse/papaparse.min.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var CSVToArray = function (csvString, config) {
+ var delimiter = GetValue(config, 'delimiter', ',');
+ var convert = GetValue(config, 'convert', true);
+
+ var arr = CSVParser.parse(csvString, {
+ delimiter: delimiter,
+ dynamicTyping: convert
+ }).data;
+ return arr;
+};
+
+export default CSVToArray;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.d.ts
new file mode 100644
index 000000000..100063f58
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.d.ts
@@ -0,0 +1,125 @@
+export default CsvToHashTable;
+
+declare namespace CsvToHashTable {
+ type ConverCallbackType = (value: string, rowKey: string | number, colKey: string | number) => any;
+
+ type AppendDataCallbackType = (table: CsvToHashTable, rowKey: string | number, colKey: string | number) => any
+
+ type SortModeType = 0 | 1 | 2 | 3 | 'ascending' | 'descending' | 'logical ascending' | 'logical descending';
+
+ type SortCallbackType = (key0: string, key1: string) => number;
+
+ type EachCallbackType = (table: CsvToHashTable, rowKey: string | number, colKey: string | number, value: any) => void;
+
+ interface ILoadConfig {
+ delimiter?: string,
+ convert?: boolean | ConverCallbackType,
+ convertScope?: object
+ }
+}
+
+declare class CsvToHashTable {
+ constructor();
+
+ destroy(): void;
+
+ loadCSV(
+ csvString: string,
+ config?: CsvToHashTable.ILoadConfig
+ ): this;
+
+ convertCol(
+ colKey: string | number,
+ convertCallback?: boolean | CsvToHashTable.ConverCallbackType,
+ convertCallbackScope?: object
+ ): this;
+
+ convertRow(
+ rowKey: string | number,
+ convertCallback?: boolean | CsvToHashTable.ConverCallbackType,
+ convertCallbackScope?: object
+ ): this;
+
+ get(
+ rowKey: string | number, colKey: string | number
+ ): any;
+
+ set(
+ rowKey: string | number, colKey: string | number,
+ value: any
+ ): this;
+
+ add(
+ rowKey: string | number, colKey: string | number,
+ value: number
+ ): this;
+
+ hasRowKey(rowKey: string | number): boolean;
+
+ hasColKey(colKey: string | number): boolean;
+
+ hasKey(rowKey: string | number, colKey: string | number): boolean;
+
+ isValueInRol(rowKey: string | number, data: any): boolean;
+
+ isValueInCol(colKey: string | number, data: any): boolean;
+
+ clear(): this;
+
+ appendCol(
+ colKey: string | number,
+ initValue: any
+ ): this;
+
+ appendCol(
+ colKey: string | number,
+ callback: CsvToHashTable.AppendDataCallbackType,
+ scope?: object
+ ): this;
+
+ appendRow(
+ rowKey: string | number,
+ initValue: any
+ ): this;
+
+ appendRow(
+ rowKey: string | number,
+ callback: CsvToHashTable.AppendDataCallbackType,
+ scope?: object
+ ): this;
+
+ removeCol(colKey: string | number): this;
+
+ removeRol(rowKey: string | number): this;
+
+ sortCol(
+ colKey: string | number,
+ mode: CsvToHashTable.SortModeType
+ ): this;
+
+ sortCol(
+ callback: CsvToHashTable.SortCallbackType,
+ scope?: object
+ ): this;
+
+ sortRow(
+ rowKey: string | number,
+ mode: CsvToHashTable.SortModeType
+ ): this;
+
+ sortRow(
+ callback: CsvToHashTable.SortCallbackType,
+ scope?: object
+ ): this;
+
+ eachCol(rowKey: string | number,
+ callback: CsvToHashTable.EachCallbackType,
+ scope?: object
+ ): this;
+
+ eachRow(colKey: string | number,
+ callback: CsvToHashTable.EachCallbackType,
+ scope?: object
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.js b/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.js
new file mode 100644
index 000000000..919852f7f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/csvtohashtable/CsvToHashTable.js
@@ -0,0 +1,618 @@
+import CSVParser from 'papaparse/papaparse.min.js';
+import TypeConvert from '../../utils/string/TypeConvert.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class CsvToHashTable {
+ constructor(config) {
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ this.table = GetValue(o, 'table', {}); // 2d hash table
+ this.rowKeys = GetValue(o, 'row', []);
+ this.colKeys = GetValue(o, 'col', []);
+ this.cursor = GetValue(o, 'cursor', {});
+ return this;
+ }
+
+ toJSON() {
+ return {
+ table: this.table,
+ row: this.rowKeys,
+ col: this.colKeys,
+ cursor: this.cursor
+ };
+ }
+
+ shutdown() {
+ this.table = undefined;
+ this.rowKeys = undefined;
+ this.colKeys = undefined;
+ this.cursor = undefined;
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ loadCSV(csvString, config) {
+ var delimiter = GetValue(config, 'delimiter', ',');
+ var convert = GetValue(config, 'convert', true);
+ var convertScope = GetValue(config, 'convertScope', undefined);
+ if (!convert) {
+ convert = undefined;
+ convertScope = undefined;
+ } else if (convert === true) {
+ convert = TypeConvert;
+ convertScope = undefined;
+ }
+
+ var arr = CSVParser.parse(csvString, {
+ delimiter: delimiter
+ }).data;
+
+ var inColKeys = arr[0];
+ for (var i = 0, cnt = inColKeys.length; i < cnt; i++) {
+ var colKey = inColKeys[i];
+ if (this.colKeys.indexOf(colKey) !== -1) {
+ continue;
+ }
+ this.colKeys.push(colKey);
+ }
+
+ var inRowKeys = arr.map(function (row) { return row[0] });
+ inRowKeys.shift(); // skip 1st row
+ for (var i = 0, cnt = inRowKeys.length; i < cnt; i++) {
+ var rowKey = inRowKeys[i];
+ if (this.rowKeys.indexOf(rowKey) !== -1) {
+ continue;
+ }
+ this.rowKeys.push(rowKey);
+ }
+
+ var table = this.table;
+ var colKey, rowKey, row, value;
+
+ for (var r = 0, rcnt = inRowKeys.length; r < rcnt; r++) {
+ rowKey = inRowKeys[r];
+ if (!table.hasOwnProperty(rowKey)) {
+ table[rowKey] = {};
+ }
+ row = table[rowKey];
+ for (var c = 0, ccnt = inColKeys.length; c < ccnt; c++) {
+ value = arr[r + 1][c];
+ colKey = inColKeys[c];
+
+ if (convert) {
+ if (convertScope) {
+ value = convert.call(convertScope, value, rowKey, colKey, this);
+ } else {
+ value = convert(value, rowKey, colKey, this);
+ }
+ }
+ row[colKey] = value;
+ }
+ }
+
+ this.setCursor('', '');
+
+ return this;
+ }
+
+ clear() {
+ var table = this.table;
+ for (var key in table) {
+ delete table[key];
+ }
+ this.rowKeys.length = 0;
+ this.colKeys.length = 0;
+ return this;
+ };
+
+ get(rowKey, colKey) {
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var value = undefined;
+ var table = this.table;
+ if (table.hasOwnProperty(rowKey)) {
+ var row = table[rowKey];
+ if (row.hasOwnProperty(colKey)) {
+ value = row[colKey];
+ }
+ }
+
+ this.setCursor(rowKey, colKey);
+ return value;
+ }
+
+ set(rowKey, colKey, value) {
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var table = this.table;
+ if (table.hasOwnProperty(rowKey)) {
+ var row = table[rowKey];
+ if (row.hasOwnProperty(colKey)) {
+ row[colKey] = value;
+ }
+ }
+
+ this.setCursor(rowKey, colKey);
+ return this;
+ }
+
+ add(rowKey, colKey, value) {
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var table = this.table;
+ if (table.hasOwnProperty(rowKey)) {
+ var row = table[rowKey];
+ if (row.hasOwnProperty(colKey)) {
+ row[colKey] += value;
+ }
+ }
+
+ this.setCursor(rowKey, colKey);
+ return this;
+ }
+
+ hasRowKey(rowKey) {
+ if (typeof (rowKey) === 'number') {
+ return this.rowKeys.length > rowKey;
+ }
+
+ return (this.rowKeys.indexOf(rowKey) !== -1);
+ }
+
+ hasColKey(colKey) {
+ if (typeof (colKey) === 'number') {
+ return this.colKeys.length > colKey;
+ }
+
+ return (this.colKeys.indexOf(colKey) !== -1);
+ }
+
+ hasKey(rowKey, colKey) {
+ return this.hasRowKey(rowKey) && this.hasColKey(colKey);
+ }
+
+ isValueInRol(rowKey, value) {
+ if (!this.hasRowKey(rowKey)) {
+ return false;
+ }
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+
+ var row = this.table[rowKey];
+ var colKey, colKeys = this.colKeys;
+ for (var i = 0, len = colKeys.length; i < len; i++) {
+ colKey = colKeys[i];
+ if (row[colKey] === value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ isValueInCol(colKey, value) {
+ if (!this.hasColKey(colKey)) {
+ return false;
+ }
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var table = this.table;
+ var rowKey, rowKeys = this.rowKeys
+ for (var i = 0, len = rowKeys.length; i < len; i++) {
+ if (table[rowKey][colKey] === value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ appendRow(rowKey, callback, scope) {
+ if (this.hasRowKey(rowKey)) {
+ return this;
+ }
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+
+
+ var isCallbackMode = (typeof (callback) === 'function');
+ var initValue = (isCallbackMode) ? undefined : callback;
+
+ this.rowKeys.push(rowKey);
+ var row = {};
+ this.table[rowKey] = row;
+ var colKey, colKeys = this.colKeys,
+ value;
+ for (var i = 0, len = colKeys.length; i < len; i++) {
+ colKey = colKeys[i];
+
+ if (isCallbackMode) {
+ if (scope) {
+ value = callback.call(scope, this, rowKey, colKey);
+ } else {
+ value = callback(this, rowKey, colKey)
+ }
+ } else {
+ value = initValue;
+ }
+ row[colKey] = value;
+ }
+
+ return this;
+ }
+
+ appendCol(colKey, callback, scope) {
+ if (this.hasColKey(colKey)) {
+ return this;
+ }
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var isCallbackMode = (typeof (callback) === 'function');
+ var initValue = (isCallbackMode) ? undefined : callback;
+
+ this.colKeys.push(colKey);
+ var table = this.table;
+ var rowKey, rowKeys = this.rowKeys,
+ value;
+ for (var i = 0, len = rowKeys.length; i < len; i++) {
+ rowKey = rowKeys[i];
+
+ if (isCallbackMode) {
+ if (scope) {
+ value = callback.call(scope, this, rowKey, colKey);
+ } else {
+ value = callback(this, rowKey, colKey);
+ }
+ } else {
+ value = initValue;
+ }
+ table[rowKey][colKey] = value;
+ }
+
+ return this;
+ }
+
+ removeRol(rowKey) {
+ var idx;
+ if (typeof (rowKey) === 'number') {
+ idx = (this.rowKeys.length > rowKey) ? rowKey : -1;
+ } else {
+ idx = this.rowKeys.indexOf(rowKey);
+ }
+
+ if (idx === -1) {
+ return this;
+ }
+ this.rowKeys.splice(idx, 1);
+
+ delete this.table[rowKey];
+ return this;
+ }
+
+ removeCol(colKey) {
+ var idx;
+ if (typeof (colKey) === 'number') {
+ idx = (this.colKeys.length > colKey) ? colKey : -1;
+ } else {
+ idx = this.colKeys.indexOf(colKey);
+ }
+
+ if (idx === -1) {
+ return this;
+ }
+ this.colKeys.splice(idx, 1);
+
+ var table = this.table;
+ var rowKeys = this.rowKeys;
+ for (var i = 0, len = rowKeys.length; i < len; i++) {
+ delete table[rowKeys[i]][colKey];
+ }
+ return this;
+ }
+
+ eachRow(colKey, callback, scope) {
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ var rowKeys = this.rowKeys,
+ rowKey, value;
+ var isValidColKey = this.hasColKey(colKey);
+
+ for (var i = 0, len = rowKeys.length; i < len; i++) {
+ rowKey = rowKeys[i];
+ if (isValidColKey) {
+ value = this.get(rowKey, colKey);
+ }
+
+ if (scope) {
+ callback.call(scope, this, rowKey, colKey, value);
+ } else {
+ callback(this, rowKey, colKey, value);
+ }
+ }
+ return this;
+ }
+
+ eachCol(rowKey, callback, scope) {
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+
+ var colKeys = this.colKeys,
+ colKey, value;
+ var isValidRowKey = this.hasRowKey(rowKey);
+ for (var i = 0, len = colKeys.length; i < len; i++) {
+ colKey = colKeys[i];
+ if (isValidRowKey) {
+ value = this.get(rowKey, colKey);
+ }
+
+ if (scope) {
+ callback.call(scope, this, rowKey, colKey, value);
+ } else {
+ callback(scope, this, rowKey, colKey, value);
+ }
+ }
+ return this;
+ }
+
+ convertCol(colKey, callback, scope) {
+ if (typeof (colKey) === 'number') {
+ colKey = this.colKeys[colKey];
+ }
+
+ if (callback === undefined) {
+ callback = TypeConvert;
+ }
+
+ if (Array.isArray(colKey)) {
+ for (var i = 0, len = colKey.length; i < len; i++) {
+ this.convertCol(colKey[i], callback, scope);
+ }
+ return this;
+ }
+
+ if (!this.hasColKey(colKey)) {
+ return this;
+ }
+
+ var table = this.table,
+ row;
+ var rowKey, rowKeys = this.rowKeys,
+ value;
+ for (var r = 0, rcnt = rowKeys.length; r < rcnt; r++) {
+ rowKey = rowKeys[r];
+ row = table[rowKey];
+ value = row[colKey];
+ if (scope) {
+ value = callback.call(scope, this, rowKey, colKey, value);
+ } else {
+ value = callback(this, rowKey, colKey, value);
+ }
+
+ row[colKey] = value;
+ }
+ return this;
+ }
+
+ convertRow(rowKey, callback, scope) {
+ if (typeof (rowKey) === 'number') {
+ rowKey = this.rowKeys[rowKey];
+ }
+
+ if (callback === undefined) {
+ callback = TypeConvert;
+ }
+
+ if (Array.isArray(rowKey)) {
+ for (var i = 0, len = rowKey.length; i < len; i++) {
+ this.convertRow(rowKey[i], callback, scope);
+ }
+ return this;
+ }
+
+ var row = this.table[rowKey];
+ var colKey, colKeys = this.colKeys,
+ value;
+ for (var c = 0, ccnt = colKeys.length; c < ccnt; c++) {
+ colKey = colKeys[r];
+ value = row[colKey];
+ if (scope) {
+ value = callback.call(scope, this, rowKey, colKey, value);
+ } else {
+ value = callback(this, rowKey, colKey, value);
+ }
+
+ row[colKey] = value;
+ }
+ return this;
+
+ }
+
+ get curColKey() {
+ return this.cursor.colKey;
+ }
+
+ get curRowKey() {
+ return this.cursor.rowKey;
+ }
+
+ nextColKey(colKey, step) {
+ if (colKey === undefined) {
+ colKey = this.cursor.colKey;
+ }
+ if (step === undefined) {
+ step = 1;
+ }
+
+ var colKeys = this.colKeys;
+ var idx = colKeys.indexOf(colKey);
+ if (idx === -1) {
+ return undefined;
+ }
+ return colKeys[idx + step];
+ }
+
+ nextRowKey(rowKey, step) {
+ if (rowKey === undefined) {
+ rowKey = this.cursor.rowKey;
+ }
+ if (step === undefined) {
+ step = 1;
+ }
+
+ var rowKeys = this.rowKeys;
+ var idx = rowKeys.indexOf(rowKey);
+ if (idx === -1) {
+ return undefined;
+ }
+ return rowKeys[idx + 1];
+ }
+
+ previousColKey(colKey, step) {
+ if (step === undefined) {
+ step = 1;
+ }
+ step = -step;
+ return this.nextColKey(colKey, step);
+ }
+
+ previousRowKey(rowKey, step) {
+ if (step === undefined) {
+ step = 1;
+ }
+ step = -step;
+ return this.nextRowlKey(rowKey, step);
+ }
+
+ sortCol(callback, scope) {
+ if (typeof (callback) === 'function') {
+ if (scope) {
+ callback = callback.bind(scope);
+ }
+ } else {
+ var colKey = callback;
+ if (!this.hasColKey(colKey)) {
+ return this;
+ }
+ var mode = scope;
+ if (typeof (mode) === 'string') {
+ mode = SORTMODE[mode];
+ }
+ var table = this;
+ callback = function (rowKeyA, rowKeyB) {
+ var valA = table.get(rowKeyA, colKey);
+ var valB = table.get(rowKeyB, colKey);
+ var retVal;
+ if (mode >= 2) {
+ valA = parseFloat(valA);
+ valB = parseFloat(valB);
+ }
+ switch (mode) {
+ case 0:
+ case 2:
+ retVal = (valA > valB) ? 1 :
+ (valA < valB) ? -1 : 0;
+ break;
+
+ case 1:
+ case 3:
+ retVal = (valA < valB) ? 1 :
+ (valA > valB) ? -1 : 0;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ this.rowKeys.sort(callback);
+ return this;
+ }
+
+ sortRow(callback, scope) {
+ if (typeof (callback) === 'function') {
+ if (scope) {
+ callback = callback.bind(scope);
+ }
+ } else {
+ var rowKey = callback;
+ if (!this.hasRowKey(rowKey)) {
+ return this;
+ }
+ var mode = scope;
+ if (typeof (mode) === 'string') {
+ mode = SORTMODE[mode];
+ }
+ var table = this;
+ callback = function (colKeyA, colKeyB) {
+ var valA = table.get(rowKey, colKeyA);
+ var valB = table.get(rowKey, colKeyB);
+ var retVal;
+ if (mode >= 2) {
+ valA = parseFloat(valA);
+ valB = parseFloat(valB);
+ }
+ switch (mode) {
+ case 0:
+ case 2:
+ retVal = (valA > valB) ? 1 :
+ (valA < valB) ? -1 : 0;
+ break;
+
+ case 1:
+ case 3:
+ retVal = (valA < valB) ? 1 :
+ (valA > valB) ? -1 : 0;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ this.colKeys.sort(callback);
+ return this;
+ }
+
+ setCursor(rowKey, colKey) {
+ var cursor = this.cursor;
+ cursor.rowKey = rowKey;
+ cursor.colKey = colKey;
+ return this;
+ }
+
+}
+
+const SORTMODE = {
+ 'ascending': 0,
+ 'descending': 1,
+ 'logical ascending': 2,
+ 'logical descending': 3
+}
+export default CsvToHashTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.d.ts
new file mode 100644
index 000000000..060cb80a1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.d.ts
@@ -0,0 +1,11 @@
+export default AppendData;
+
+type BasicDataType = number | string;
+type DictDataType = { [key: string]: BasicDataType } | { [key: string]: DictDataType } | { [key: string]: ListDateType };
+type ListDateType = (BasicDataType | ListDateType | DictDataType)[];
+type DataType = BasicDataType | DictDataType | ListDateType;
+
+declare function AppendData(
+ pngBuffer: Uint8Array,
+ data: DataType | Uint8Array,
+): Uint8Array;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.js b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.js
new file mode 100644
index 000000000..b402128f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/AppendData.js
@@ -0,0 +1,41 @@
+import GetChunkEndByteIndex from './GetChunkEndByteIndex.js';
+import Uint8ArrayWriter from '../../utils/arraybuffers/Uint8ArrayWriter.js';
+
+var AppendData = function (pngBuffer, data) {
+ // Get End of last png chunk (IEND)
+ var pngByteLength = GetChunkEndByteIndex(pngBuffer, 'IEND');
+
+ var isUint8Array = (typeof (obj) === 'object') && (obj.constructor === Uint8Array);
+ var dataType = (isUint8Array) ? 1 : 0;
+ var header0 = dataType;
+ var header1 = 0;
+
+ var dataUint8Array;
+ if (isUint8Array) {
+ dataUint8Array = data;
+ } else {
+ if (data != null) {
+ // JSON -> string -> Uint8Array
+ data = JSON.stringify(data);
+ dataUint8Array = (new TextEncoder()).encode(data);
+ } else {
+ dataUint8Array = new Uint8Array(0);
+ }
+ }
+
+ // Append dataUint8Array after png-chunks
+ var outputLength = pngByteLength + 8 + dataUint8Array.length;
+ var writer = (new Uint8ArrayWriter(outputLength))
+ // png-buffer
+ .writeUint8Array(pngBuffer.slice(0, pngByteLength))
+ // header0: dataType
+ .writeUint32(header0)
+ // header1: 0x0
+ .writeUint32(header1)
+ // myData
+ .writeUint8Array(dataUint8Array)
+
+ return writer.buf;
+}
+
+export default AppendData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.d.ts
new file mode 100644
index 000000000..7c0f9c04b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.d.ts
@@ -0,0 +1,10 @@
+export default ExtractData;
+
+type BasicDataType = number | string;
+type DictDataType = { [key: string]: BasicDataType } | { [key: string]: DictDataType } | { [key: string]: ListDateType };
+type ListDateType = (BasicDataType | ListDateType | DictDataType)[];
+type DataType = BasicDataType | DictDataType | ListDateType;
+
+declare function ExtractData(
+ pngBuffer: Uint8Array,
+): DataType | Uint8Array;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.js b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.js
new file mode 100644
index 000000000..35ec4b284
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/ExtractData.js
@@ -0,0 +1,33 @@
+import GetChunkEndByteIndex from './GetChunkEndByteIndex.js';
+import Uint8ArrayReader from '../../utils/arraybuffers/Uint8ArrayReader.js';
+
+var ExtractData = function (pngBuffer) {
+ var reader = new Uint8ArrayReader(pngBuffer);
+
+ // Get End of last png chunk (IEND)
+ var pngByteLength = GetChunkEndByteIndex(reader, 'IEND');
+ reader.seek(pngByteLength);
+ if (reader.outOfArray) {
+ return null;
+ }
+
+ // Get header0, header1
+ var header0 = reader.readUint32();
+ var dataType = header0 & 0xf;
+ var header1 = reader.readUint32();
+ // Get myData
+ var data = reader.readUint8Array();
+ if (dataType === 0) {
+ if (data.length === 0) {
+ return null;
+ } else {
+ // Uint8Array -> string -> JSON
+ data = (new TextDecoder()).decode(data);
+ data = JSON.parse(data);
+ }
+ }
+
+ return data;
+}
+
+export default ExtractData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pngappender/GetChunkEndByteIndex.js b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/GetChunkEndByteIndex.js
new file mode 100644
index 000000000..61eb81499
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pngappender/GetChunkEndByteIndex.js
@@ -0,0 +1,33 @@
+import Uint8ArrayReader from '../../utils/arraybuffers/Uint8ArrayReader.js';
+
+var GetChunkEndByteIndex = function (pngBuffer, chunkType) {
+ var reader;
+ if (pngBuffer instanceof (Uint8ArrayReader)) {
+ reader = pngBuffer;
+ } else {
+ reader = new Uint8ArrayReader(pngBuffer);
+ }
+
+ reader.seek(8); // Skip png header
+ while (!reader.outOfArray) {
+ var dataLength = reader.readUint32(true);
+ if (chunkType === reader.readString(4)) {
+ return reader.pointer + dataLength + 4;
+ } else {
+ reader.seekForward(dataLength + 4);
+ }
+ }
+
+ return -1;
+}
+
+/*
+Chunk structure:
+
+- dataLength: 4 bytes -> uint32 (big-endian)
+- chunkType: 4 bytes -> string
+- data: dataLength bytes -> unit8Array
+- crc: 4 bytes -> uint32 (big-endian)
+*/
+
+export default GetChunkEndByteIndex;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/pool/ObjectPool.js b/ui/src/phaser3-rex-plugins/plugins/data/pool/ObjectPool.js
new file mode 100644
index 000000000..a730c78fc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/pool/ObjectPool.js
@@ -0,0 +1,21 @@
+import Pool from '../../pool.js';
+
+class ObjectPool extends Pool {
+ shutdown() {
+ var items = this.items,
+ item;
+ for (var i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ if (item.destroy) { // Assume that object has destroy function
+ item.destroy();
+ }
+ }
+ items.length = 0;
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+}
+
+export default ObjectPool;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.d.ts
new file mode 100644
index 000000000..fd1421451
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.d.ts
@@ -0,0 +1,31 @@
+export default DataManager;
+
+declare class DataManager extends Phaser.Data.DataManager {
+ constructor(
+ parent: object,
+ eventEmitter?: Phaser.Events.EventEmitter
+ );
+
+ constructor(
+ parent: object,
+ eventEmitter?: Phaser.Events.EventEmitter,
+ config?: object
+ );
+
+ commit(alias?: string): this;
+
+ restore(
+ version?: string | number,
+ restoreFromVersion0?: boolean
+ ): this;
+
+ set version(value: string | number);
+ get version(): number;
+ readonly lastVersion: number;
+
+ readonly versionAlias: string;
+ readonly versionAliases: string[];
+
+ toJSON(): object;
+ resetFromJSON(o?: object): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.js b/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.js
new file mode 100644
index 000000000..8ac4f15cd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/restorabledata/DataManager.js
@@ -0,0 +1,219 @@
+import Clear from '../../utils/object/Clear.js';
+
+const Base = Phaser.Data.DataManager;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const EventEmitterKlass = Phaser.Events.EventEmitter;
+
+class DataManager extends Base {
+ constructor(parent, eventEmitter, config) {
+ var useDefaultEventEmitter = (eventEmitter === undefined);
+ if (useDefaultEventEmitter) {
+ eventEmitter = new EventEmitterKlass();
+ }
+
+ super(parent, eventEmitter);
+
+ if (useDefaultEventEmitter) {
+ var parentEventEmitter = (parent.events) ? parent.events : parent;
+ if (parentEventEmitter) {
+ parentEventEmitter.once('destroy', this.destroy, this);
+ }
+ }
+
+ this._recordEnable = true;
+ this.resetFromJSON(config);
+
+ this.events
+ .on('changedata', this.onValueChange, this)
+ .on('setdata', function (parent, key, value) {
+ this.onValueChange(parent, key, value, null);
+ }, this)
+ .on('removedata', function (parent, key, value) {
+ this.onValueChange(parent, key, null, value);
+ }, this);
+ }
+
+ resetFromJSON(o) {
+ this._version = GetValue(o, 'version', 0);
+ this._versionAlias = GetValue(o, 'versionAlias', '');
+ this._repository = GetValue(o, 'repository', []);
+ this._versionAliases = GetValue(o, 'versionAliases', {});
+ var changeList = GetValue(o, 'changeList', {});
+
+ var data = GetValue(o, 'data', undefined);
+ if (data) {
+ this._recordEnable = false;
+ this.set(data);
+ this._recordEnable = true;
+ } else {
+ // Restore from version 0 to current version
+ var currentVersion = (this._versionAlias !== '') ? this._versionAlias : this._version;
+ this._version = 0;
+ this.restore(currentVersion);
+ // Restore change list
+ this._recordEnable = false;
+ for (var key in changeList) {
+ this.setValue(key, changeList[key][0]);
+ }
+ this._recordEnable = true;
+ }
+
+ this._changeList = changeList;
+ }
+
+ toJSON(includeData) {
+ if (includeData === undefined) {
+ includeData = false;
+ }
+ var o = {
+ version: this._version,
+ versionAlias: this._versionAlias,
+ changeList: this._changeList,
+ repository: this._repository,
+ versionAliases: this._versionAliases,
+ };
+ if (includeData) {
+ o.data = this.list;
+ }
+ return o;
+ }
+
+ get version() {
+ return this._version;
+ }
+
+ set version(value) {
+ var alias;
+ if (typeof (value) === 'string') {
+ alias = value;
+ value = this._versionAliases[value];
+ }
+ if (typeof (value) !== 'number') {
+ this._versionAlias = '';
+ return;
+ }
+
+ this._versionAlias = (alias) ? alias : '';
+ if (value === 0) {
+ this._recordEnable = false;
+ super.reset();
+ this._version = 0;
+ Clear(this._changeList);
+ this._recordEnable = true;
+ return;
+ }
+
+ value = Math.min(value, this._repository.length);
+
+ var changeList, merged = {};
+ // Reverse current change
+ for (var key in this._changeList) {
+ merged[key] = this._changeList[key][1];
+ delete this._changeList[key];
+ }
+
+ if (this._version === value) {
+ // Do nothing
+ } else if (this._version < value) {
+ // Forward
+ for (var i = this._version; i < value; i++) {
+ changeList = this._repository[i];
+ for (var key in changeList) {
+ merged[key] = changeList[key][0];
+ }
+ }
+ } else {
+ // Backward
+ for (var i = this._version - 1; i >= value; i--) {
+ changeList = this._repository[i];
+ for (var key in changeList) {
+ merged[key] = changeList[key][1];
+ }
+ }
+ }
+
+ this._version = value;
+ var value;
+ this._recordEnable = false;
+ for (var key in merged) {
+ value = merged[key];
+ if (value === null) {
+ this.removeValue(key);
+ } else {
+ this.setValue(key, value);
+ }
+ }
+ this._recordEnable = true;
+ }
+
+ get versionAlias() {
+ return this._versionAlias;
+ }
+
+ get lastVersion() {
+ return this._repository.length;
+ }
+
+ get versionAliases() {
+ var aliases = [];
+ for (var name in this._versionAliases) {
+ aliases.push(name);
+ }
+ return aliases;
+ }
+
+ commit(alias) {
+ this._repository.length = this._version;
+ for (var name in this._versionAliases) {
+ if (this._versionAliases[name] > this._version) {
+ delete this._versionAliases[name];
+ }
+ }
+
+ this._repository.push(this._changeList);
+ this._changeList = {};
+ this._version++;
+
+ if (typeof (alias) === 'string') {
+ this._versionAlias = alias;
+ this._versionAliases[alias] = this._version;
+ }
+ return this;
+ }
+
+ restore(value, restoreFromVersion0) {
+ if (value === undefined) {
+ value = (this._versionAlias !== '') ? this._versionAlias : this._version;
+ }
+ if (restoreFromVersion0 === undefined) {
+ restoreFromVersion0 = false;
+ }
+
+ if (restoreFromVersion0) {
+ this.version = 0;
+ }
+ this.version = value;
+ return this;
+ }
+
+ reset() {
+ this.restore(0);
+ this._repository.length = 0;
+ Clear(this._versionAliases);
+ return this;
+ }
+
+ onValueChange(parent, key, value, previousValue) {
+ if (!this._recordEnable) {
+ return;
+ }
+
+ if (this._changeList.hasOwnProperty(key)) {
+ this._changeList[key][0] = value;
+ } else {
+ this._changeList[key] = [value, previousValue];
+ // [newData, previousData]
+ }
+ }
+}
+export default DataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ArrayMethods.js b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ArrayMethods.js
new file mode 100644
index 000000000..683d170c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ArrayMethods.js
@@ -0,0 +1,201 @@
+import SpliceOne from '../../utils/array/SpliceOne.js';
+import RandomBetween from '../../utils/math/Between.js';
+import Shuffle from '../../utils/array/Shuffle.js';
+import Clone from '../../utils/object/Clone.js';
+
+export default {
+ isEmpty() {
+ return (this.items.length === 0);
+ },
+
+ get(index) {
+ return this.items[index];
+ },
+
+ getFirst() {
+ return this.items[0];
+ },
+
+ getLast() {
+ return this.items[this.items.length - 1];
+ },
+
+ getRandom() {
+ var index = RandomBetween(0, this.items.length - 1);
+ return this.items[index];
+ },
+
+ add(item, index, moveToNewPosition) {
+ var currentIndex = this.items.indexOf(item);
+ if (currentIndex !== -1) {
+ if (moveToNewPosition && (index !== currentIndex)) {
+ this.remove(undefined, currentIndex);
+ this.add(item, index);
+ }
+ return this;
+ }
+
+ if ((index === undefined) || (index >= this.items.length)) {
+ this.items.push(item);
+ } else {
+ this.items.splice(index, 0, item);
+ }
+
+ this.addDestroyCallback(item);
+
+ return this;
+ },
+
+ addFirst(item, moveToNewPosition) {
+ this.add(item, 0, moveToNewPosition);
+ return this;
+ },
+
+ addLast(item, moveToNewPosition) {
+ this.add(item, undefined, moveToNewPosition);
+ return this;
+ },
+
+ addMultiple(items, index, moveToNewPosition) {
+ if (index === undefined) {
+ for (var i = 0, cnt = items.length; i < cnt; i++) {
+ this.add(items[i]);
+ }
+ } else {
+ for (var i = 0, cnt = items.length; i < cnt; i++) {
+ if (this.contains(items[i])) {
+ continue;
+ }
+ this.add(items[i], index, moveToNewPosition);
+ index++;
+ }
+ }
+ return this;
+ },
+
+ remove(item, index) {
+ if (item) {
+ index = this.items.indexOf(item);
+ if (index === -1) {
+ return this;
+ }
+ } else {
+ item = this.items[index];
+ if (!item) {
+ return this;
+ }
+ }
+
+
+ if (index === (this.items.length - 1)) {
+ this.items.length -= 1;
+ } else {
+ SpliceOne(this.items, index);
+ }
+
+ this.removeDestroyCallback(item);
+
+ return this;
+ },
+
+ onChildDestroy(child, fromScene) {
+ this.remove(child);
+ },
+
+ removeFirst() {
+ this.remove(undefined, 0);
+ return this;
+ },
+
+ removeLast() {
+ this.remove(undefined, (this.item.length - 1));
+ return this;
+ },
+
+ removeMultiple(items) {
+ for (var i = items.length; i > 0; i--) {
+ this.remove(items[i - 1]);
+ }
+ return this;
+ },
+
+ clear(destroyItems) {
+ var items;
+ if (destroyItems) {
+ items = this.cloneItems();
+ }
+
+ this.removeDestroyCallback(this.items);
+ this.items.length = 0;
+
+ if (destroyItems) {
+ for (var i = items.length; i > 0; i--) {
+ items[i].destroy();
+ }
+ }
+ return this;
+ },
+
+ clone(out) {
+ if (out === this) {
+ return this;
+ } else if (out === undefined) {
+ out = this.newList();
+ }
+
+ out.clear();
+ Clone(this.items, out.items);
+ out.addDestroyCallback(out.items)
+ return out;
+ },
+
+ pop(index) {
+ if (index === undefined) {
+ index = 0;
+ }
+
+ var item = this.items[index];
+ this.remove(undefined, index);
+ return item;
+ },
+
+ popFirst() {
+ return this.pop(0);
+ },
+
+ popLast() {
+ return this.pop(this.items.length - 1);
+ },
+
+ popRandom() {
+ var index = RandomBetween(0, this.items.length - 1);
+ return this.pop(index);
+ },
+
+ slice(start, end, out) {
+ var result = this.items.slice(start, (end + 1));
+
+ if (out === undefined) {
+ out = this.newList();
+ }
+ out.clear();
+ Clone(result, out.items);
+ out.addDestroyCallback(out.items);
+ return out;
+ },
+
+ reverse() {
+ this.items.reverse();
+ return this;
+ },
+
+ sort(callback) {
+ this.items.sort(callback);
+ return this;
+ },
+
+ shuffle() {
+ Shuffle(this.items);
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ContainMethods.js b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ContainMethods.js
new file mode 100644
index 000000000..af7033b5d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/ContainMethods.js
@@ -0,0 +1,25 @@
+export default {
+ contains(item) {
+ return (this.items.indexOf(item) !== -1);
+ },
+
+ any(listB) {
+ var items = (this.isList(listB)) ? listB.items : listB;
+ for (var i = 0, cnt = items; i < cnt; i++) {
+ if (this.contains(items[i])) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ all(listB) {
+ var items = (this.isList(listB)) ? listB.items : listB;
+ for (var i = 0, cnt = items; i < cnt; i++) {
+ if (!this.contains(items[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/DestroyCallbackMethods.js b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/DestroyCallbackMethods.js
new file mode 100644
index 000000000..fb28a54bb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/DestroyCallbackMethods.js
@@ -0,0 +1,49 @@
+import IsArray from '../../utils/object/IsArray.js';
+
+export default {
+ setAutoCleanupEnable(enabled) {
+ if (enabled === undefined) {
+ enabled = true;
+ }
+ this.autoCleanupEnable = enabled;
+ return this;
+ },
+
+ addDestroyCallback(gameObject) {
+ if ((!gameObject) || (!this.autoCleanupEnable)) {
+ return this;
+ }
+
+ if (IsArray(gameObject)) {
+ var gameObjects = gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ this.addDestroyCallback(gameObjects[i]);
+ }
+ return this;
+ }
+
+ if (gameObject.on) {
+ gameObject.once('destroy', this.onChildDestroy, this);
+ }
+ return this;
+ },
+
+ removeDestroyCallback(gameObject) {
+ if ((!gameObject) || (!this.autoCleanupEnable)) {
+ return this;
+ }
+
+ if (IsArray(gameObject)) {
+ var gameObjects = gameObject;
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ this.removeDestroyCallback(gameObjects[i]);
+ }
+ return this;
+ }
+
+ if (gameObject.off) {
+ gameObject.off('destroy', this.onChildDestroy, this);
+ }
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/SetMethods.js b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/SetMethods.js
new file mode 100644
index 000000000..5459dd7a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/SetMethods.js
@@ -0,0 +1,126 @@
+import Clone from "../../utils/object/Clone";
+
+export default {
+ union(listB, out) {
+ if (this === listB) {
+ if (this !== out) {
+ out = this.clone(out);
+ }
+ } else if (this === out) {
+ this.addMultiple(listB.items);
+ } else if (listB === out) {
+ listB.addMultiple(this.items);
+ } else {
+ if (this.items.length >= listB.items.length) {
+ out = this.clone(out);
+ out.addMultiple(listB.items);
+ } else {
+ out = listB.clone(out);
+ out.addMultiple(this.items);
+ }
+ }
+ return out;
+ },
+
+ intersect(listB, out) {
+ if (this === listB) {
+ if (this !== out) {
+ out = this.clone(out);
+ }
+ } else if (this === out) {
+ var itemsA = Clone(this.items);
+ this.clear();
+
+ var item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsA[i];
+ if (listB.contains(item)) {
+ this.add(item);
+ }
+ }
+ } else if (listB === out) {
+ var itemsB = Clone(listB.items);
+ listB.clear();
+
+ var item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsB[i];
+ if (this.contains(item)) {
+ listB.add(item);
+ }
+ }
+ } else {
+ out = this.newList();
+ if (this.items.length >= listB.items.length) {
+ var itemsB = listB.items, item;
+ for (var i = 0, cnt = itemsB.length; i < cnt; i++) {
+ item = itemsB[i];
+ if (this.contains(item)) {
+ out.add(item);
+ }
+ }
+ } else {
+ var itemsA = this.items, item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsA[i];
+ if (listB.contains(item)) {
+ out.add(item);
+ }
+ }
+ }
+ }
+ return out;
+ },
+
+ difference(listB, out) {
+ if (this === listB) {
+ if (this === out) {
+ this.clear();
+ } else {
+ out = this.newList();
+ }
+ } else if (this === out) {
+ var itemsA = Clone(this.items);
+ this.clear();
+
+ var item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsA[i];
+ if (!listB.contains(item)) {
+ this.add(item);
+ }
+ }
+ } else if (listB === out) {
+ var itemsB = Clone(listB.items);
+ listB.clear();
+
+ var item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsB[i];
+ if (!this.contains(item)) {
+ listB.add(item);
+ }
+ }
+ } else {
+ out = this.newList();
+ if (this.items.length >= listB.items.length) {
+ var itemsB = listB.items, item;
+ for (var i = 0, cnt = itemsB.length; i < cnt; i++) {
+ item = itemsB[i];
+ if (!this.contains(item)) {
+ out.add(item);
+ }
+ }
+ } else {
+ var itemsA = this.items, item;
+ for (var i = 0, cnt = itemsA.length; i < cnt; i++) {
+ item = itemsA[i];
+ if (!listB.contains(item)) {
+ out.add(item);
+ }
+ }
+ }
+ }
+ return out;
+ },
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.d.ts b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.d.ts
new file mode 100644
index 000000000..6821cdf2d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.d.ts
@@ -0,0 +1,116 @@
+export default UniqueItemList;
+
+declare namespace UniqueItemList {
+ interface IConfig {
+ items?: any[],
+ autoCleanup?: boolean,
+ }
+}
+
+declare class UniqueItemList {
+ constructor(
+ config?: UniqueItemList.IConfig
+ );
+
+ constructor(
+ items?: any[]
+ );
+
+ getFirst(): ItemType;
+
+ getLast(): ItemType;
+
+ get(index: number): ItemType;
+
+ getRandom(): ItemType;
+
+ getItems(): ItemType[];
+
+ cloneItems(): ItemType[];
+
+ readonly length: number;
+
+ isEmpty(): boolean;
+
+ contains(item: ItemType): boolean;
+
+ any(listB: UniqueItemList): boolean;
+
+ all(listB: UniqueItemList): boolean;
+
+ add(
+ item: ItemType,
+ index?: number,
+ moveToNewPosition?: boolean
+ ): this;
+
+ addLast(item: ItemType): this;
+
+ addFirst(item: ItemType): this;
+
+ addMultiple(items: ItemType[]): this;
+
+ clone(out?: UniqueItemList): UniqueItemList;
+
+ remove(item: ItemType): this;
+
+ remove(
+ item: undefined | null | false,
+ index: number
+ ): this;
+
+ removeFirst(): this;
+
+ removeLast(): this;
+
+ removeMultiple(items: ItemType[]): this;
+
+ clear(destroyItems?: boolean): this;
+
+ pop(index?: number): ItemType;
+
+ popFirst(): ItemType;
+
+ popLast(): ItemType;
+
+ popRandom(): ItemType;
+
+ slice(
+ startIndex: number,
+ endIndex: number,
+ out?: UniqueItemList
+ ): UniqueItemList;
+
+ sort(
+ callback: (itemA: ItemType, itemB: ItemType) => number
+ ): this;
+
+ reverse(): this;
+
+ shuffle(): this;
+
+ union(
+ listB: UniqueItemList,
+ out?: UniqueItemList
+ ): UniqueItemList;
+
+ intersect(
+ listB: UniqueItemList,
+ out?: UniqueItemList
+ ): UniqueItemList;
+
+ difference(
+ listB: UniqueItemList,
+ out?: UniqueItemList
+ ): UniqueItemList;
+
+ call(
+ callback: (item: ItemType, index: number) => void,
+ scope?: object
+ ): this;
+
+ call(
+ fnName: string, ...args: any
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.js b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.js
new file mode 100644
index 000000000..6f575ecf9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/data/uniqueitemlist/UniqueItemList.js
@@ -0,0 +1,90 @@
+import IsPlainObject from '../../utils/object/IsPlainObject.js';
+import GetValue from '../../utils/object/GetValue.js';
+import DestroyCallbackMethods from './DestroyCallbackMethods.js';
+import ContainMethods from './ContainMethods.js';
+import ArrayMethods from './ArrayMethods.js';
+import SetMethods from './SetMethods.js';
+import Clone from '../../utils/object/Clone.js';
+import ArrayCopy from '../../utils/array/Copy.js';
+
+class UniqueItemList {
+ constructor(items, config) {
+ if (IsPlainObject(items)) {
+ config = items;
+ items = GetValue(config, 'items', undefined);
+ }
+
+ this.items = [];
+ this.setAutoCleanupEnable(GetValue(config, 'autoCleanup', true));
+ if (items) {
+ this.addMultiple(items);
+ }
+ }
+
+ destroy(destroyItems) {
+ this.clear(destroyItems);
+ this.items = undefined;
+ }
+
+ getItems() {
+ return this.items;
+ }
+
+ cloneItems(out) {
+ return Clone(this.items, out);
+ }
+
+ isList(item) {
+ return (item instanceof UniqueItemList);
+ }
+
+ newList(items) {
+ var config = {
+ autoCleanup: this.autoCleanupEnable
+ }
+ return new UniqueItemList(items, config);
+ }
+
+ get length() {
+ return this.items.length;
+ }
+
+ call(callback, scope) {
+ if (this.items.length === 0) {
+ return this;
+ }
+
+ if (typeof (callback) === 'string') {
+ var fnName = callback;
+ ArrayCopy(ARGS, arguments, 1);
+ var item;
+ for (var i = 0, cnt = this.items.length; i < cnt; i++) {
+ item = this.items[i];
+ item[fnName].apply(item, ARGS);
+ }
+ ARGS.length = 0;
+
+ } else {
+ for (var i = 0, cnt = this.items.length; i < cnt; i++) {
+ if (scope) {
+ callback.call(scope, this.items[i], i);
+ } else {
+ callback(this.items[i], i);
+ }
+ }
+ }
+ return this;
+ }
+}
+
+var ARGS = []; // reuse this array
+
+Object.assign(
+ UniqueItemList.prototype,
+ DestroyCallbackMethods,
+ ContainMethods,
+ ArrayMethods,
+ SetMethods
+)
+
+export default UniqueItemList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.d.ts
new file mode 100644
index 000000000..f2832149b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.d.ts
@@ -0,0 +1,29 @@
+// import * as Phaser from 'phaser';
+import DissolvePostFxPipeline from './dissolvepipeline';
+
+export default DissolvePipelinePlugin;
+
+declare namespace DissolvePipelinePlugin {
+
+ interface IConfig extends DissolvePostFxPipeline.IConfig {
+ name?: string,
+ }
+
+}
+
+declare class DissolvePipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: DissolvePipelinePlugin.IConfig
+ ): DissolvePostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): DissolvePostFxPipeline | DissolvePostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.js
new file mode 100644
index 000000000..fb75ff573
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline-plugin.js
@@ -0,0 +1,14 @@
+import DissolvePostFxPipeline from './dissolvepipeline.js';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class DissolvePipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(DissolvePostFxPipeline, 'rexDissolvePostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.DissolvePostFx', DissolvePostFxPipeline);
+
+export default DissolvePipelinePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.d.ts
new file mode 100644
index 000000000..c577a4361
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.d.ts
@@ -0,0 +1,2 @@
+import DissolvePostFxPipeline from './shaders/dissolve/DissolvePostFxPipeline';
+export default DissolvePostFxPipeline;
diff --git a/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.js b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.js
new file mode 100644
index 000000000..db1625e89
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dissolvepipeline.js
@@ -0,0 +1,2 @@
+import DissolvePostFxPipeline from './shaders/dissolve/DissolvePostFxPipeline.js';
+export default DissolvePostFxPipeline;
diff --git a/ui/src/phaser3-rex-plugins/plugins/drag-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/drag-plugin.d.ts
new file mode 100644
index 000000000..60b18cfb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/drag-plugin.d.ts
@@ -0,0 +1,9 @@
+import Drag from './drag';
+
+export default class DragPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Drag.IConfig
+ ): Drag;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/drag-plugin.js b/ui/src/phaser3-rex-plugins/plugins/drag-plugin.js
new file mode 100644
index 000000000..8a387d259
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/drag-plugin.js
@@ -0,0 +1,20 @@
+import Drag from './drag.js';
+
+class DragPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Drag(gameObject, config);
+ }
+
+}
+
+export default DragPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/drag.d.ts b/ui/src/phaser3-rex-plugins/plugins/drag.d.ts
new file mode 100644
index 000000000..ba2f083ff
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/drag.d.ts
@@ -0,0 +1,2 @@
+import Drag from './input/drag/Drag';
+export default Drag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/drag.js b/ui/src/phaser3-rex-plugins/plugins/drag.js
new file mode 100644
index 000000000..5597795e2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/drag.js
@@ -0,0 +1,2 @@
+import Drag from './input/drag/Drag.js';
+export default Drag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.d.ts
new file mode 100644
index 000000000..68538c3d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.d.ts
@@ -0,0 +1,9 @@
+import DragRotate from './dragrotate';
+
+export default class DragRotatePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: DragRotate.IConfig
+ ): DragRotate;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.js
new file mode 100644
index 000000000..beb645827
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragrotate-plugin.js
@@ -0,0 +1,20 @@
+import DragRotate from './dragrotate.js';
+
+class DragRotatePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(scene, config) {
+ return new DragRotate(scene, config);
+ }
+
+}
+
+export default DragRotatePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragrotate.d.ts b/ui/src/phaser3-rex-plugins/plugins/dragrotate.d.ts
new file mode 100644
index 000000000..b0bb841a6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragrotate.d.ts
@@ -0,0 +1,2 @@
+import DragRotate from './input/dragrotate/DragRotate';
+export default DragRotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragrotate.js b/ui/src/phaser3-rex-plugins/plugins/dragrotate.js
new file mode 100644
index 000000000..3aab549ac
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragrotate.js
@@ -0,0 +1,2 @@
+import DragRotate from './input/dragrotate/DragRotate.js';
+export default DragRotate;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragspeed-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dragspeed-plugin.js
new file mode 100644
index 000000000..4278f58f6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragspeed-plugin.js
@@ -0,0 +1,20 @@
+import DragSpeed from './dragspeed.js';
+
+class DragSpeedPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new DragSpeed(gameObject, config);
+ }
+
+}
+
+export default DragSpeedPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dragspeed.js b/ui/src/phaser3-rex-plugins/plugins/dragspeed.js
new file mode 100644
index 000000000..2d4b7247b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dragspeed.js
@@ -0,0 +1,2 @@
+import DragSpeed from './input/dragspeed/DragSpeed.js';
+export default DragSpeed;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.d.ts
new file mode 100644
index 000000000..70733d768
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.d.ts
@@ -0,0 +1,9 @@
+import DropDown from './dropdown';
+
+export default class DropDownPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: DropDown.IConfig
+ ): DropDown;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.js
new file mode 100644
index 000000000..58eda7d70
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropdown-plugin.js
@@ -0,0 +1,18 @@
+import DropDown from './behaviors/dropdown/DropDown.js';
+
+class DropDownPlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new DropDown(gameObject, config);
+ }
+}
+
+export default DropDownPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropdown.d.ts b/ui/src/phaser3-rex-plugins/plugins/dropdown.d.ts
new file mode 100644
index 000000000..3462812ba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropdown.d.ts
@@ -0,0 +1,2 @@
+import DropDown from './behaviors/dropdown/Dropdown';
+export default DropDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropdown.js b/ui/src/phaser3-rex-plugins/plugins/dropdown.js
new file mode 100644
index 000000000..1f7e3968b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropdown.js
@@ -0,0 +1,2 @@
+import DropDown from './behaviors/dropdown/DropDown.js';
+export default DropDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.d.ts
new file mode 100644
index 000000000..20395ff41
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.d.ts
@@ -0,0 +1,30 @@
+// import * as Phaser from 'phaser';
+import DropShadowPostFxPipeline from './dropshadowpipeline';
+
+
+export default DropShadowPipelinePlugin;
+
+declare namespace DropShadowPipelinePlugin {
+
+ interface IConfig extends DropShadowPostFxPipeline.IConfig {
+ name?: string,
+ }
+
+}
+
+declare class DropShadowPipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: DropShadowPipelinePlugin.IConfig
+ ): DropShadowPostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): DropShadowPostFxPipeline | DropShadowPostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.js
new file mode 100644
index 000000000..f1f9832dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline-plugin.js
@@ -0,0 +1,14 @@
+import DropShadowPostFxPipeline from './dropshadowpipeline.js';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class DropShadowPipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(DropShadowPostFxPipeline, 'rexDropShadowPostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.DropShadowPostFx', DropShadowPostFxPipeline);
+
+export default DropShadowPipelinePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.d.ts
new file mode 100644
index 000000000..1f404a793
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.d.ts
@@ -0,0 +1,2 @@
+import DropShadowPostFxPipeline from './shaders/dropshadow/DropShadowPostFxPipeline';
+export default DropShadowPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.js b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.js
new file mode 100644
index 000000000..021faaec4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dropshadowpipeline.js
@@ -0,0 +1,2 @@
+import DropShadowPostFxPipeline from './shaders/dropshadow/DropShadowPostFxPipeline.js';
+export default DropShadowPostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dynamictext-plugin.js b/ui/src/phaser3-rex-plugins/plugins/dynamictext-plugin.js
new file mode 100644
index 000000000..4bf7d012d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dynamictext-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/dynamictext/dynamictext/Factory';
+import Creator from './gameobjects/dynamictext/dynamictext/Creator.js';
+import DynamicText from './gameobjects/dynamictext/dynamictext/DynamicText.js';
+import SetValue from './utils/object/SetValue.js';
+
+class DynamicTextPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexDynamicText', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.DynamicText', DynamicText);
+
+export default DynamicTextPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dynamictext.d.ts b/ui/src/phaser3-rex-plugins/plugins/dynamictext.d.ts
new file mode 100644
index 000000000..5220639d2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dynamictext.d.ts
@@ -0,0 +1,2 @@
+import DynamicText from './gameobjects/dynamictext/dynamictext/DynamicText';
+export default DynamicText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/dynamictext.js b/ui/src/phaser3-rex-plugins/plugins/dynamictext.js
new file mode 100644
index 000000000..6f78571ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/dynamictext.js
@@ -0,0 +1,2 @@
+import DynamicText from './gameobjects/dynamictext/dynamictext/DynamicText.js';
+export default DynamicText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.d.ts
new file mode 100644
index 000000000..aa910fa1c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.d.ts
@@ -0,0 +1,8 @@
+import { EaseData } from './easedata.js';
+
+export default class ScalePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: EaseData.IConfig
+ ): EaseData;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.js b/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.js
new file mode 100644
index 000000000..3e6af8c66
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easedata-plugin.js
@@ -0,0 +1,19 @@
+import { EaseData } from './easedata.js';
+
+class EaseDataPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new EaseData(gameObject, config);
+ }
+}
+
+export default EaseDataPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easedata.d.ts b/ui/src/phaser3-rex-plugins/plugins/easedata.d.ts
new file mode 100644
index 000000000..93db552af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easedata.d.ts
@@ -0,0 +1,5 @@
+import EaseData from './behaviors/easedata/EaseData';
+
+export {
+ EaseData
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easedata.js b/ui/src/phaser3-rex-plugins/plugins/easedata.js
new file mode 100644
index 000000000..4c2ce2829
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easedata.js
@@ -0,0 +1,5 @@
+import EaseData from './behaviors/easedata/EaseData.js';
+
+export {
+ EaseData
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.d.ts
new file mode 100644
index 000000000..49bfb0e81
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.d.ts
@@ -0,0 +1,13 @@
+import { EaseMove, EaseMoveTo, EaseMoveToDestroy, EaseMoveFrom, EaseMoveFromDestroy } from './easemove';
+
+export default class ScalePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: EaseMove.IConfig
+ ): EaseMove;
+
+ moveTo: typeof EaseMoveTo;
+ moveFrom: typeof EaseMoveFrom;
+ moveToDestroy: typeof EaseMoveToDestroy;
+ moveFromDestroy: typeof EaseMoveFromDestroy;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.js b/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.js
new file mode 100644
index 000000000..0124a891f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easemove-plugin.js
@@ -0,0 +1,31 @@
+import { EaseMove, EaseMoveTo, EaseMoveToDestroy, EaseMoveFrom, EaseMoveFromDestroy } from './easemove.js';
+
+class EaseMovePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new EaseMove(gameObject, config);
+ }
+}
+
+// mixin
+var methods = {
+ moveTo: EaseMoveTo,
+ moveFrom: EaseMoveFrom,
+ moveToDestroy: EaseMoveToDestroy,
+ moveFromDestroy: EaseMoveFromDestroy
+}
+Object.assign(
+ EaseMovePlugin.prototype,
+ methods
+);
+
+export default EaseMovePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easemove.d.ts b/ui/src/phaser3-rex-plugins/plugins/easemove.d.ts
new file mode 100644
index 000000000..009a4e058
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easemove.d.ts
@@ -0,0 +1,27 @@
+import EaseMove from './behaviors/easemove/EaseMove';
+import EaseMoveTo from './behaviors/easemove/EaseMoveTo';
+import EaseMoveFrom from './behaviors/easemove/EaseMoveFrom';
+
+declare function EaseMoveToDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ endX: number | string | undefined,
+ endY: number | string | undefined,
+ ease?: string,
+ easeMove?: EaseMove
+): EaseMove;
+
+declare function EaseMoveFromDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ startX: number | string | undefined,
+ startY: number | string | undefined,
+ ease?: string,
+ easeMove?: EaseMove
+): EaseMove;
+
+export {
+ EaseMove,
+ EaseMoveTo, EaseMoveToDestroy,
+ EaseMoveFrom, EaseMoveFromDestroy
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/easemove.js b/ui/src/phaser3-rex-plugins/plugins/easemove.js
new file mode 100644
index 000000000..c7947d730
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/easemove.js
@@ -0,0 +1,17 @@
+import EaseMove from './behaviors/easemove/EaseMove.js';
+import EaseMoveTo from './behaviors/easemove/EaseMoveTo.js';
+import EaseMoveFrom from './behaviors/easemove/EaseMoveFrom.js';
+
+var EaseMoveToDestroy = function (gameObject, duration, endX, endY, ease, easeMove) {
+ return EaseMoveTo(gameObject, duration, endX, endY, ease, true, easeMove);
+}
+
+var EaseMoveFromDestroy = function (gameObject, duration, startX, startY, ease, easeMove) {
+ return EaseMoveFrom(gameObject, duration, startX, startY, ease, true, easeMove);
+}
+
+export {
+ EaseMove,
+ EaseMoveTo, EaseMoveToDestroy,
+ EaseMoveFrom, EaseMoveFromDestroy
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/effectlayer-plugin.js b/ui/src/phaser3-rex-plugins/plugins/effectlayer-plugin.js
new file mode 100644
index 000000000..eb376b6ad
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/effectlayer-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shader/effectlayer/effectlayer/Factory.js';
+import Creator from './gameobjects/shader/effectlayer/effectlayer/Creator.js';
+import EffectLayer from './gameobjects/shader/effectlayer/effectlayer/EffectLayer.js';
+import SetValue from './utils/object/SetValue.js';
+
+class EffectLayerPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexEffectLayer', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.EffectLayer', EffectLayer);
+
+export default EffectLayerPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/effectlayer.js b/ui/src/phaser3-rex-plugins/plugins/effectlayer.js
new file mode 100644
index 000000000..2f1f48890
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/effectlayer.js
@@ -0,0 +1,2 @@
+import EffectLayer from './gameobjects/shader/effectlayer/EffectLayer.js';
+export default EffectLayer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.d.ts
new file mode 100644
index 000000000..e5b4e0547
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.d.ts
@@ -0,0 +1,9 @@
+import EightDirection from './eightdirection';
+
+export default class EightDirectionPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: EightDirection.IConfig
+ ): EightDirection;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.js b/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.js
new file mode 100644
index 000000000..cc4f28b56
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eightdirection-plugin.js
@@ -0,0 +1,20 @@
+import EightDirection from './eightdirection.js';
+
+class EightDirectionPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new EightDirection(gameObject, config);
+ }
+
+}
+
+export default EightDirectionPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eightdirection.d.ts b/ui/src/phaser3-rex-plugins/plugins/eightdirection.d.ts
new file mode 100644
index 000000000..7be5603a6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eightdirection.d.ts
@@ -0,0 +1,2 @@
+import EightDirection from './behaviors/eightdirection/EightDirection';
+export default EightDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eightdirection.js b/ui/src/phaser3-rex-plugins/plugins/eightdirection.js
new file mode 100644
index 000000000..223994c39
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eightdirection.js
@@ -0,0 +1,2 @@
+import EightDirection from './behaviors/eightdirection/EightDirection.js';
+export default EightDirection;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.d.ts
new file mode 100644
index 000000000..78dc33077
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.d.ts
@@ -0,0 +1,7 @@
+import { WaitEvent, WaitComplete, Delay } from './eventpromise'
+
+export default class EventPromisePlugin extends Phaser.Plugins.BasePlugin {
+ waitEvent: typeof WaitEvent;
+ waitComplete: typeof WaitComplete;
+ delay: typeof Delay;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.js b/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.js
new file mode 100644
index 000000000..e4c14f9af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eventpromise-plugin.js
@@ -0,0 +1,22 @@
+import { WaitEvent, WaitComplete, Delay } from './eventpromise.js'
+
+class EventPromisePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+}
+
+var methods = {
+ waitEvent: WaitEvent,
+ waitComplete: WaitComplete,
+ delay: Delay,
+}
+
+// mixin
+Object.assign(
+ EventPromisePlugin.prototype,
+ methods
+);
+
+export default EventPromisePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eventpromise.d.ts b/ui/src/phaser3-rex-plugins/plugins/eventpromise.d.ts
new file mode 100644
index 000000000..5eb1de952
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eventpromise.d.ts
@@ -0,0 +1,3 @@
+import { WaitEvent, WaitComplete } from './utils/promise/WaitEvent';
+import Delay from './utils/promise/Delay';
+export { WaitEvent, WaitComplete, Delay };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/eventpromise.js b/ui/src/phaser3-rex-plugins/plugins/eventpromise.js
new file mode 100644
index 000000000..318c7eb1a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/eventpromise.js
@@ -0,0 +1,4 @@
+import { WaitEvent, WaitComplete } from './utils/promise/WaitEvent.js';
+import Delay from './utils/promise/Delay.js';
+
+export { WaitEvent, WaitComplete, Delay };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.d.ts
new file mode 100644
index 000000000..fa34c71ef
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.d.ts
@@ -0,0 +1,11 @@
+import ExpressionParser from './expressionparser';
+import Compile from './math/expressionparser/utils/Compile';
+import CreateProxyContext from './math/expressionparser/utils/CreateProxyContext';
+
+export default class ExpressionParserPlugin extends Phaser.Plugins.BasePlugin {
+ add(): ExpressionParser;
+
+ compile: typeof Compile;
+
+ createProxyContext: typeof CreateProxyContext;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.js b/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.js
new file mode 100644
index 000000000..4ca437d61
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/expressionparser-plugin.js
@@ -0,0 +1,32 @@
+import ExpressionParser from './expressionparser.js';
+import Compile from './math/expressionparser/utils/Complile.js';
+import CreateProxyContext from './utils/proxy/createproxycontext/CreateProxyContext.js';
+import SetValue from './utils/object/SetValue.js';
+
+class ExpressionParserPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add() {
+ return new ExpressionParser();
+ }
+
+ compile(expression) {
+ return Compile(expression);
+ }
+
+ createProxyContext(config, baseContext) {
+ return CreateProxyContext(config, baseContext);
+ }
+}
+
+SetValue(window, 'RexPlugins.ExpressionParser', ExpressionParser);
+
+export default ExpressionParserPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/expressionparser.d.ts b/ui/src/phaser3-rex-plugins/plugins/expressionparser.d.ts
new file mode 100644
index 000000000..d7b04168a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/expressionparser.d.ts
@@ -0,0 +1,2 @@
+import ExpressionParser from './math/expressionparser/ExpressionParser';
+export default ExpressionParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/expressionparser.js b/ui/src/phaser3-rex-plugins/plugins/expressionparser.js
new file mode 100644
index 000000000..806d32fdc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/expressionparser.js
@@ -0,0 +1,2 @@
+import ExpressionParser from './math/expressionparser/ExpressionParser.js';
+export default ExpressionParser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-in.d.ts b/ui/src/phaser3-rex-plugins/plugins/fade-in.d.ts
new file mode 100644
index 000000000..ea7e7f8ad
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-in.d.ts
@@ -0,0 +1,8 @@
+import Fade from './fade';
+
+export default function FadeIn(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ alpha?: number | { start: number, end: number },
+ fade?: Fade
+): Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-in.js b/ui/src/phaser3-rex-plugins/plugins/fade-in.js
new file mode 100644
index 000000000..145c97e68
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-in.js
@@ -0,0 +1,37 @@
+import Fade from './fade.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+
+var FadeIn = function (gameObject, duration, alpha, fade) {
+ var startAlpha, endAlpha;
+ if (IsPlainObject(alpha)) {
+ startAlpha = alpha.start;
+ endAlpha = alpha.end;
+ } else {
+ endAlpha = alpha;
+ }
+ if (startAlpha === undefined) {
+ startAlpha = 0
+ }
+ if (endAlpha === undefined) {
+ endAlpha = 1;
+ }
+
+ var config = {
+ mode: 0,
+ start: startAlpha,
+ end: endAlpha,
+ duration: duration,
+ }
+
+ if (fade === undefined) {
+ fade = new Fade(gameObject, config);
+ } else {
+ fade.resetFromJSON(config);
+ }
+ fade.restart();
+
+ return fade;
+};
+
+export default FadeIn;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.d.ts b/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.d.ts
new file mode 100644
index 000000000..92182f629
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.d.ts
@@ -0,0 +1,14 @@
+import Fade from './fade';
+
+export default function FadeOutDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ fade?: Fade
+): Fade;
+
+export default function FadeOutDestroy(
+ gameObject: Phaser.GameObjects.GameObject,
+ duration: number,
+ destroyMode?: boolean,
+ fade?: Fade
+): Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.js b/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.js
new file mode 100644
index 000000000..f51b422a7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-out-destroy.js
@@ -0,0 +1,29 @@
+import Fade from './fade.js';
+
+var FadeOutDestroy = function (gameObject, duration, destroyMode, fade) {
+ if (destroyMode instanceof Fade) {
+ fade = destroyMode;
+ destroyMode = undefined;
+ }
+
+ if (destroyMode === undefined) {
+ destroyMode = true;
+ }
+
+ var config = {
+ mode: (destroyMode) ? 1 : 0,
+ end: 0,
+ duration: duration,
+ }
+
+ if (fade === undefined) {
+ fade = new Fade(gameObject, config);
+ } else {
+ fade.resetFromJSON(config);
+ }
+ fade.restart();
+
+ return fade;
+};
+
+export default FadeOutDestroy;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/fade-plugin.d.ts
new file mode 100644
index 000000000..cc6ebe026
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-plugin.d.ts
@@ -0,0 +1,11 @@
+import Fade from './fade';
+import FadeOutDestroy from './fade-out-destroy';
+
+export default class FadePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Fade.IConfig
+ ): Fade;
+
+ fadeOutDestroy: typeof FadeOutDestroy;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade-plugin.js b/ui/src/phaser3-rex-plugins/plugins/fade-plugin.js
new file mode 100644
index 000000000..072777c3d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade-plugin.js
@@ -0,0 +1,29 @@
+import Fade from './fade.js';
+import FadeOutDestroy from './fade-out-destroy.js';
+
+class FadePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Fade(gameObject, config);
+ }
+}
+
+// mixin
+var methods = {
+ fadeOutDestroy: FadeOutDestroy
+};
+Object.assign(
+ FadePlugin.prototype,
+ methods
+);
+
+export default FadePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade.d.ts b/ui/src/phaser3-rex-plugins/plugins/fade.d.ts
new file mode 100644
index 000000000..156e7198a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade.d.ts
@@ -0,0 +1,2 @@
+import Fade from './behaviors/fade/Fade';
+export default Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fade.js b/ui/src/phaser3-rex-plugins/plugins/fade.js
new file mode 100644
index 000000000..394c1737a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fade.js
@@ -0,0 +1,2 @@
+import Fade from './behaviors/fade/Fade.js';
+export default Fade;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filechooser-plugin.js b/ui/src/phaser3-rex-plugins/plugins/filechooser-plugin.js
new file mode 100644
index 000000000..7d1a1380d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filechooser-plugin.js
@@ -0,0 +1,29 @@
+import OpenFileChooser from './behaviors/filechooser/Open.js';
+import Factory from './gameobjects/dom/filechooser/Factory.js';
+import Creator from './gameobjects/dom/filechooser/Creator.js';
+import FileChooser from './gameobjects/dom/filechooser/FileChooser.js';
+import SetValue from './utils/object/SetValue.js';
+
+class FileChooserPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexFileChooser', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ // Note: Not working in iOS9+
+ open(config) {
+ return OpenFileChooser(this.game, config);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.FileChooser', FileChooser);
+
+export default FileChooserPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filechooser.d.ts b/ui/src/phaser3-rex-plugins/plugins/filechooser.d.ts
new file mode 100644
index 000000000..f41f89e16
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filechooser.d.ts
@@ -0,0 +1,3 @@
+import OpenFileChooser from './behaviors/filechooser/Open';
+import FileChooser from './gameobjects/dom/filechooser/FileChooser';
+export { OpenFileChooser, FileChooser };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filechooser.js b/ui/src/phaser3-rex-plugins/plugins/filechooser.js
new file mode 100644
index 000000000..744ba71a2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filechooser.js
@@ -0,0 +1,3 @@
+import OpenFileChooser from './behaviors/filechooser/Open.js';
+import FileChooser from './gameobjects/dom/filechooser/FileChooser.js';
+export { OpenFileChooser, FileChooser };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filedropzone-plugin.js b/ui/src/phaser3-rex-plugins/plugins/filedropzone-plugin.js
new file mode 100644
index 000000000..3244eafb5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filedropzone-plugin.js
@@ -0,0 +1,28 @@
+import Factory from './gameobjects/dom/filedropzone/Factory.js';
+import Creator from './gameobjects/dom/filedropzone/Creator.js';
+import FileDropZone from './gameobjects/dom/filedropzone/FileDropZone.js';
+import SetValue from './utils/object/SetValue.js';
+
+class FileDropZonePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexFileDropZone', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ // Note: Not working in iOS9+
+ open(config) {
+ return OpenFileChooser(this.game, config);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.FileDropZone', FileDropZone);
+
+export default FileDropZonePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filedropzone.d.ts b/ui/src/phaser3-rex-plugins/plugins/filedropzone.d.ts
new file mode 100644
index 000000000..6f630e322
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filedropzone.d.ts
@@ -0,0 +1,2 @@
+import FileDropZone from './gameobjects/dom/filedropzone/FileDropZone';
+export default FileDropZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/filedropzone.js b/ui/src/phaser3-rex-plugins/plugins/filedropzone.js
new file mode 100644
index 000000000..a1bbd7d17
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/filedropzone.js
@@ -0,0 +1,2 @@
+import FileDropZone from './gameobjects/dom/filedropzone/FileDropZone.js';
+export default FileDropZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase-components.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase-components.d.ts
new file mode 100644
index 000000000..51a5e0c69
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase-components.d.ts
@@ -0,0 +1,27 @@
+import Preload from './firebase/preload/Preload.js';
+
+import Broadcast from './firebase/database/broadcast/Broadcast.js';
+import OnlineUserList from './firebase/database/onlineuserlist/OnlineUserList.js';
+import Room from './firebase/database/room/Room.js';
+import SingleRoom from './firebase/database/singleroom/SingleRoom.js';
+import ItemTable from './firebase/database/itemtable/ItemTable.js';
+
+import PageLoader from './firebase/firestore/pageloader/PageLoader.js';
+import Files from './firebase/firestore/files/Files.js';
+import IdAlias from './firebase/firestore/idalias/IdAlias.js';
+import LeaderBoard from './firebase/firestore/leaderboard/LeaderBoard.js';
+import Messages from './firebase/firestore/messages/Messages.js';
+
+export {
+ Preload,
+ Broadcast,
+ OnlineUserList,
+ Room,
+ SingleRoom,
+ ItemTable,
+ PageLoader,
+ Files,
+ IdAlias,
+ LeaderBoard,
+ Messages
+};
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase-components.js b/ui/src/phaser3-rex-plugins/plugins/firebase-components.js
new file mode 100644
index 000000000..a1c8b3ae2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase-components.js
@@ -0,0 +1,27 @@
+import Preload from './firebase/preload/Preload';
+
+import Broadcast from './firebase/database/broadcast/Broadcast';
+import OnlineUserList from './firebase/database/onlineuserlist/OnlineUserList';
+import Room from './firebase/database/room/Room';
+import SingleRoom from './firebase/database/singleroom/SingleRoom';
+import ItemTable from './firebase/database/itemtable/ItemTable';
+
+import PageLoader from './firebase/firestore/pageloader/PageLoader';
+import Files from './firebase/firestore/files/Files';
+import IdAlias from './firebase/firestore/idalias/IdAlias';
+import LeaderBoard from './firebase/firestore/leaderboard/LeaderBoard';
+import Messages from './firebase/firestore/messages/Messages';
+
+export {
+ Preload,
+ Broadcast,
+ OnlineUserList,
+ Room,
+ SingleRoom,
+ ItemTable,
+ PageLoader,
+ Files,
+ IdAlias,
+ LeaderBoard,
+ Messages
+};
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase-plugin.js b/ui/src/phaser3-rex-plugins/plugins/firebase-plugin.js
new file mode 100644
index 000000000..a25b94840
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase-plugin.js
@@ -0,0 +1,40 @@
+import LoaderCallback from './firebase/preload/LoaderCallback.js';
+import ObjectFactory from './firebase/ObjectFactory.js';
+
+import BroadcastFactory from './firebase/database/broadcast/Factory.js';
+import OnlineUserListFactory from './firebase/database/onlineuserlist/Factory.js';
+import RoomFactory from './firebase/database/room/Factory.js';
+import SingleRoomFactory from './firebase/database/singleroom/Factory.js';
+import ItemTableFactory from './firebase/database/itemtable/Factory.js';
+
+import PageLoaderFactory from './firebase/firestore/pageloader/Factory.js';
+import FilesFactory from './firebase/firestore/files/Factory.js';
+import IdAliasFactory from './firebase/firestore/idalias/Factory.js';
+import LeaderBoardFactory from './firebase/firestore/leaderboard/Factory.js';
+import MessagesFactory from './firebase/firestore/messages/Factory.js';
+
+class FirebasePlugin extends Phaser.Plugins.BasePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ this.add = new ObjectFactory();
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ initializeApp(config) {
+ this.add.initializeApp(config);
+ return this;
+ }
+
+ preload(scene, urlConfig, firebaseConfig) {
+ LoaderCallback.call(scene.sys.load, urlConfig, firebaseConfig);
+ return this;
+ }
+}
+
+
+export default FirebasePlugin;
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase.js b/ui/src/phaser3-rex-plugins/plugins/firebase.js
new file mode 100644
index 000000000..925f0a1e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase.js
@@ -0,0 +1,32 @@
+import Preload from './firebase/preload/Preload.js';
+import ObjectFactory from './firebase/ObjectFactory.js'
+
+import BroadcastFactory from './firebase/database/broadcast/Factory.js';
+import OnlineUserListFactory from './firebase/database/onlineuserlist/Factory.js';
+import RoomFactory from './firebase/database/room/Factory.js';
+import SingleRoomFactory from './firebase/database/singleroom/Factory.js';
+import ItemTableFactory from './firebase/database/itemtable/Factory.js';
+
+import PageLoaderFactory from './firebase/firestore/pageloader/Factory.js';
+import FilesFactory from './firebase/firestore/files/Factory.js';
+import IdAliasFactory from './firebase/firestore/idalias/Factory.js';
+import LeaderBoardFactory from './firebase/firestore/leaderboard/Factory.js';
+import MessagesFactory from './firebase/firestore/messages/Factory.js';
+
+class FirebasePlugin {
+ constructor() {
+ this.add = new ObjectFactory();
+ }
+
+ initializeApp(config) {
+ this.add.initializeApp(config);
+ return this;
+ }
+
+ preload(urlConfig, firebaseConfig) {
+ return Preload(urlConfig, firebaseConfig);
+ }
+}
+
+
+export default FirebasePlugin;
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/ObjectFactory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/ObjectFactory.js
new file mode 100644
index 000000000..63bc67288
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/ObjectFactory.js
@@ -0,0 +1,15 @@
+class ObjectFactory {
+ constructor() {
+ }
+
+ initializeApp(config) {
+ firebase.initializeApp(config);
+ return this;
+ }
+
+ static register(type, callback) {
+ ObjectFactory.prototype[type] = callback;
+ }
+};
+
+export default ObjectFactory;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.d.ts
new file mode 100644
index 000000000..5182b6d4e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.d.ts
@@ -0,0 +1,54 @@
+import EventEmitter from "../../../utils/eventemitter/EventEmitter";
+
+export default Broadcast;
+
+declare namespace Broadcast {
+ interface IConfig {
+ root?: string,
+ senderID?: string,
+ senderName?: string,
+ receiverID?: string,
+ history?: number | boolean,
+
+ eventEmitter?: EventEmitter | false,
+ }
+
+ type MessageType = string |
+ { [name: string]: number | string | boolean };
+
+ interface IReceiveData {
+ senderID: string, senderName?: string,
+ message: MessageType
+ }
+}
+
+declare class Broadcast extends EventEmitter {
+ constructor(
+ config?: Broadcast.IConfig
+ );
+
+ setSender(
+ userID: string, userName?: string
+ ): this;
+
+ setSender(
+ config: { userID: string, userName?: string }
+ ): this;
+
+ userID: string;
+ userName: string;
+ readonly userInfo: { userID?: string, userName?: string };
+
+ setReceiver(receiverID: string): this;
+ receiverID: string;
+
+ send(
+ message: Broadcast.MessageType
+ ): Promise;
+
+ startReceiving(): this;
+ stopReceiving(): this;
+
+ getHistory(): Broadcast.IReceiveData[];
+ clearHistory(): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.js
new file mode 100644
index 000000000..ec2a276ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Broadcast.js
@@ -0,0 +1,123 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Send from './Send.js';
+import ReceiveMethods from './ReceiveMethods.js';
+import History from './History.js';
+
+class Broadcast {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+ this.eventNameMap = GetValue(config, 'eventNames', DefaultEventNames);
+
+ this.database = firebase.database();
+ this.setRootPath(GetValue(config, 'root', ''));
+
+ // Sender
+ this.skipFirst = true;
+ this.stamp = false;
+ this.userInfo = { userID: '', userName: undefined };
+ this.setSender(GetValue(config, 'senderID', ''), GetValue(config, 'senderName', ''));
+ this.setReceiver(GetValue(config, 'receiverID', ''));
+
+ // Receiver
+ this.isReceiving = false;
+
+ // History messages
+ var historyMaxLength = GetValue(config, 'history', 0);
+ if (historyMaxLength === true) {
+ historyMaxLength = -1;
+ } else if (historyMaxLength === false) {
+ historyMaxLength = 0;
+ }
+ this.history = new History({
+ maxLength: historyMaxLength
+ });
+
+ }
+
+ shutdown() {
+ this
+ .stopReceiving()
+ .destroyEventEmitter();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ setRootPath(rootPath) {
+ this.rootPath = rootPath;
+ this.sendToRef = undefined;
+ this.receiverRef = undefined;
+ return this;
+ }
+
+ setSender(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ setReceiver(receiverID) {
+ this.receiverID = receiverID;
+ return this;
+ }
+
+ changeUserName(userID, userName) {
+ if (userID === this.userID) {
+ this.userName = userName;
+ }
+ this.history.changeUserName(userID, userName);
+ return this;
+ }
+
+ getHistory() {
+ return this.history.records;
+ }
+
+ clearHistory() {
+ this.history.clear();
+ return this;
+ }
+}
+
+var methods = {
+ send: Send
+}
+Object.assign(
+ Broadcast.prototype,
+ EventEmitterMethods,
+ ReceiveMethods,
+ methods
+);
+
+const DefaultEventNames = {
+ receive: 'receive'
+}
+
+
+export default Broadcast;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.d.ts
new file mode 100644
index 000000000..79dda23c6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.d.ts
@@ -0,0 +1,5 @@
+import Broadcast from './Broadcast';
+
+export default function (
+ config: Broadcast.IConfig
+): Broadcast;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.js
new file mode 100644
index 000000000..c6506ab81
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Factory.js
@@ -0,0 +1,11 @@
+import Broadcast from './Broadcast.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('broadcast', function (config) {
+ return new Broadcast(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.Broadcast', Broadcast);
+
+export default Broadcast;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/History.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/History.js
new file mode 100644
index 000000000..2b59ddb05
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/History.js
@@ -0,0 +1,40 @@
+import GetValue from '../../../utils/object/GetValue.js';
+
+class History {
+ constructor(config) {
+ this.maxLength = GetValue(config, 'maxLength', -1); // -1: Infinity
+ this.records = [];
+ }
+
+ add(record) {
+ if (this.maxLength === 0) {
+ return this;
+ }
+
+ this.records.push(record);
+ if ((this.maxLength > 0) && (this.records.length > this.maxLength)) {
+ this.records.shift();
+ }
+ return this;
+ }
+
+ clear() {
+ this.records.length = 0;
+ return this;
+ }
+
+ changeUserName(userID, userName) {
+ if (this.maxLength === 0) {
+ return this;
+ }
+
+ this.records.forEach(function (record) {
+ if (record.senderID === userID) {
+ record.senderName = userName;
+ }
+ })
+ return this;
+ }
+}
+
+export default History;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/ReceiveMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/ReceiveMethods.js
new file mode 100644
index 000000000..188f46bf6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/ReceiveMethods.js
@@ -0,0 +1,45 @@
+var methods = {
+ startReceiving() {
+ if (this.isReceiving && (this.receiverRef.key === this.receiverID)) {
+ return this;
+ }
+
+ this.stopReceiving();
+
+ this.isReceiving = true;
+ this.skipFirst = true; // Skip previous message
+ this.receiverRef = this.database.ref(this.rootPath).child(this.receiverID);
+ this.receiverRef.on('value', OnReceive, this);
+ this.receiverRef.onDisconnect().remove();
+ return this;
+ },
+
+ stopReceiving() {
+ if (!this.isReceiving) {
+ return this;
+ }
+
+ this.isReceiving = false;
+ this.receiverRef.off('value', OnReceive, this);
+ this.receiverRef.remove();
+ this.receiverRef.onDisconnect().cancel();
+ return this;
+ }
+}
+
+var OnReceive = function (snapshot) {
+ if (this.skipFirst) {
+ this.skipFirst = false;
+ return;
+ }
+ var d = snapshot.val();
+ if (d == null) {
+ return;
+ }
+
+ delete d.stamp;
+ this.history.add(d);
+ this.emit(this.eventNameMap.receive, d);
+}
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Send.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Send.js
new file mode 100644
index 000000000..142301baa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/Send.js
@@ -0,0 +1,24 @@
+var Send = function (message) {
+ if ((!this.sendToRef) || (this.sendToRef.key !== this.receiverID)) {
+ this.sendToRef = this.database.ref(this.rootPath).child(this.receiverID);
+ }
+
+ // Clear message
+ if (message === undefined) {
+ return this.sendToRef.remove(); // Promise
+ }
+
+ var d = {
+ message: message,
+ senderID: this.userID,
+ stamp: this.stamp,
+ };
+ if (this.userName !== undefined) {
+ d.senderName = this.userName;
+ }
+ this.skipFirst = false;
+ this.stamp = !this.stamp;
+ return this.sendToRef.set(d);
+}
+
+export default Send;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/schema.md
new file mode 100644
index 000000000..9f24f76bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/broadcast/schema.md
@@ -0,0 +1,5 @@
+-
+ - `message` - Message
+ - `senderID` - Unique ID of sender
+ - `senderName` - Name of sender
+ - `stamp` - Toggle between true and false
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.d.ts
new file mode 100644
index 000000000..5ec6745bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.d.ts
@@ -0,0 +1,5 @@
+import ItemTable from './ItemTable';
+
+export default function (
+ config: ItemTable.IConfig
+): ItemTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.js
new file mode 100644
index 000000000..1f01c2689
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/Factory.js
@@ -0,0 +1,11 @@
+import ItemTable from './ItemTable.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('itemTable', function (config) {
+ return new ItemTable(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.ItemTable', ItemTable);
+
+export default ItemTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.d.ts
new file mode 100644
index 000000000..491272e10
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.d.ts
@@ -0,0 +1,149 @@
+import EventEmitter from "../../../utils/eventemitter/EventEmitter";
+
+export default ItemTable;
+
+declare namespace ItemTable {
+
+ type TableType = 1 | 2 | 3 | '1d' | '2d' | '3d';
+
+ interface IConfig {
+ root?: string,
+ type?: TableType,
+
+ eventEmitter?: EventEmitter | false,
+ }
+
+ type ValueType = number | string | boolean |
+ { [name: string]: ValueType };
+
+ type TransactionCallbackType = (prevValue: ValueType) => ValueType;
+
+}
+
+declare class ItemTable extends EventEmitter {
+ constructor(
+ config?: ItemTable.IConfig
+ );
+
+ setData(
+ key0: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ setData(
+ key0: string, key1: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ setData(
+ key0: string, key1: string, key2: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ incValue(
+ key0: string,
+ value: number
+ ): Promise;
+
+ incValue(
+ key0: string, key1: string,
+ value: number
+ ): Promise;
+
+ incValue(
+ key0: string, key1: string, key2: string,
+ value: number
+ ): Promise;
+
+ removeData(
+ key0: string
+ ): Promise;
+
+ removeData(
+ key0: string, key1: string
+ ): Promise;
+
+ removeData(
+ key0: string, key1: string, key2: string
+ ): Promise;
+
+ updateData(
+ data: { [path: string]: ItemTable.ValueType }
+ ): Promise;
+
+ transaction(
+ key0: string,
+ callback: ItemTable.TransactionCallbackType
+ ): Promise;
+
+ transaction(
+ key0: string, key1: string,
+ callback: ItemTable.TransactionCallbackType
+ ): Promise;
+
+ transaction(
+ key0: string, key1: string, key2: string,
+ callback: ItemTable.TransactionCallbackType
+ ): Promise;
+
+ removeDataOnDisconnect(
+ key0: string,
+ ): Promise;
+
+ removeDataOnDisconnect(
+ key0: string, key1: string,
+ ): Promise;
+
+ removeDataOnDisconnect(
+ key0: string, key1: string, key2: string,
+ ): Promise;
+
+ setDataOnDisconnect(
+ key0: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ setDataOnDisconnect(
+ key0: string, key1: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ setDataOnDisconnect(
+ key0: string, key1: string, key2: string,
+ value: ItemTable.ValueType
+ ): Promise;
+
+ startUpdate(): this;
+ stopUpdate(): this;
+
+ getData(
+ ): ItemTable.ValueType;
+
+ getData(
+ key0: string,
+ ): ItemTable.ValueType;
+
+ getData(
+ key0: string, key1: string,
+ ): ItemTable.ValueType;
+
+ getData(
+ key0: string, key1: string, key2: string,
+ ): ItemTable.ValueType;
+
+ cloneData(
+ ): ItemTable.ValueType;
+
+ cloneData(
+ key0: string,
+ ): ItemTable.ValueType;
+
+ cloneData(
+ key0: string, key1: string,
+ ): ItemTable.ValueType;
+
+ cloneData(
+ key0: string, key1: string, key2: string,
+ ): ItemTable.ValueType;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.js
new file mode 100644
index 000000000..34f44d883
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/ItemTable.js
@@ -0,0 +1,144 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import Table from '../../../utils/struct/Tree.js';
+import ColumnUpdater from './read/ColumnUpdater.js';
+import RowUpdater from './read/RowUpdater.js';
+import Pagepdater from './read/PageUpdater.js';
+import Init from './read/Init.js';
+import SetData from './write/SetData.js';
+import RemoveData from './write/RemoveData.js';
+import IncValue from './write/IncValue.js';
+import Transaction from './write/Transaction.js';
+import UpdateData from './write/UpdateData.js';
+import RemoveDataOnDisconnect from './write/RemoveDataOnDisconnect.js';
+import SetDataOnDisconnect from './write/SetDataOnDisconnect.js';
+
+class ItemTable {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+ this.eventNameMap = GetValue(config, 'eventNames', DefaultEventNames);
+
+ this.database = firebase.database();
+ this.table = new Table();
+ this.setTableType(GetValue(config, 'type', 3));
+ this.setRootPath(GetValue(config, 'root', ''));
+ this.initialFlag = false;
+ }
+
+ shutdown() {
+ this.updater.destroy();
+ this
+ .destroyEventEmitter()
+ .stopUpdate();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ setRootPath(rootPath) {
+ this.rootPath = rootPath;
+ this.updater.setRootPath(rootPath);
+ return this;
+ }
+
+ setTableType(type) {
+ if (typeof (type) === 'string') {
+ type = TABLE_TYPE[type];
+ }
+ this.tableType = type;
+ var UpdaterClass = UpdaterClasses[type];
+ this.updater = new UpdaterClass({
+ type: type,
+ eventEmitter: this.getEventEmitter(),
+ eventNames: this.eventNameMap,
+ table: this.table
+ })
+
+
+ return this;
+ }
+
+ getRootRef() {
+ return this.database.ref(this.rootPath)
+ }
+
+ getRef(key0, key1, key2) {
+ var ref = this.getRootRef();
+ ref = (key0) ? ref.child(key0) : ref;
+ ref = (key1) ? ref.child(key1) : ref;
+ ref = (key2) ? ref.child(key2) : ref;
+ return ref;
+ }
+
+ startUpdate() {
+ Init.call(this);
+ this.updater
+ .startUpdate();
+ return this;
+ }
+
+ stopUpdate() {
+ this.updater.stopUpdate();
+ return this;
+ }
+
+ clear() {
+ this.updater.clear();
+ return this;
+ }
+
+ getData() {
+ return this.table.getValue(arguments);
+ }
+
+ cloneData() {
+ return this.table.cloneValue(arguments);
+ }
+}
+
+var UpdaterClasses = {
+ 1: ColumnUpdater,
+ 2: RowUpdater,
+ 3: Pagepdater
+};
+
+var methods = {
+ setData: SetData,
+ removeData: RemoveData,
+ incValue: IncValue,
+ transaction: Transaction,
+ updateData: UpdateData,
+ removeDataOnDisconnect: RemoveDataOnDisconnect,
+ setDataOnDisconnect: SetDataOnDisconnect
+}
+Object.assign(
+ ItemTable.prototype,
+ EventEmitterMethods,
+ methods
+);
+
+const TABLE_TYPE = {
+ '1d': 1,
+ '2d': 2,
+ '3d': 3
+}
+
+const DefaultEventNames = {
+ init: 'init',
+ update: 'update',
+ addkey0: 'addkey0',
+ removekey0: 'removekey0',
+ changekey0: 'changekey0',
+ addkey1: 'addkey1',
+ removekey1: 'removekey1',
+ changekey1: 'changekey1',
+ addkey2: 'addkey2',
+ removekey2: 'removekey2',
+ changekey2: 'changekey2'
+}
+
+export default ItemTable
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/BaseUpdater.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/BaseUpdater.js
new file mode 100644
index 000000000..921ebeefe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/BaseUpdater.js
@@ -0,0 +1,154 @@
+import EventEmitterMethods from '../../../../utils/eventemitter/EventEmitterMethods.js';
+
+class BaseUpdater {
+ constructor(config) {
+ // Event emitter
+ this.setEventEmitter(config.eventEmitter, config.EventEmitterClass);
+
+ this.parent = config.parent;
+ this.key = config.key;
+ if (this.parent) {
+ this.fullKeyPath = ExtendKeyPath(this.parent.fullKeyPath, this.key);
+ } else {
+ this.fullKeyPath = '';
+ }
+ this.type = config.type;
+ this.eventNameMap = config.eventNames;
+ this.table = config.table;
+
+ this.database = firebase.database();
+ this.setRootPath();
+ this.children = {};
+ }
+
+ shutdown() {
+ this
+ .stopUpdate()
+ .clear()
+ .destroyEventEmitter();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ setRootPath(rootPath) {
+ if (rootPath === undefined) {
+ var parentRootPath = (this.parent) ? this.parent.rootPath : '';
+ rootPath = `${parentRootPath}/${this.key}`;
+ }
+ this.rootPath = rootPath;
+
+ var child;
+ for (var key in this.children) {
+ child = this.children[key];
+ if (child instanceof BaseUpdater) {
+ child.setRootPath();
+ }
+ }
+ return this;
+ }
+
+ get rootRef() {
+ return this.database.ref(this.rootPath);
+ }
+
+ load() {
+ var self = this;
+ return this.rootRef.once('value')
+ .then(function (snapshot) {
+ // Won't add any child
+ var value = snapshot.val() || {};
+ self.table.setValue(value)
+ return Promise.resolve(value)
+ })
+ }
+
+ setData(key, value) {
+ if (key === undefined) {
+ this.clear(); // Clear
+ } else if (value === undefined) {
+ var data = key; // JSON data
+ for (key in this.children) { // Not in new data
+ if (!data.hasOwnProperty(key)) {
+ this.removeChild(key);
+ }
+ }
+ for (key in data) {
+ this.setChildData(key, data[key]);
+ }
+ } else {
+ this.setChildData(key, value); // Pass data to column-updater
+ }
+ return this;
+ }
+
+ clear() {
+ this.table.removeKey(this.fullKeyPath);
+ for (var key in this.children) {
+ this.removeChild(key);
+ }
+ return this;
+ }
+
+ // Overwrite
+ get childClass() {
+ return undefined;
+ }
+
+ // Overwrite
+ setChildData(key, data) {
+ var keyPath = ExtendKeyPath(this.fullKeyPath, key);
+ this.table.setValue(keyPath, data);
+
+ if (!this.children.hasOwnProperty(key)) {
+ if (this.childClass) {
+ var child = new this.childClass({
+ parent: this,
+ key: key,
+ type: this.type,
+ eventEmitter: this.getEventEmitter(),
+ eventNames: this.eventNameMap,
+ table: this.table
+ });
+ child.startUpdate();
+ this.children[key] = child;
+ }
+ } else {
+ this.children[key].setData(data);
+ }
+ return this;
+ }
+
+ // Overwrite
+ removeChild(key) {
+ if (this.children.hasOwnProperty(key)) {
+ this.children[key].destroy();
+ delete this.children[key];
+ }
+ return this;
+ }
+
+ // Overwrite
+ startUpdate() { }
+
+ // Overwrite
+ stopUpdate() { }
+}
+
+var ExtendKeyPath = function (baseKeyPath, newKey) {
+ if ((baseKeyPath == null) || (baseKeyPath === '')) {
+ return newKey;
+ } else if ((newKey == null) || (newKey === '')) {
+ return baseKeyPath;
+ } else {
+ return `${baseKeyPath}.${newKey}`;
+ }
+}
+
+Object.assign(
+ BaseUpdater.prototype,
+ EventEmitterMethods
+);
+
+export default BaseUpdater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/ColumnUpdater.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/ColumnUpdater.js
new file mode 100644
index 000000000..1e7dd06b2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/ColumnUpdater.js
@@ -0,0 +1,80 @@
+import BaseUpdater from './BaseUpdater.js';
+
+class ColumnUpdater extends BaseUpdater {
+ startUpdate() {
+ this.rootRef.on('child_added', this.addCol, this);
+ this.rootRef.on('child_removed', this.removeCol, this);
+ this.rootRef.on('child_changed', this.changeColValue, this);
+ return this;
+ }
+
+ stopUpdate() {
+ this.rootRef.off('child_added', this.addCol, this);
+ this.rootRef.off('child_removed', this.removeCol, this);
+ this.rootRef.off('child_changed', this.changeColValue, this);
+ return this;
+ }
+
+ addCol(snapshot) {
+ var key = snapshot.key,
+ value = snapshot.val();
+ this.setData(key, value);
+
+ switch (this.type) {
+ case 1:
+ this.emit(this.eventNameMap.addkey0, key, value);
+ break;
+ case 2:
+ this.emit(this.eventNameMap.addkey1, this.key, key, value);
+ break;
+ default: // 3
+ this.emit(this.eventNameMap.addkey2, this.pageKey, this.key, key, value);
+ break;
+ }
+ this.emit(this.eventNameMap.update, this.table.data);
+ }
+
+ removeCol(snapshot) {
+ var key = snapshot.key;
+ this.removeChild(key);
+
+ switch (this.type) {
+ case 1:
+ this.emit(this.eventNameMap.removekey0, key);
+ break;
+ case 2:
+ this.emit(this.eventNameMap.removekey1, this.key, key);
+ break;
+ default: // 3
+ this.emit(this.eventNameMap.removekey2, this.pageKey, this.key, key);
+ break;
+ }
+ this.emit(this.eventNameMap.update, this.table.data);
+ }
+
+ changeColValue(snapshot) {
+ var key = snapshot.key,
+ value = snapshot.val();
+ this.setData(key, value);
+
+ switch (this.type) {
+ case 1:
+ this.emit(this.eventNameMap.changekey0, key, value);
+ break;
+ case 2:
+ this.emit(this.eventNameMap.changekey1, this.key, key, value);
+ break;
+ default: // 3
+ this.emit(this.eventNameMap.changekey2, this.pageKey, this.key, key, value);
+ break;
+ }
+ this.emit(this.eventNameMap.update, this.table.data);
+ }
+
+ get pageKey() {
+ return this.parent.key;
+ }
+
+}
+
+export default ColumnUpdater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/Init.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/Init.js
new file mode 100644
index 000000000..b96a27b31
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/Init.js
@@ -0,0 +1,14 @@
+var Init = function () {
+ var self = this;
+ this.initialFlag = false;
+ return this.updater
+ .clear()
+ .load()
+ .then(function (value) {
+ self.initialFlag = true;
+ self.emit(self.eventNames.init, value);
+ return Promise.resolve(value);
+ })
+}
+
+export default Init;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/PageUpdater.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/PageUpdater.js
new file mode 100644
index 000000000..4990ca458
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/PageUpdater.js
@@ -0,0 +1,41 @@
+import BaseUpdater from './BaseUpdater.js';
+import RowUpdater from './RowUpdater.js';
+
+class PageUpdater extends BaseUpdater {
+ constructor(config) {
+ super(config);
+ }
+
+ startUpdate() {
+ this.rootRef.on('child_added', this.addPage, this);
+ this.rootRef.on('child_removed', this.removePage, this);
+ return this;
+ }
+
+ stopUpdate() {
+ this.rootRef.off('child_added', this.addPage, this);
+ this.rootRef.off('child_removed', this.removePage, this);
+ return this;
+ }
+
+ addPage(snapshot) {
+ var key = snapshot.key,
+ value = snapshot.val();
+ this.setData(key, value);
+
+ this.emit(this.eventNameMap.addkey0, key, value);
+ }
+
+ removePage(snapshot) {
+ var key = snapshot.key;
+ this.removeChild(key);
+
+ this.emit(this.eventNameMap.removekey0, key);
+ }
+
+ get childClass() {
+ return RowUpdater;
+ }
+}
+
+export default PageUpdater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/RowUpdater.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/RowUpdater.js
new file mode 100644
index 000000000..ab0d1385c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/read/RowUpdater.js
@@ -0,0 +1,55 @@
+import BaseUpdater from './BaseUpdater.js';
+import ColumnUpdater from './ColumnUpdater.js';
+
+class RowUpdater extends BaseUpdater {
+ startUpdate() {
+ this.rootRef.on('child_added', this.addRow, this);
+ this.rootRef.on('child_removed', this.removeRow, this);
+ return this;
+ }
+
+ stopUpdate() {
+ this.rootRef.off('child_added', this.addRow, this);
+ this.rootRef.off('child_removed', this.removeRow, this);
+ return this;
+ }
+
+ addRow(snapshot) {
+ var key = snapshot.key,
+ value = snapshot.val();
+ this.setData(key, value);
+
+ switch (this.type) {
+ case 2:
+ this.emit(this.eventNameMap.addkey0, this.key, key, value);
+ break;
+ default: // 3
+ this.emit(this.eventNameMap.addkey1, this.key, key, value);
+ break;
+ }
+ }
+
+ removeRow(snapshot) {
+ var key = snapshot.key;
+ this.removeChild(key);
+
+ switch (this.type) {
+ case 2:
+ this.emit(this.eventNameMap.removekey0, key);
+ break;
+ default: // 3
+ this.emit(this.eventNameMap.removekey1, this.key, key);
+ break;
+ }
+ }
+
+ get childClass() {
+ return ColumnUpdater;
+ }
+
+ get pageKey() {
+ return this.parent.key;
+ }
+}
+
+export default RowUpdater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/schema.md
new file mode 100644
index 000000000..50dec1210
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/schema.md
@@ -0,0 +1,14 @@
+## 1D table
+
+- : Number, string, or JSON
+
+## 2D table
+
+-
+ - : Number, string, or JSON
+
+## 3D table
+
+-
+ -
+ - : Number, string, or JSON
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/IncValue.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/IncValue.js
new file mode 100644
index 000000000..41e60c834
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/IncValue.js
@@ -0,0 +1,27 @@
+var IncValue = function () {
+ var key0, key1, key2, value;
+
+ switch (arguments.length) {
+ case 4:
+ [key0, key1, key2, value] = arguments;
+ break;
+ case 3:
+ [key0, key1, value] = arguments;
+ break;
+ case 2:
+ [key0, value] = arguments;
+ break;
+ default:
+ value = arguments[0];
+ break;
+ }
+
+ return this.getRef(key0, key1, key2).transaction(function (preValue) {
+ if (preValue === null) {
+ preValue = 0;
+ }
+ return (preValue + value);
+ });
+}
+
+export default IncValue;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveData.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveData.js
new file mode 100644
index 000000000..8955b7a5d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveData.js
@@ -0,0 +1,19 @@
+var RemoveData = function () {
+ var key0, key1, key2;
+
+ switch (arguments.length) {
+ case 3:
+ [key0, key1, key2] = arguments;
+ break;
+ case 2:
+ [key0, key1] = arguments;
+ break;
+ default:
+ key0 = arguments[0];
+ break;
+ }
+
+ return this.getRef(key0, key1, key2).remove();
+}
+
+export default RemoveData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveDataOnDisconnect.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveDataOnDisconnect.js
new file mode 100644
index 000000000..2744ec2d7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/RemoveDataOnDisconnect.js
@@ -0,0 +1,19 @@
+var RemoveDataOnDisconnect = function () {
+ var key0, key1, key2;
+
+ switch (arguments.length) {
+ case 3:
+ [key0, key1, key2] = arguments;
+ break;
+ case 2:
+ [key0, key1] = arguments;
+ break;
+ case 1:
+ key0 = arguments[0];
+ break;
+ }
+
+ return this.getRef(key0, key1, key2).onDisconnect().remove();
+}
+
+export default RemoveDataOnDisconnect;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetData.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetData.js
new file mode 100644
index 000000000..5daca28c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetData.js
@@ -0,0 +1,22 @@
+var SetData = function () {
+ var key0, key1, key2, value;
+
+ switch (arguments.length) {
+ case 4:
+ [key0, key1, key2, value] = arguments;
+ break;
+ case 3:
+ [key0, key1, value] = arguments;
+ break;
+ case 2:
+ [key0, value] = arguments;
+ break;
+ default:
+ value = arguments[0];
+ break;
+ }
+
+ return this.getRef(key0, key1, key2).set(value);
+}
+
+export default SetData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetDataOnDisconnect.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetDataOnDisconnect.js
new file mode 100644
index 000000000..809b3f3ad
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/SetDataOnDisconnect.js
@@ -0,0 +1,22 @@
+var SetDataOnDisconnect = function () {
+ var key0, key1, key2, value;
+
+ switch (arguments.length) {
+ case 4:
+ [key0, key1, key2, value] = arguments;
+ break;
+ case 3:
+ [key0, key1, value] = arguments;
+ break;
+ case 2:
+ [key0, value] = arguments;
+ break;
+ default:
+ value = arguments[0];
+ break;
+ }
+
+ return this.getRef(key0, key1, key2).onDisconnect().set(value);
+}
+
+export default SetDataOnDisconnect;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/Transaction.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/Transaction.js
new file mode 100644
index 000000000..fa2d1d5df
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/Transaction.js
@@ -0,0 +1,23 @@
+var Transaction = function () {
+ var key0, key1, key2, callback;
+
+ switch (arguments.length) {
+ case 4:
+ [key0, key1, key2, callback] = arguments;
+ break;
+ case 3:
+ [key0, key1, callback] = arguments;
+ break;
+ case 2:
+ [key0, callback] = arguments;
+ break;
+ default:
+ callback = arguments[0];
+ break;
+ }
+
+ // callback: function(preValue) { return newValue; }
+ return this.getRef(key0, key1, key2).transaction(callback);
+}
+
+export default Transaction;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/UpdateData.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/UpdateData.js
new file mode 100644
index 000000000..07c9148dc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/itemtable/write/UpdateData.js
@@ -0,0 +1,5 @@
+var UpdateData = function (data) {
+ return this.getRef().update(data);
+}
+
+export default UpdateData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/ChangeUserName.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/ChangeUserName.js
new file mode 100644
index 000000000..f348d3c9f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/ChangeUserName.js
@@ -0,0 +1,24 @@
+var ChangeUserName = function (userName) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ var userRef = self.getUserRef();
+ if (userRef) { // Find userRef
+ resolve(userRef)
+ } else { // Query userRef
+ var query = self.rootRef.orderByChild('userID').equalTo(self.userID);
+ query.once('child_added')
+ .then(function (snapshot) {
+ resolve(snapshot.ref)
+ })
+ }
+ })
+ .then(function (userRef) { // Set userName
+ return userRef.child('userName').set(userName)
+ })
+ .then(function () {
+ self.userName = userName;
+ return Promise.resolve();
+ })
+}
+
+export default ChangeUserName;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.d.ts
new file mode 100644
index 000000000..e7bdafd98
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.d.ts
@@ -0,0 +1,5 @@
+import OnlineUserList from './OnlineUserList';
+
+export default function (
+ config: OnlineUserList.IConfig
+): OnlineUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.js
new file mode 100644
index 000000000..9599445d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Factory.js
@@ -0,0 +1,11 @@
+import OnlineUserList from './OnlineUserList.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('onlineUserList', function (config) {
+ return new OnlineUserList(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.OnlineUserList', OnlineUserList);
+
+export default OnlineUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Join.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Join.js
new file mode 100644
index 000000000..391a9447e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Join.js
@@ -0,0 +1,69 @@
+import Delay from '../../../utils/promise/Delay.js';
+
+var Join = function (userID, userName) {
+ if (userID === undefined) {
+ userID = this.userID;
+ userName = this.userName;
+ }
+
+ if (this.contains(userID)) {
+ return Promise.resolve(); // Promise
+ }
+
+ // Prepare data
+ var d = {
+ userID: userID,
+ userName: userName
+ };
+ var maxUsers = this.maxUsers;
+ var rootRef = this.database.ref(this.rootPath);
+ var userRef = rootRef.push();
+
+ return userRef.onDisconnect().remove()
+ .then(function () {
+ return userRef.set(d)
+ })
+ .then(function () {
+ return Delay(0);
+ })
+ .then(function () {
+ // No user count limitation
+ if (maxUsers === 0) {
+ self.isInList = true;
+ return Promise.resolve();
+ }
+
+ // Has user count limitation
+ return rootRef.limitToFirst(maxUsers).once('value')
+ .then(function (snapshot) {
+ if (Contains(snapshot, userID)) {
+ self.isInList = true;
+ return Promise.resolve();
+ }
+
+ self.isInList = false;
+ // UserID is not in firstN list
+ return userRef.remove()
+ .then(function () {
+ return userRef.onDisconnect().cancel()
+ })
+ .then(function () {
+ return Promise.reject()
+ })
+ });
+ })
+};
+
+var Contains = function (snapshot, userID) {
+ var result = false;
+ snapshot.forEach(function (childSnapshot) {
+ var user = childSnapshot.val();
+ if (user.userID === userID) {
+ result = true;
+ return true;
+ }
+ });
+ return result;
+}
+
+export default Join;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Leave.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Leave.js
new file mode 100644
index 000000000..1e684426f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/Leave.js
@@ -0,0 +1,14 @@
+var Leave = function (userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+
+ if (!this.contains(userID)) {
+ return Promise.resolve(); // Promise
+ }
+ var itemID = this.userID2ItemID[userID];
+ var userRef = this.database.ref(this.rootPath).child(itemID);
+ return userRef.remove(); // Promise
+}
+
+export default Leave;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.d.ts
new file mode 100644
index 000000000..858ce426c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.d.ts
@@ -0,0 +1,58 @@
+import EventEmitter from "../../../utils/eventemitter/EventEmitter";
+
+export default OnlineUserList;
+
+declare namespace OnlineUserList {
+
+ interface IConfig {
+ root?: string,
+ maxUsers?: number,
+
+ userID?: string, userName?: string,
+
+ eventEmitter?: EventEmitter | false,
+ }
+}
+
+declare class OnlineUserList extends EventEmitter {
+ constructor(
+ config?: OnlineUserList.IConfig
+ );
+
+ setUser(
+ userID: string, userName?: string
+ ): this;
+
+ setUser(
+ config: { userID: string, userName?: string }
+ ): this;
+
+ userID: string;
+ userName: string;
+ readonly userInfo: { userID?: string, userName?: string };
+
+ join(
+ ): Promise;
+
+ leave(
+ userID?: string,
+ ): Promise;
+
+ changeUserName(
+ userName: string
+ ): Promise;
+
+ getUsers(
+ ): { userID?: string, userName?: string, }[];
+
+ isFirstUser(
+ userID?: string
+ ): boolean;
+
+ isFull(): boolean;
+
+ readonly maxUsers: number;
+
+ readonly isInList: boolean;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.js
new file mode 100644
index 000000000..af3e2ecce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/OnlineUserList.js
@@ -0,0 +1,203 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import ItemList from '../utils/itemlist/ItemList.js';
+import Join from './Join.js';
+import Leave from './Leave.js';
+import ChangeUserName from './ChangeUserName.js';
+
+class OnlineUserList {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+
+ this.database = firebase.database();
+ this.setRootPath(GetValue(config, 'root', ''));
+
+ this.userInfo = { userID: '', userName: '' };
+ this.setUser(GetValue(config, 'userID', ''), GetValue(config, 'userName', ''));
+ this.setMaxUsers(GetValue(config, 'maxUsers', 0));
+ this.userList = new ItemList({
+ eventEmitter: this.getEventEmitter(),
+ itemIDKey: 'joinAt',
+ eventNames: {
+ add: GetValue(config, 'eventNames.join', 'join'),
+ remove: GetValue(config, 'eventNames.leave', 'leave'),
+ update: GetValue(config, 'eventNames.update', 'update'),
+ change: GetValue(config, 'eventNames.change', 'change'),
+ init: GetValue(config, 'eventNames.init', 'init'),
+ changename: GetValue(config, 'eventNames.changename', 'changename')
+ }
+ });
+
+ this.isInList = false;
+ this.userID2ItemID = {};
+ this.userList
+ .on(this.userList.eventNames.add, function (user) {
+ this.userID2ItemID[user.userID] = user.joinAt;
+ if (user.userID === this.userInfo.userID) {
+ this.emit(this.userList.eventNames.init, this.getUsers());
+ }
+ }, this)
+ .on(this.userList.eventNames.remove, function (user) {
+ delete this.userID2ItemID[user.userID];
+
+ if (user.userID === this.userID) {
+ this.isInList = false;
+ }
+ }, this)
+ .on(this.userList.eventNames.change, function (currUserInfo, prevUserInfo) {
+ var userID = currUserInfo.userID,
+ userName = currUserInfo.userName,
+ prevUserName = prevUserInfo.userName;
+ if (userName !== prevUserName) {
+ this.emit(this.userList.eventNames.changename, userID, userName, prevUserName);
+ }
+ }, this)
+ }
+
+ shutdown() {
+ this
+ .stopUpdate()
+ .destroyEventEmitter()
+ .leave();
+
+ this.userList.shutdown();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ setRootPath(rootPath) {
+ this.rootPath = rootPath;
+ return this;
+ }
+
+ get rootRef() {
+ return this.database.ref(this.rootPath);
+ }
+
+ setUser(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ setMaxUsers(maxUsers) {
+ this.maxUsers = maxUsers;
+ return this;
+ }
+
+ clear() {
+ this.userList.clear();
+ return this;
+ }
+
+ forEach(callback, scope) {
+ this.userList.forEach(callback, scope);
+ return this;
+ }
+
+ isFull() {
+ if (this.maxUsers === 0) {
+ return false;
+ }
+ return (this.userList.getItems().length >= this.maxUsers);
+ }
+
+ isFirstUser(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+ var user = this.usersList.getItems()[0];
+ return (user && (user.userID === userID));
+ }
+
+ getUser(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+ if (!this.contains(userID)) {
+ return null;
+ }
+ var itemID = this.userID2ItemID[userID];
+ return this.userList.getItemFromItemID(itemID);
+ }
+
+ getUsers() {
+ return this.userList.getItems();
+ }
+
+ get rootRef() {
+ return this.database.ref(this.rootPath);
+ }
+
+ getUserRef(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+ if (!this.contains(userID)) {
+ return null;
+ }
+ var itemID = this.userID2ItemID[userID];
+ return this.rootRef.child(itemID);
+ }
+
+ contains(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+ return this.userID2ItemID.hasOwnProperty(userID);
+ }
+
+ startUpdate() {
+ var query = this.database.ref(this.rootPath);
+ if (this.maxUsers > 0) {
+ query = query.limitToFirst(this.maxUsers);
+ }
+ this.userList.startUpdate(query);
+ return this;
+ }
+
+ stopUpdate() {
+ this.userList.stopUpdate();
+ return this;
+ }
+}
+
+var methods = {
+ join: Join,
+ leave: Leave,
+ changeUserName: ChangeUserName
+}
+
+Object.assign(
+ OnlineUserList.prototype,
+ EventEmitterMethods,
+ methods
+);
+
+export default OnlineUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/schema.md
new file mode 100644
index 000000000..955dcf481
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/onlineuserlist/schema.md
@@ -0,0 +1,3 @@
+-
+ - `userID` - Unique ID of user
+ - `userName` - Name of the user
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeFilterData.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeFilterData.js
new file mode 100644
index 000000000..6857cb7eb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeFilterData.js
@@ -0,0 +1,20 @@
+var ChangeFilterData = function (roomID, filterData) {
+ if (arguments.length === 1) {
+ filterData = roomID;
+ roomID = undefined;
+ }
+ if (roomID === undefined) {
+ roomID = this.roomID;
+ }
+
+ var self = this;
+ return this.hasRoom(roomID)
+ .then(function (hasRoom) {
+ if (!hasRoom) {
+ return Promise.resolve();
+ }
+ return self.getRoomFilterRef(roomID).child('data').update(filterData)
+ })
+}
+
+export default ChangeFilterData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomName.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomName.js
new file mode 100644
index 000000000..eed19b891
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomName.js
@@ -0,0 +1,23 @@
+var ChangeRoomName = function (roomID, roomName) {
+ if (arguments.length === 1) {
+ roomName = roomID;
+ roomID = undefined;
+ }
+ if (roomID === undefined) {
+ roomID = this.roomID;
+ }
+
+ var self = this;
+ return this.hasRoom(roomID)
+ .then(function (hasRoom) {
+ if (!hasRoom) {
+ return Promise.resolve();
+ }
+ var d = {};
+ d[`room-filters/${roomID}/name`] = roomName;
+ d[`room-data/${roomID}/name`] = roomName;
+ return self.getRootRef().update(d)
+ })
+}
+
+export default ChangeRoomName;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomState.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomState.js
new file mode 100644
index 000000000..8e7deafc4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeRoomState.js
@@ -0,0 +1,39 @@
+import { GetFilterString } from './utils/RoomFilterMethods.js';
+
+var ChangeRoomState = function (roomID, roomState) {
+ if (arguments.length === 1) {
+ roomState = roomID;
+ roomID = undefined;
+ }
+ if (roomID === undefined) {
+ roomID = this.roomID;
+ }
+
+ var self = this;
+ return this.hasRoom(roomID)
+ .then(function (hasRoom) {
+ if (!hasRoom) {
+ return Promise.resolve();
+ }
+
+ var filter = GetFilterString(roomState, self.roomType);
+ var d = {};
+ d[`room-filters/${roomID}/filter`] = filter;
+ d[`room-data/${roomID}/filter`] = filter;
+ return self.getRootRef().update(d)
+ })
+}
+
+var OpenRoom = function (roomID) {
+ return this.setRoomState(roomID, 'open');
+}
+
+var CloseRoom = function (roomID) {
+ return this.setRoomState(roomID, 'closed');
+}
+
+export {
+ ChangeRoomState,
+ OpenRoom,
+ CloseRoom
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeUserName.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeUserName.js
new file mode 100644
index 000000000..c1bc5ccb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/ChangeUserName.js
@@ -0,0 +1,5 @@
+var ChangeUserName = function (userName) {
+ return this.userList.changeUserName(userName);
+}
+
+export default ChangeUserName;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRandomRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRandomRoom.js
new file mode 100644
index 000000000..fb7ab732c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRandomRoom.js
@@ -0,0 +1,29 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import GetRandomWord from '../../../utils/string/GetRandomWord.js';
+
+var CreateRandomRoom = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ var digits = GetValue(config, 'digits', 10);
+ var candidates = GetValue(config, 'candidates', '0123456789');
+ var retry = GetValue(config, 'retry', 1000);
+
+ return TryCreateRandomRoom.call(this, digits, candidates, retry, config);
+}
+
+var TryCreateRandomRoom = function (digits, candidates, retry, config) {
+ config.roomID = GetRandomWord(digits, digits, candidates);
+ if (retry <= 0) {
+ return Promise.reject(config);
+ }
+ retry--;
+ var self = this;
+ return this.createRoom(config)
+ .catch(function () {
+ return TryCreateRandomRoom.call(self, digits, candidates, retry, config);
+ })
+}
+
+export default CreateRandomRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRoom.js
new file mode 100644
index 000000000..3e35e2d8b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/CreateRoom.js
@@ -0,0 +1,115 @@
+import MergeRight from '../../../utils/object/MergeRight.js';
+import { GetFilterString } from './utils/RoomFilterMethods.js';
+import OnJoinRoom from './utils/OnJoinRoom.js';
+
+var TryCreateRoom = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+ if (config.roomID == null) {
+ config.roomID = this.getRoomRef().push().key;
+ }
+
+ var self = this;
+ return RegisterRoom.call(self, config.roomID)
+ .then(function () { // Create room
+ return CreateRoom.call(self, config);
+ });
+}
+
+var RegisterRoom = function (roomID) {
+ return this.getRoomAliveRef(roomID)
+ .transaction(function (value) {
+ if (value === null) { // Room is not existed, register success
+ return true;
+ }
+ else { // Room is existed, register fail
+ return; // Abort the transaction
+ }
+ });
+}
+
+var CreateRoom = function (config) {
+ config = MergeRight(DefaultConfig, config);
+ var roomID = config.roomID;
+ var roomName = config.roomName;
+ var roomType = config.roomType;
+ var doorState = config.door;
+ var join = config.join;
+ var filterData = config.filterData;
+
+ var roomRef = this.getRoomRef(roomID);
+ var roomFilterRef = this.getRoomFilterRef(roomID);
+ var roomMetadataRef = this.getRoomDataRef(roomID);
+
+ // Remove room when creater is offline
+ this.isRemoveRoomWhenLeft = !config.presisted;
+ if (this.isRemoveRoomWhenLeft) {
+ roomRef.onDisconnect().remove();
+ roomFilterRef.onDisconnect().remove();
+ roomMetadataRef.onDisconnect().remove();
+ }
+
+ var filter = GetFilterString(doorState, roomType);
+
+ var d = {};
+
+ // Room-filter
+ var roomFilterData = {
+ filter: filter,
+ name: roomName
+ };
+ if (filterData) {
+ roomFilterData.data = filterData;
+ }
+ d[`room-filters/${roomID}`] = roomFilterData;
+
+ // Room-metadata
+ var roomMetadata = {
+ name: roomName,
+ filter: filter,
+ maxUsers: config.maxUsers,
+ moderators: {}
+ };
+ roomMetadata.moderators[this.userID] = this.userName;
+ d[`room-data/${roomID}`] = roomMetadata;
+
+
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ if (join) {
+ var promise = self.userList
+ .setRootPath(self.getUserListPath(roomID))
+ .setMaxUsers(0) // Don't test max user count
+ .join(); // Promise
+ self.userList
+ .setMaxUsers(config.maxUsers);
+ return promise.then(resolve, reject);
+ } else {
+ return resolve();
+ }
+ })
+ .then(function () {
+ return self.getRootRef().update(d)
+ })
+ .then(function () {
+ self.isRoomCreator = true;
+ if (join) {
+ OnJoinRoom.call(self, config);
+ }
+ return Promise.resolve(config);
+ })
+}
+
+var DefaultConfig = {
+ roomID: '',
+ roomName: '',
+ roomType: '',
+ maxUsers: 0,
+ presisted: false,
+ door: 'open',
+ join: true,
+ filterData: undefined
+}
+
+export default TryCreateRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Factory.js
new file mode 100644
index 000000000..4b9f9726a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Factory.js
@@ -0,0 +1,11 @@
+import Room from './Room.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('room', function (config) {
+ return new Room(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.Room', Room);
+
+export default Room;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRefMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRefMethods.js
new file mode 100644
index 000000000..0dfef0304
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRefMethods.js
@@ -0,0 +1,87 @@
+import { GetFilterString } from './utils/RoomFilterMethods.js';
+
+var Methods = {
+ getRootRef(childKey) {
+ var ref = this.database.ref(this.rootPath);
+ if (childKey) {
+ ref = ref.child(childKey);
+ }
+ return ref;
+ },
+
+ getRoomRef(roomID, childKey) {
+ var ref = this.getRootRef('rooms');
+ if (roomID !== undefined) {
+ ref = ref.child(roomID);
+ if (childKey !== undefined) {
+ ref = ref.child(childKey);
+ }
+ }
+ return ref;
+ },
+
+ getRoomAliveRef(roomID) {
+ return this.getRoomRef(roomID, 'alive');
+ },
+
+ getUserListRef(roomID) {
+ return this.getRoomRef(roomID, 'users');
+ },
+
+ getRoomFilterRef(roomID) {
+ var ref = this.getRootRef('room-filters');
+ if (roomID !== undefined) {
+ ref = ref.child(roomID);
+ }
+ return ref;
+ },
+
+ getRoomDataRef(roomID) {
+ var ref = this.getRootRef('room-data');
+ if (roomID !== undefined) {
+ ref = ref.child(roomID);
+ }
+ return ref;
+ },
+
+ // TODO: ??
+ getUserDataRef(userID) {
+ var ref = this.getRootRef('user-data');
+ if (userID !== undefined) {
+ ref = ref.child(userID);
+ }
+ return ref;
+ },
+
+ getRoomDataPath(roomID, childKey) {
+ var path = `${this.rootPath}/rooms/${roomID}`;
+ if (childKey) {
+ path += `/${childKey}`;
+ }
+ return path;
+ },
+
+ getUserListPath(roomID) {
+ return this.getRoomDataPath(roomID, 'users');
+ },
+
+ getItemTablePath(roomID, key) {
+ return `${this.getRoomDataPath(roomID, 'tables')}/${key}`;
+ },
+
+ getRoomListQuery(roomType, roomState) {
+ if (roomState === undefined) {
+ roomState = 'open';
+ }
+ var query = this.getRoomFilterRef();
+ query = query.orderByChild('filter');
+ if (roomType === undefined) {
+ query = query.startAt(roomState).endAt(`${roomState}~`);
+ } else {
+ query = query.equalTo(GetFilterString(roomState, roomType));
+ }
+ return query;
+ }
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRoomList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRoomList.js
new file mode 100644
index 000000000..c44da7e3c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetRoomList.js
@@ -0,0 +1,12 @@
+var GetRoomList = function (roomType, roomState) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.roomList
+ .once('roomlist.update', function (rooms) {
+ resolve(rooms)
+ })
+ .startUpdate(self.getRoomListQuery(roomType, roomState));
+ })
+}
+
+export default GetRoomList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetUserList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetUserList.js
new file mode 100644
index 000000000..388b8c588
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/GetUserList.js
@@ -0,0 +1,23 @@
+import ItemList from '../utils/itemlist/ItemList.js';
+
+var GetUserList = function (roomID) {
+ if (roomID === undefined) {
+ return this.userList.getUsers();
+ }
+
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ var userList = new ItemList({
+ itemIDKey: 'joinAt',
+ mode: 'once'
+ })
+
+ userList
+ .once('update', function (users) {
+ resolve(users)
+ })
+ .startUpdate(self.getUserListRef(roomID));
+ })
+}
+
+export default GetUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/HasRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/HasRoom.js
new file mode 100644
index 000000000..131feea09
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/HasRoom.js
@@ -0,0 +1,13 @@
+var HasRoom = function (roomID) {
+ if (roomID === this.roomID) {
+ return Promise.resolve(true);
+ }
+
+ return this.getRoomDataRef(roomID).once('value')
+ .then(function (snapshot) {
+ var hasRoom = (snapshot.val() !== null);
+ return Promise.resolve(hasRoom);
+ })
+}
+
+export default HasRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/IsRoomOpened.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/IsRoomOpened.js
new file mode 100644
index 000000000..8c4ec27bf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/IsRoomOpened.js
@@ -0,0 +1,33 @@
+import { GetRoomState } from './utils/RoomFilterMethods.js';
+
+var IsRoomOpened = function (metadata) {
+ if (metadata == null) {
+ return false;
+ }
+
+ var state = GetRoomState(metadata.filter);
+ if (state === 'closed') {
+ return false;
+ }
+
+ var userID = this.userID;
+ var IsModerator = metadata.moderators.hasOwnProperty(userID);
+ if (IsModerator) {
+ return true;
+ }
+
+ switch (metadata.permission) {
+ case 'black-list':
+ var blackList = metadata['black-list'];
+ return !(blackList && blackList.hasOwnProperty(userID));
+
+ case 'white-list':
+ var whiteList = metadata['white-list'];
+ return whiteList && whiteList.hasOwnProperty(userID);
+
+ default: // 'anyone'
+ return true;
+ }
+}
+
+export default IsRoomOpened;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRandomRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRandomRoom.js
new file mode 100644
index 000000000..99d3be3b7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRandomRoom.js
@@ -0,0 +1,32 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import Shuffle from '../../../utils/array/Shuffle.js';
+
+var JoinRandomRoom = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ var roomType = GetValue(config, 'roomType', '');
+ var roomState = GetValue(config, 'door', 'open');
+ var self = this;
+ return this.getRoomList(roomType, roomState)
+ .then(function (rooms) {
+ Shuffle(rooms);
+ return JoinNextRoom.call(self, config, rooms, 0);
+ })
+}
+
+var JoinNextRoom = function (config, rooms, index) {
+ if (index === rooms.length) {
+ return Promise.reject();
+ }
+ config.roomID = rooms[index].roomID;
+ index++;
+ var self = this;
+ return this.joinRoom(config)
+ .catch(function () {
+ return JoinNextRoom.call(self, config, rooms, index);
+ })
+}
+
+export default JoinRandomRoom;
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRoom.js
new file mode 100644
index 000000000..0b3097b72
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/JoinRoom.js
@@ -0,0 +1,59 @@
+import { GetRoomType } from './utils/RoomFilterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import OnJoinRoom from './utils/OnJoinRoom.js';
+
+var TryJoinRoom = function (config) {
+ var leftThenJoin = GetValue(config, 'leftThenJoin', true);
+
+ var self = this;
+ if (leftThenJoin) {
+ return this.leaveRoom()
+ .then(function () {
+ return JoinRoom.call(self, config);
+ })
+ } else {
+ return JoinRoom.call(self, config);
+ }
+}
+
+var JoinRoom = function (config) {
+ var roomID = GetValue(config, 'roomID', undefined);
+ if (roomID === undefined) {
+ return Promise.reject();
+ }
+
+ this.isRemoveRoomWhenLeft = false;
+ var self = this;
+ return IsRoomOpened.call(self, config)
+ .then(function (metadata) {
+ return self.userList
+ .setRootPath(self.getUserListPath(config.roomID))
+ .setMaxUsers(metadata.maxUsers)
+ .join();
+ })
+ .then(function () {
+ OnJoinRoom.call(self, config);
+ return Promise.resolve(config);
+ })
+}
+
+var IsRoomOpened = function (config) {
+ var self = this;
+ return this.getRoomDataRef(config.roomID).once('value')
+ .then(function (snapshot) {
+ var metadata = snapshot.val();
+ if (metadata === null) { // Can't find room
+ return Promise.reject();
+ }
+
+ config.roomName = metadata.name;
+ config.roomType = GetRoomType(metadata.filter);
+ if (!self.isRoomOpened(metadata)) {
+ return Promise.reject();
+ } else {
+ return Promise.resolve(metadata);
+ }
+ });
+}
+
+export default TryJoinRoom;
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/KickUser.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/KickUser.js
new file mode 100644
index 000000000..b2ee04bb4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/KickUser.js
@@ -0,0 +1,12 @@
+var KickUser = function (userID) {
+ if (!this.userList.contains(userID)) {
+ return Promise.resolve();
+ } else if (userID === this.userID) {
+ return this.leaveRoom();
+ } else {
+ // TODO: Who can kick user?
+ return this.userList.leave(userID);
+ }
+}
+
+export default KickUser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/LeaveRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/LeaveRoom.js
new file mode 100644
index 000000000..b9a869dc8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/LeaveRoom.js
@@ -0,0 +1,21 @@
+var LeaveRoom = function () {
+ if (!this.isInRoom()) {
+ return Promise.resolve();
+ }
+
+ // 'userlist.leave' event -> 'room.leave' event -> then
+ this.leftRoomFlag = true;
+ if (this.isRemoveRoomWhenLeft) {
+ // Remove room, include user list
+ return this.removeRoom()
+ } else {
+ var prevRoomInfo = this.getRoomInfo();
+ // Leave user list only
+ return this.userList.leave()
+ .then(function () {
+ return Promise.resolve(prevRoomInfo)
+ })
+ }
+}
+
+export default LeaveRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Methods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Methods.js
new file mode 100644
index 000000000..d87b622e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Methods.js
@@ -0,0 +1,43 @@
+import CreateRoom from './CreateRoom.js';
+import CreateRandomRoom from './CreateRandomRoom.js';
+import JoinRoom from './JoinRoom.js';
+import JoinRandomRoom from './JoinRandomRoom.js';
+import LeaveRoom from './LeaveRoom.js';
+import RemoveRoom from './RemoveRoom.js';
+import KickUser from './KickUser.js';
+import IsRoomOpened from './IsRoomOpened.js';
+import { ChangeRoomState, OpenRoom, CloseRoom } from './ChangeRoomState.js';
+import ChangeFilterData from './ChangeFilterData.js';
+import ChangeUserName from './ChangeUserName.js';
+import ChangeRoomName from './ChangeRoomName.js';
+import GetUserList from './GetUserList.js';
+import GetRoomList from './GetRoomList.js';
+import HasRoom from './HasRoom.js';
+import GetRefMethods from './GetRefMethods.js';
+
+var Methods = {
+ createRoom: CreateRoom,
+ createRandomRoom: CreateRandomRoom,
+ joinRoom: JoinRoom,
+ joinRandomRoom: JoinRandomRoom,
+ leaveRoom: LeaveRoom,
+ removeRoom: RemoveRoom,
+ kickUser: KickUser,
+ isRoomOpened: IsRoomOpened,
+ changeRoomState: ChangeRoomState,
+ changeFilterData: ChangeFilterData,
+ changeUserName: ChangeUserName,
+ changeRoomName: ChangeRoomName,
+ openRoom: OpenRoom,
+ closeRoom: CloseRoom,
+ getUserList: GetUserList,
+ getRoomList: GetRoomList,
+ hasRoom: HasRoom
+}
+
+Object.assign(
+ Methods,
+ GetRefMethods
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/RemoveRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/RemoveRoom.js
new file mode 100644
index 000000000..6a173eb46
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/RemoveRoom.js
@@ -0,0 +1,21 @@
+var RemoveRoom = function (roomID) {
+ if (roomID === undefined) {
+ roomID = this.roomID;
+ }
+ if (roomID === undefined) {
+ return Promise.resolve();
+ }
+
+ var d = {};
+ d[`room-filter/${roomID}`] = null;
+ d[`room-data/${roomID}`] = null;
+ d[`rooms/${roomID}`] = null;
+
+ var prevRoomInfo = this.getRoomInfo();
+ return this.getRootRef().update(d)
+ .then(function () {
+ return Promise.resolve(prevRoomInfo);
+ })
+}
+
+export default RemoveRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Room.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Room.js
new file mode 100644
index 000000000..159e75adc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/Room.js
@@ -0,0 +1,130 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import CreateUserList from './utils/CreateUserList.js';
+import CreateRoomList from './utils/CreateRoomList.js';
+import CreateBroadcast from './utils/CreateBroadcast.js';
+import CreateTables from './utils/CreateTables.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Methods from './Methods.js';
+
+class Room {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+
+ this.database = firebase.database()
+ this.rootPath = GetValue(config, 'root', '');
+
+ // User properties
+ this.userInfo = { userID: '', userName: '' };
+ this.setUser(GetValue(config, 'userID', ''), GetValue(config, 'userName', ''));
+ // Room properties
+ this.isRoomCreator = false;
+ this.roomID = undefined;
+ this.roomName = undefined;
+ this.roomType = undefined;
+ this.doorState = undefined;
+ this.leftRoomFlag = false;
+ this.isRemoveRoomWhenLeft = undefined;
+ // User list
+ this.userList = CreateUserList.call(this, config);
+ // Room list
+ this.roomList = CreateRoomList.call(this, config);
+ // Broadcast
+ this.broadcast = CreateBroadcast.call(this, config);
+ // Item tables
+ this.tables = CreateTables.call(this, config);
+ }
+
+ shutdown() {
+ var self = this;
+ this
+ .destroyEventEmitter()
+ .leaveRoom()
+ .then(function () {
+ self.userList.destroy();
+ self.userList = undefined;
+
+ self.roomList.destroy();
+ self.roomList = undefined;
+
+ self.broadcast.destroy();
+ self.broadcast = undefined;
+ })
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ getRoomInfo(roomID, roomName) {
+ if (roomID === undefined) {
+ roomID = this.roomID;
+ }
+ if (roomName === undefined) {
+ roomName = this.roomName;
+ }
+ return { roomID: roomID, roomName: roomName };
+ }
+
+ setUser(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ isInRoom(roomID) {
+ return (roomID === undefined) ? (this.roomID !== undefined) : (this.roomID === roomID);
+ }
+
+ isFull() {
+ return this.userList.isFull();
+ }
+
+ isFirstUser(userID) {
+ return this.userList.isFirstUser(userID);
+ }
+
+ getUsers() {
+ return this.userList.getUsers();
+ }
+
+ get maxUsers() {
+ return this.userList.maxUsers;
+ }
+
+ getTable(key) {
+ return this.tables[key];
+ }
+}
+
+
+Object.assign(
+ Room.prototype,
+ EventEmitterMethods,
+ Methods
+);
+
+export default Room;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/schema.md
new file mode 100644
index 000000000..56064f7bb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/schema.md
@@ -0,0 +1,65 @@
+```
+# Filter to monitor opened rooms
+room-filters/
+
+ filter - open/closed + "|" + public/private/...
+ name - The display name of the room
+ data - null, or JSON data
+
+
+# Header of room, write by owner of room. Each room has unique roomID
+room-data/
+
+ name - The display name of the room
+
+ # monitor filter to catch room open/closed event
+ filter - open/closed + "|" + public/private/...
+
+ # moderators of this room
+ moderators/
+ - Unique ID of user
+
+ # join permission
+ permission - null("anyone")/("black-list")/("white-list")
+ black-list/
+ - Unique ID of user
+ white-list/
+ - Unique ID of user
+ # ignore room if user can not join
+
+ maxUsers - The maximum number of users that can join this room.
+ # limit the amount of users
+
+ table/
+ -
+ -
+ - : value
+
+# Body of room data. Each room has unique roomID
+rooms/
+
+ alive - true or null
+
+ # users in this room.
+ users/
+
+ userID - Unique ID of user
+ userName - The name of the user
+
+ broadcast/
+ - `message` - Message
+ - `senderID` - Unique ID of sender
+ - `senderName` - Name of sender
+ - `stamp` - Toggle between true and false
+
+
+# Write by each user, user could join to many rooms
+user-data\
+
+ user/
+ ID - Unique ID of user
+ name - The display name of the user
+ room/
+ ID - The id of the room
+ name - The display name of the room
+```
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateBroadcast.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateBroadcast.js
new file mode 100644
index 000000000..cbdfd6e59
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateBroadcast.js
@@ -0,0 +1,37 @@
+import Broadcast from '../../broadcast/Broadcast.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+
+var CreateBroadcast = function (config) {
+ var broadcastConfig = GetValue(config, 'broadcast', true);
+ if (!broadcastConfig) {
+ return null;
+ }
+
+ var broadcast = new Broadcast({
+ eventEmitter: this.getEventEmitter(),
+ eventNames: {
+ receive: 'broadcast.receive'
+ },
+
+ receiverID: 'boradcast',
+ senderID: this.userInfo,
+ history: GetValue(broadcastConfig, 'history', false)
+ });
+
+ this
+ .on('room.join', function (roomConfig) {
+ broadcast
+ .setRootPath(this.getRoomDataPath(roomConfig.roomID))
+ .startReceiving()
+ }, this)
+ .on('room.leave', function () {
+ broadcast.stopReceiving()
+ }, this)
+ .on('userlist.changename', function (userID, userName) {
+ broadcast.changeUserName(userID, userName);
+ }, this)
+
+ return broadcast;
+}
+
+export default CreateBroadcast;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateRoomList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateRoomList.js
new file mode 100644
index 000000000..46f1aac5b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateRoomList.js
@@ -0,0 +1,20 @@
+import ItemList from '../../utils/itemlist/ItemList.js';
+
+var CreateRoomList = function (config) {
+ var roomList = new ItemList({
+ eventEmitter: this.getEventEmitter(),
+ root: this.getRoomFilterRef(),
+ itemIDKey: 'roomID',
+ eventNames: {
+ update: 'roomlist.update',
+ add: 'roomlist.add',
+ remove: 'roomlist.remove',
+ change: 'roomlist.change'
+ },
+ mode: 'once'
+ })
+
+ return roomList;
+}
+
+export default CreateRoomList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateTables.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateTables.js
new file mode 100644
index 000000000..2c497bc11
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateTables.js
@@ -0,0 +1,55 @@
+import ItemTable from '../../itemtable/ItemTable.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+
+var CreateTables = function (config) {
+ var tablesConfig = GetValue(config, 'tables', undefined);
+ if (tablesConfig === undefined) {
+ return {};
+ }
+
+ var tableConfig;
+ var tables = {};
+ for (var i = 0, cnt = tablesConfig.length; i < cnt; i++) {
+ tableConfig = tablesConfig[i];
+ tables[tableConfig.key] = CreateTable.call(this, tableConfig);
+ }
+
+ return tables;
+}
+var CreateTable = function (config) {
+ var key = config.key;
+ var table = new ItemTable({
+ eventEmitter: this.getEventEmitter(),
+ root: this.getItemTablePath(this.roomID, key),
+
+ type: GetValue(config, 'type', 1),
+ eventNames: {
+ init: `tables.${key}.init`,
+ update: `tables.${key}.update`,
+ addkey0: `tables.${key}.addkey0`,
+ removekey0: `tables.${key}.removekey0`,
+ changekey0: `tables.${key}.changekey0`,
+ addkey1: `tables.${key}.addkey1`,
+ removekey1: `tables.${key}.removekey1`,
+ changekey1: `tables.${key}.changekey1`,
+ addkey2: `tables.${key}.addkey2`,
+ removekey2: `tables.${key}.removekey2`,
+ changekey2: `tables.${key}.changekey2`
+ }
+ });
+
+ this
+ .on('room.join', function () {
+ table
+ .startUpdate()
+ })
+ .on('room.leave', function () {
+ table
+ .clear()
+ .stopUpdate()
+ })
+
+ return table;
+}
+
+export default CreateTables;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateUserList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateUserList.js
new file mode 100644
index 000000000..e87d24b87
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/CreateUserList.js
@@ -0,0 +1,51 @@
+import OnlineUserList from '../../onlineuserlist/OnlineUserList.js';
+
+var CreateUserList = function (config) {
+ var userList = new OnlineUserList({
+ eventEmitter: this.getEventEmitter(),
+ eventNames: {
+ join: 'userlist.join', // Any user join
+ leave: 'userlist.leave', // Any user leave
+ update: 'userlist.update', // Update user list
+ change: 'userlist.change', // Any user(name) change
+ init: 'userlist.init',
+ changename: 'userlist.changename'
+ },
+
+ userID: this.userInfo
+ });
+ userList
+ .on('userlist.leave', function (user) {
+ if (user.userID === this.userID) {
+ OnLeftRoom.call(this); // Current user is left or kicked
+ }
+ }, this)
+
+ this
+ .on('room.join', function () {
+ userList
+ .startUpdate()
+ })
+ .on('room.leave', function () {
+ userList
+ .stopUpdate()
+ .clear()
+ })
+
+ return userList;
+}
+
+var OnLeftRoom = function () {
+ this.emit('room.leave');
+
+ // Clear room info later
+ var self = this;
+ setTimeout(function () {
+ self.roomID = undefined;
+ self.roomName = undefined;
+ self.doorState = undefined;
+ self.leftRoomFlag = false;
+ }, 0);
+}
+
+export default CreateUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/OnJoinRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/OnJoinRoom.js
new file mode 100644
index 000000000..b59a6b550
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/OnJoinRoom.js
@@ -0,0 +1,9 @@
+var OnJoinRoom = function (config) {
+ this.roomID = config.roomID;
+ this.roomName = config.roomName;
+ this.roomType = config.roomType;
+
+ this.emit('room.join', config);
+}
+
+export default OnJoinRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/RoomFilterMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/RoomFilterMethods.js
new file mode 100644
index 000000000..7a2ba7f01
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/room/utils/RoomFilterMethods.js
@@ -0,0 +1,20 @@
+var GetRoomState = function (filterString) {
+ return filterString.split('|')[0];
+}
+
+var GetRoomType = function (filterString) {
+ return filterString.split('|')[1];
+}
+
+var GetFilterString = function (roomState, roomType) {
+ if (roomType === undefined) {
+ roomType = '';
+ }
+ return `${roomState}|${roomType}`;
+}
+
+export {
+ GetRoomState,
+ GetRoomType,
+ GetFilterString
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/ChangeUserName.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/ChangeUserName.js
new file mode 100644
index 000000000..c1bc5ccb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/ChangeUserName.js
@@ -0,0 +1,5 @@
+var ChangeUserName = function (userName) {
+ return this.userList.changeUserName(userName);
+}
+
+export default ChangeUserName;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.d.ts
new file mode 100644
index 000000000..7b2b6d223
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.d.ts
@@ -0,0 +1,5 @@
+import SingleRoom from './SingleRoom';
+
+export default function (
+ config: SingleRoom.IConfig
+): SingleRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.js
new file mode 100644
index 000000000..9c1b1a3c0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Factory.js
@@ -0,0 +1,11 @@
+import SingleRoom from './SingleRoom.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('singleRoom', function (config) {
+ return new SingleRoom(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.SingleRoom', SingleRoom);
+
+export default SingleRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetRefMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetRefMethods.js
new file mode 100644
index 000000000..9c4739380
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetRefMethods.js
@@ -0,0 +1,31 @@
+var Methods = {
+ getRoomRef(childKey) {
+ var ref = this.database.ref(this.rootPath);
+ if (childKey) {
+ ref = ref.child(childKey);
+ }
+ return ref;
+ },
+
+ getUserListRef() {
+ return this.getRoomRef('users');
+ },
+
+ getRoomDataPath(childKey) {
+ var path = this.rootPath;
+ if (childKey) {
+ path += `/${childKey}`;
+ }
+ return path;
+ },
+
+ getUserListPath() {
+ return this.getRoomDataPath('users');
+ },
+
+ getItemTablePath(key) {
+ return `${this.getRoomDataPath('tables')}/${key}`;
+ }
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetUserList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetUserList.js
new file mode 100644
index 000000000..5b87c72c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/GetUserList.js
@@ -0,0 +1,5 @@
+var GetUserList = function () {
+ return this.userList.getUsers();
+}
+
+export default GetUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/JoinRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/JoinRoom.js
new file mode 100644
index 000000000..23a8d5d18
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/JoinRoom.js
@@ -0,0 +1,10 @@
+var JoinRoom = function () {
+ var self = this;
+ return this.userList.join()
+ .then(function () {
+ self.emit('room.join');
+ return Promise.resolve();
+ })
+}
+
+export default JoinRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/KickUser.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/KickUser.js
new file mode 100644
index 000000000..b2ee04bb4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/KickUser.js
@@ -0,0 +1,12 @@
+var KickUser = function (userID) {
+ if (!this.userList.contains(userID)) {
+ return Promise.resolve();
+ } else if (userID === this.userID) {
+ return this.leaveRoom();
+ } else {
+ // TODO: Who can kick user?
+ return this.userList.leave(userID);
+ }
+}
+
+export default KickUser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/LeaveRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/LeaveRoom.js
new file mode 100644
index 000000000..cd3627a3f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/LeaveRoom.js
@@ -0,0 +1,11 @@
+var LeaveRoom = function () {
+ if (!this.isInRoom()) {
+ return Promise.resolve();
+ }
+
+ // 'userlist.leave' event -> 'room.leave' event -> then
+ this.leftRoomFlag = true;
+ return this.userList.leave();
+}
+
+export default LeaveRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Methods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Methods.js
new file mode 100644
index 000000000..c6838d192
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/Methods.js
@@ -0,0 +1,21 @@
+import JoinRoom from './JoinRoom.js';
+import LeaveRoom from './LeaveRoom.js';
+import KickUser from './KickUser.js';
+import ChangeUserName from './ChangeUserName.js';
+import GetUserList from './GetUserList.js';
+import GetRefMethods from './GetRefMethods.js';
+
+var Methods = {
+ joinRoom: JoinRoom,
+ leaveRoom: LeaveRoom,
+ kickUser: KickUser,
+ changeUserName: ChangeUserName,
+ getUserList: GetUserList
+}
+
+Object.assign(
+ Methods,
+ GetRefMethods
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.d.ts
new file mode 100644
index 000000000..717c9a036
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.d.ts
@@ -0,0 +1,76 @@
+import EventEmitter from "../../../utils/eventemitter/EventEmitter";
+import Broadcast from "../broadcast/Broadcast";
+import ItemTable from "../itemtable/ItemTable";
+
+export default SingleRoom;
+
+declare namespace SingleRoom {
+ interface ITableConfig {
+ key: string,
+ type?: ItemTable.TableType
+ }
+
+ interface IConfig {
+ root?: string,
+ maxUsers?: number,
+ broadcast: boolean |
+ {
+ history?: number | boolean,
+ },
+ tables?: undefined | ITableConfig[],
+
+ userID?: string, userName?: string,
+
+ eventEmitter?: EventEmitter | false,
+ }
+}
+
+declare class SingleRoom extends EventEmitter {
+ constructor(config?: SingleRoom.IConfig);
+
+ setUser(
+ userID: string, userName?: string
+ ): this;
+
+ setUser(
+ config: { userID: string, userName?: string }
+ ): this;
+
+ userID: string;
+ userName: string;
+ readonly userInfo: { userID?: string, userName?: string };
+
+ joinRoom(
+ ): Promise;
+
+ leaveRoom(
+ ): Promise;
+
+ kickUser(
+ userID: string
+ ): Promise;
+
+ getUsers(
+ ): { userID?: string, userName?: string, }[];
+
+ isFirstUser(
+ userID?: string
+ ): boolean;
+
+ isFull(): boolean;
+
+ readonly maxUsers: number;
+
+ isInRoom(): boolean;
+
+ readonly broadcast: Broadcast;
+
+ changeUserName(
+ userName: string
+ ): Promise;
+
+ getTable(
+ key: string
+ ): ItemTable;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.js
new file mode 100644
index 000000000..1f09cfcfe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/SingleRoom.js
@@ -0,0 +1,95 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import CreateUserList from './utils/CreateUserList.js';
+import CreateBroadcast from './utils/CreateBroadcast.js';
+import CreateTables from './utils/CreateTables.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Methods from './Methods.js';
+
+class SingleRoom {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+
+ this.database = firebase.database()
+ this.rootPath = GetValue(config, 'root', '');
+
+ // User properties
+ this.userInfo = { userID: '', userName: '' };
+ this.setUser(GetValue(config, 'userID', ''), GetValue(config, 'userName', ''));
+ // Room properties
+ this.leftRoomFlag = false;
+ // User list
+ this.userList = CreateUserList.call(this, config);
+ // Broadcast
+ this.broadcast = CreateBroadcast.call(this, config);
+ // Item tables
+ this.tables = CreateTables.call(this, config);
+ }
+
+ shutdown() {
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ setUser(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ isInRoom() {
+ return this.userList.isInList;
+ }
+
+ isFull() {
+ return this.userList.isFull();
+ }
+
+ isFirstUser(userID) {
+ return this.userList.isFirstUser(userID);
+ }
+
+ getUsers() {
+ return this.userList.getUsers();
+ }
+
+ get maxUsers() {
+ return this.userList.maxUsers;
+ }
+
+ getTable(key) {
+ return this.tables[key];
+ }
+}
+
+Object.assign(
+ SingleRoom.prototype,
+ EventEmitterMethods,
+ Methods
+);
+export default SingleRoom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/schema.md
new file mode 100644
index 000000000..0b8cfc432
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/schema.md
@@ -0,0 +1,21 @@
+```
+# Body of room data. Each room has unique roomID
+rooms/
+
+ # users in this room.
+ users/
+
+ userID - Unique ID of user
+ userName - The name of the user
+
+ broadcast/
+ - `message` - Message
+ - `senderID` - Unique ID of sender
+ - `senderName` - Name of sender
+ - `stamp` - Toggle between true and false
+
+ table/
+ -
+ -
+ - : value
+```
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateBroadcast.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateBroadcast.js
new file mode 100644
index 000000000..1894bbe45
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateBroadcast.js
@@ -0,0 +1,36 @@
+import Broadcast from '../../broadcast/Broadcast.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+
+var CreateBroadcast = function (config) {
+ var broadcastConfig = GetValue(config, 'broadcast', true);
+ if (!broadcastConfig) {
+ return null;
+ }
+
+ var broadcast = new Broadcast({
+ eventEmitter: this.getEventEmitter(),
+ eventNames: {
+ receive: 'broadcast.receive'
+ },
+
+ root: this.rootPath,
+ receiverID: 'broadcast',
+ senderID: this.userInfo,
+ history: GetValue(broadcastConfig, 'history', false)
+ });
+
+ this
+ .on('room.join', function () {
+ broadcast.startReceiving()
+ })
+ .on('room.leave', function () {
+ broadcast.stopReceiving()
+ })
+ .on('userlist.changename', function (userID, userName) {
+ broadcast.changeUserName(userID, userName);
+ }, this)
+
+ return broadcast;
+}
+
+export default CreateBroadcast;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateTables.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateTables.js
new file mode 100644
index 000000000..150a412ca
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateTables.js
@@ -0,0 +1,55 @@
+import ItemTable from '../../itemtable/ItemTable.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+
+var CreateTables = function (config) {
+ var tablesConfig = GetValue(config, 'tables', undefined);
+ if (tablesConfig === undefined) {
+ return {};
+ }
+
+ var tableConfig;
+ var tables = {};
+ for (var i = 0, cnt = tablesConfig.length; i < cnt; i++) {
+ tableConfig = tablesConfig[i];
+ tables[tableConfig.key] = CreateTable.call(this, tableConfig);
+ }
+
+ return tables;
+}
+var CreateTable = function (config) {
+ var key = config.key;
+ var table = new ItemTable({
+ eventEmitter: this.getEventEmitter(),
+ root: this.getItemTablePath(key),
+
+ type: GetValue(config, 'type', 1),
+ eventNames: {
+ init: `tables.${key}.init`,
+ update: `tables.${key}.update`,
+ addkey0: `tables.${key}.addkey0`,
+ removekey0: `tables.${key}.removekey0`,
+ changekey0: `tables.${key}.changekey0`,
+ addkey1: `tables.${key}.addkey1`,
+ removekey1: `tables.${key}.removekey1`,
+ changekey1: `tables.${key}.changekey1`,
+ addkey2: `tables.${key}.addkey2`,
+ removekey2: `tables.${key}.removekey2`,
+ changekey2: `tables.${key}.changekey2`
+ }
+ });
+
+ this
+ .on('room.join', function () {
+ table
+ .startUpdate()
+ })
+ .on('room.leave', function () {
+ table
+ .clear()
+ .stopUpdate()
+ })
+
+ return table;
+}
+
+export default CreateTables;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateUserList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateUserList.js
new file mode 100644
index 000000000..d4d094a00
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/singleroom/utils/CreateUserList.js
@@ -0,0 +1,52 @@
+import OnlineUserList from '../../onlineuserlist/OnlineUserList.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+
+var CreateUserList = function (config) {
+ var userList = new OnlineUserList({
+ eventEmitter: this.getEventEmitter(),
+ eventNames: {
+ join: 'userlist.join', // Any user join
+ leave: 'userlist.leave', // Any user leave
+ update: 'userlist.update', // Update user list
+ change: 'userlist.change', // Any user(name) change
+ init: 'userlist.init',
+ changename: 'userlist.changename'
+ },
+
+ root: this.getUserListPath(),
+ userID: this.userInfo,
+ maxUsers: GetValue(config, 'maxUsers', 0)
+ });
+
+ userList
+ .on('userlist.leave', function (user) {
+ if (user.userID === this.userID) {
+ OnLeftRoom.call(this); // Current user is left or kicked
+ }
+ }, this)
+
+ this
+ .on('room.join', function () {
+ userList
+ .startUpdate()
+ })
+ .on('room.leave', function () {
+ userList
+ .stopUpdate()
+ .clear()
+ })
+
+ return userList;
+}
+
+var OnLeftRoom = function () {
+ this.emit('room.leave');
+
+ // Clear room info later
+ var self = this;
+ setTimeout(function () {
+ self.leftRoomFlag = false;
+ }, 0);
+}
+
+export default CreateUserList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemList.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemList.js
new file mode 100644
index 000000000..eace8ed8a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemList.js
@@ -0,0 +1,121 @@
+import EventEmitterMethods from '../../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../../utils/object/GetValue.js';
+import ItemMethods from './ItemMethods.js';
+import UpdateOnce from './updaters/UpdateOnce.js';
+import UpdateChild from './updaters/UpdateChild.js';
+import UpdateAll from './updaters/UpdateAll.js';
+
+class ItemList {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+ this.eventNameMap = GetValue(config, 'eventNames', DefaultEventNames);
+
+ this.isUpdating = false;
+ this.items = [];
+ this.itemID2Index = {};
+ this.setItemIDKey(GetValue(config, 'itemIDKey', '__itemID__'));
+ this.setMode(GetValue(config, 'mode', 1));
+ this.setGetitemCallback(GetValue(config, 'getItemCallback', DefaultGetItemCallback), GetValue(config, 'getItemCallbackScope', this));
+ this.setQuery(GetValue(config, 'query', undefined));
+ }
+
+ shutdown() {
+ this
+ .stopUpdate()
+ .clear();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ setItemIDKey(key) {
+ this.keyItemID = key;
+ return this;
+ }
+
+ setMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = MODE[mode];
+ }
+
+ this.mode = mode;
+ this.updater = Updaters[mode];
+ return this;
+ }
+
+ setGetitemCallback(callback, scope) {
+ this.getItemCallback = callback;
+ this.getItemCallbackScope = scope;
+ return this;
+ }
+
+ setQuery(query) {
+ this.query = query;
+ return this;
+ }
+
+ startUpdate(query) {
+ if (query) {
+ this.setQuery(query);
+ } else if (this.query) {
+ query = this.query;
+ } else { // !query && !this.query
+ return this;
+ }
+
+ this
+ .stopUpdate()
+ .clear();
+
+ this.isUpdating = true;
+ this.updater.start.call(this, query);
+ return this;
+ }
+
+ stopUpdate() {
+ if ((!this.query) || (!this.isUpdating)) {
+ return this;
+ }
+
+ this.isUpdating = false;
+ this.updater.stop.call(this);
+ return this;
+ }
+}
+
+var DefaultGetItemCallback = function (snapshot) {
+ var item = snapshot.val();
+ item[this.keyItemID] = snapshot.key;
+ return item;
+}
+
+Object.assign(
+ ItemList.prototype,
+ EventEmitterMethods,
+ ItemMethods
+);
+
+const DefaultEventNames = {
+ update: 'update',
+ add: 'add',
+ remove: 'remove',
+ change: 'change'
+}
+
+const Updaters = {
+ 0: UpdateOnce,
+ 1: UpdateChild,
+ 2: UpdateAll
+};
+
+const MODE = {
+ once: 0,
+ child: 1,
+ all: 2
+}
+
+export default ItemList;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemMethods.js
new file mode 100644
index 000000000..3fc5f5727
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/ItemMethods.js
@@ -0,0 +1,52 @@
+import Clear from '../../../../utils/object/Clear.js';
+
+var Methods = {
+ clear() {
+ this.items.length = 0;
+ Clear(this.itemID2Index);
+ return this;
+ },
+
+ getItems() {
+ return this.items;
+ },
+
+ hasItem(itemID) {
+ return this.itemID2Index.hasOwnProperty(itemID);
+ },
+
+ getItemIndexFromItemID(itemID) {
+ if (itemID == null) {
+ return null;
+ }
+ return this.itemID2Index[itemID];
+ },
+
+ getItemFromItemID(itemID) {
+ if (itemID == null) {
+ return null;
+ }
+ var index = this.getItemIndexFromItemID(itemID);
+ if (index == null) {
+ return null;
+ }
+
+ return this.items[index];
+ },
+
+ forEach(callback, scope) {
+ this.items.forEach(callback, scope);
+ return this;
+ },
+
+ updateItemID2Index() {
+ Clear(this.itemID2Index);
+ var itemID;
+ for (var i = 0, cnt = this.items.length; i < cnt; i++) {
+ itemID = this.items[i][this.keyItemID];
+ this.itemID2Index[itemID] = i;
+ }
+ return this;
+ }
+}
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/Callbacks.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/Callbacks.js
new file mode 100644
index 000000000..f3da3432e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/Callbacks.js
@@ -0,0 +1,78 @@
+import SpliceOne from '../../../../../utils/array/SpliceOne.js';
+
+var AddChildCallback = function (snapshot, prevName) {
+ var item = AddItem.call(this, snapshot, prevName);
+ this.updateItemID2Index();
+
+ this.emit(this.eventNameMap.add, item);
+ this.emit(this.eventNameMap.update, this.items);
+}
+
+var ChangeChildCallback = function (snapshot, prevName) {
+ var prevItem = RemoveItem.call(this, snapshot);
+ this.updateItemID2Index();
+ var newItem = AddItem.call(this, snapshot, prevName);
+ this.updateItemID2Index();
+
+ this.emit(this.eventNameMap.change, newItem, prevItem);
+ this.emit(this.eventNameMap.update, this.items);
+}
+
+var RemoveChildCallback = function (snapshot) {
+ var item = RemoveItem.call(this, snapshot);
+ this.updateItemID2Index();
+
+ this.emit(this.eventNameMap.remove, item);
+ this.emit(this.eventNameMap.update, this.items);
+}
+
+var GetAllChildrenCallback = function (snapshot) {
+ this.clear();
+ snapshot.forEach((function (childSnapshot) {
+ AddItem.call(this, childSnapshot, null, true);
+ }).bind(this));
+ this.updateItemID2Index();
+
+ this.emit(this.eventNameMap.update, this.items);
+}
+
+var AddItem = function(snapshot, prevName, pushMode) {
+ var item;
+ var callback = this.getItemCallback;
+ var scope = this.getItemCallbackScope;
+ if (scope) {
+ item = callback.call(scope, snapshot);
+ } else {
+ item = callback(snapshot);
+ }
+
+ if (pushMode) {
+ this.items.push(item);
+ return item;
+ }
+
+ if (prevName == null) {
+ this.items.unshift(item);
+ } else {
+ var i = this.itemID2Index[prevName];
+ if (i === this.items.length - 1) {
+ this.items.push(item);
+ } else {
+ this.items.splice(i + 1, 0, item);
+ }
+ }
+ return item;
+}
+
+var RemoveItem = function (snapshot) {
+ var index = this.itemID2Index[snapshot.key];
+ var item = SpliceOne(this.items, index);
+ return item
+}
+
+export {
+ AddChildCallback,
+ ChangeChildCallback,
+ RemoveChildCallback,
+ GetAllChildrenCallback
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateAll.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateAll.js
new file mode 100644
index 000000000..99f181f81
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateAll.js
@@ -0,0 +1,14 @@
+import { GetAllChildrenCallback } from './Callbacks.js';
+
+var Updater = {
+ start(query) {
+ query.on('value', GetAllChildrenCallback, this);
+ return this;
+ },
+ stop() {
+ this.query.off('value', GetAllChildrenCallback, this);
+ return this;
+ }
+}
+
+export default Updater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateChild.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateChild.js
new file mode 100644
index 000000000..a32b765d9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateChild.js
@@ -0,0 +1,19 @@
+import { AddChildCallback, RemoveChildCallback, ChangeChildCallback } from './Callbacks.js';
+
+var Updater = {
+ start(query) {
+ query.on('child_added', AddChildCallback, this);
+ query.on('child_removed', RemoveChildCallback, this);
+ query.on('child_moved', ChangeChildCallback, this);
+ query.on('child_changed', ChangeChildCallback, this);
+ return this;
+ },
+ stop() {
+ this.query.off('child_added', AddChildCallback, this);
+ this.query.off('child_removed', RemoveChildCallback, this);
+ this.query.off('child_moved', ChangeChildCallback, this);
+ this.query.off('child_changed', ChangeChildCallback, this);
+ return this;
+ },
+}
+export default Updater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateOnce.js b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateOnce.js
new file mode 100644
index 000000000..3d9c39754
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/database/utils/itemlist/updaters/UpdateOnce.js
@@ -0,0 +1,15 @@
+import { GetAllChildrenCallback } from './Callbacks.js';
+
+var Updater = {
+ start(query) {
+ this.isUpdating = false;
+ query.once('value', GetAllChildrenCallback, this);
+ return this;
+ },
+ stop() {
+ // Do nothing
+ return this;
+ }
+}
+
+export default Updater;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Clear.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Clear.js
new file mode 100644
index 000000000..bcda7308e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Clear.js
@@ -0,0 +1,32 @@
+var Clear = function () {
+ var userID = this.userID;
+ var self = this;
+ return this.getFileQuery(userID, undefined, 'header')
+ .get()
+ .then(function (querySnapshot) {
+ var batch = self.database.batch();
+ var header;
+ querySnapshot.forEach(function (doc) {
+ header = DocToHeader(doc);
+ batch.delete(self.rootRef.doc(header.headerDocID));
+ if (header.contentDocID) {
+ batch.delete(self.rootRef.doc(header.contentDocID));
+ }
+ });
+ return batch.commit();
+ })
+ .then(function () {
+ self.clearCache();
+ return Promise.resolve({
+ userID: userID
+ });
+ })
+ .catch(function (error) {
+ return Promise.reject({
+ error: error,
+ userID: userID
+ });
+ });
+}
+
+export default Clear;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Delete.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Delete.js
new file mode 100644
index 000000000..cf6542be4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Delete.js
@@ -0,0 +1,39 @@
+var Delete = function (fileID) {
+ var userID = this.userID;
+ var self = this;
+ return LoadHeader.call(this, fileID) // Try load header
+ .then(function (prevHeader) {
+ if (!prevHeader) { // File dose not exist
+ return Promise.resolve({
+ userID: userID,
+ fileID: fileID
+ });
+ }
+
+ var batch = self.database.batch();
+ batch.delete(self.rootRef.doc(prevHeader.headerDocID));
+ if (prevHeader.contentDocID) {
+ batch.delete(self.rootRef.doc(prevHeader.contentDocID));
+ }
+ return batch.commit();
+ })
+ .then(function () {
+ if (self.cacheHeaders.hasOwnProperty(fileID)) {
+ delete self.cacheHeaders[fileID];
+ }
+
+ return Promise.resolve({
+ userID: userID,
+ fileID: fileID
+ });
+ })
+ .catch(function (error) {
+ return Promise.reject({
+ error: error,
+ userID: userID,
+ fileID: fileID
+ });
+ });
+}
+
+export default Delete;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/DocToHeader.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/DocToHeader.js
new file mode 100644
index 000000000..6a2fe5737
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/DocToHeader.js
@@ -0,0 +1,7 @@
+var DocToHeader = function (doc) {
+ var header = doc.data();
+ header.headerDocID = doc.id;
+ return header;
+}
+
+export default DocToHeader;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.d.ts
new file mode 100644
index 000000000..2d7961a3f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.d.ts
@@ -0,0 +1,5 @@
+import Files from './Files';
+
+export default function (
+ config: Files.IConfig
+): Files;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.js
new file mode 100644
index 000000000..d1b108a20
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Factory.js
@@ -0,0 +1,11 @@
+import Files from './Files.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('files', function (config) {
+ return new Files(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.Files', Files);
+
+export default Files;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.d.ts
new file mode 100644
index 000000000..381759277
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.d.ts
@@ -0,0 +1,62 @@
+export default Files;
+
+declare namespace Files {
+ interface IConfig {
+ root?: string
+ }
+
+ interface IBaseData {
+ userID?: string;
+ fileID?: string;
+ type?: 'header' | 'content';
+
+ // Other properties
+ [name: string]: unknown;
+ }
+
+ interface IHeader extends IBaseData { }
+
+ interface IContent extends IBaseData { }
+}
+
+declare class Files {
+ constructor(
+ config: Files.IConfig
+ );
+
+ setOwner(userID: string): this;
+
+ setOwner(
+ config: { userID: string }
+ ): this;
+
+ userID: string;
+ readonly userInfo: { userID?: string, userName?: string };
+
+ save(
+ fileID: string,
+ header?: Files.IHeader,
+ content?: Files.IContent,
+ updateMode?: boolean
+ ): Promise<
+ { userID: string, fileID: string }
+ >;
+
+ loadHeaders(
+ ): Promise<
+ {
+ userID: string,
+ headers: { [fileID: string]: Files.IHeader }
+ }
+ >;
+
+ load(
+ fileID: string
+ ): Promise<
+ {
+ userID: string, fileID: string,
+ header: Files.IHeader,
+ content: Files.IContent
+ }
+ >;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.js
new file mode 100644
index 000000000..a2eea1d0f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Files.js
@@ -0,0 +1,85 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Save from './Save.js';
+import Load from './Load.js';
+import LoadHeaders from './LoadHeaders.js';
+import Delete from './Delete.js';
+import Clear from './Clear.js';
+import ClearDict from '../../../utils/object/Clear.js';
+
+class Files {
+ constructor(config) {
+ this.database = firebase.firestore();
+ this.setRootPath(GetValue(config, 'root', ''));
+
+ this.cacheHeaders = {};
+
+ // Owner
+ this.userInfo = { userID: '' };
+ this.setOwner(GetValue(config, 'userID', ''));
+
+ }
+
+ shutdown() {
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ setRootPath(rootPath) {
+ this.rootPath = rootPath;
+ this.rootRef = this.database.collection(rootPath);
+ return this;
+ }
+
+ setOwner(userID) {
+ var prevUserID = this.userID;
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ }
+ if (prevUserID !== this.userID) {
+ this.clearCache();
+ }
+ return this;
+ }
+
+ clearCache() {
+ ClearDict(this.cacheHeaders);
+ return this;
+ }
+
+ getFileQuery(userID, fileID, type) {
+ var query = this.rootRef;
+ query = (userID) ? query.where('userID', '==', userID) : query;
+ query = (fileID) ? query.where('fileID', '==', fileID) : query;
+ query = (type) ? query.where('type', '==', type) : query;
+ return query;
+ }
+
+}
+
+var methods = {
+ save: Save,
+ load: Load,
+ loadHeaders: LoadHeaders,
+ delete: Delete,
+ clear: Clear,
+}
+
+Object.assign(
+ Files.prototype,
+ methods
+);
+
+export default Files;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Load.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Load.js
new file mode 100644
index 000000000..9a686a516
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Load.js
@@ -0,0 +1,36 @@
+import DocToHeader from './DocToHeader.js';
+
+var Load = function (fileID) {
+ var userID = this.userID;
+
+ var self = this;
+ return this.getFileQuery(userID, fileID).get()
+ .then(function (querySnapshot) {
+ var header, content;
+ querySnapshot.forEach(function (doc) {
+ switch (docData.type) {
+ case 'header':
+ header = DocToHeader(doc);
+ break;
+ case 'content':
+ content = doc.data();
+ break;
+ }
+ });
+ return Promise.resolve({
+ userID: userID,
+ fileID: fileID,
+ header: header,
+ content: content
+ });
+ })
+ .catch(function () {
+ return Promise.reject({
+ error: error,
+ userID: userID,
+ fileID: fileID
+ });
+ });
+}
+
+export default Load;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeader.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeader.js
new file mode 100644
index 000000000..2a512148c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeader.js
@@ -0,0 +1,25 @@
+import DocToHeader from './DocToHeader.js';
+
+// Internal used
+var LoadHeader = function (fileID) {
+ var userID = this.userID;
+ let header = this.cacheHeaders[fileID];
+ if (header && (header.userID === userID)) {
+ return Promise.resolve(header);
+ }
+
+ // Can't find in cache headers, load from firestore
+ var self = this;
+ return this.getFileQuery(userID, fileID, 'header').limit(1).get()
+ .then(function (querySnapshot) {
+ let header = undefined;
+ if (querySnapshot.size > 0) {
+ var doc = querySnapshot.docs[0];
+ header = DocToHeader(doc);
+ self.cacheHeaders[fileID] = header; // Cache it
+ }
+ return Promise.resolve(header);
+ });
+}
+
+export default LoadHeader;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeaders.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeaders.js
new file mode 100644
index 000000000..26602d375
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/LoadHeaders.js
@@ -0,0 +1,28 @@
+import DocToHeader from './DocToHeader.js';
+import ClearDict from '../../../utils/object/Clear.js';
+
+var LoadHeaders = function () {
+ var userID = this.userID;
+ var self = this;
+ return this.getFileQuery(userID, undefined, 'header').get()
+ .then(function (querySnapshot) {
+ var header;
+ ClearDict(self.cacheHeaders);
+ querySnapshot.forEach(function (doc) {
+ header = DocToHeader(doc);
+ self.cacheHeaders[header.fileID] = header;
+ });
+ return Promise.resolve({
+ userID: userID,
+ headers: self.cacheHeaders
+ });
+ })
+ .catch(function () {
+ return Promise.reject({
+ error: error,
+ userID: userID
+ });
+ });
+}
+
+export default LoadHeaders;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Save.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Save.js
new file mode 100644
index 000000000..4d2b26c20
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/Save.js
@@ -0,0 +1,79 @@
+import LoadHeader from './LoadHeader.js';
+
+var Save = function (fileID, header, content, updateMode) {
+ if (typeof (content) === 'boolean') {
+ updateMode = content;
+ content = undefined;
+ }
+ if (updateMode === undefined) {
+ updateMode = false;
+ }
+
+ var userID = this.userID;
+ if (header === undefined) {
+ header = {};
+ }
+ header.userID = userID;
+ header.fileID = fileID;
+ header.type = 'header';
+
+ if (content) {
+ content.userID = userID;
+ content.fileID = fileID;
+ content.type = 'content';
+ }
+ var writeCommand = (updateMode) ? 'update' : 'set';
+
+ var self = this;
+ return LoadHeader.call(this, fileID) // Try load header
+ .then(function (prevHeader) {
+ var headerDocRef, contentDocRef;
+ if (prevHeader) { // Overwrite file
+ headerDocRef = self.rootRef.doc(prevHeader.headerDocID);
+ if (content) {
+ if (prevHeader.contentDocID) {
+ contentDocRef = self.rootRef.doc(prevHeader.contentDocID);
+ } else {
+ contentDocRef = self.rootRef.doc();
+ }
+ }
+ } else { // Add new file
+ headerDocRef = self.rootRef.doc();
+ if (content) {
+ contentDocRef = self.rootRef.doc();
+ }
+ }
+
+ // Don't save headerDocID to server
+ if (header.hasOwnProperty('headerDocID')) {
+ delete header.headerDocID;
+ }
+ // Save contentDocID
+ if (contentDocRef) {
+ header.contentDocID = contentDocRef.id;
+ }
+
+
+ var batch = self.database.batch();
+ batch[writeCommand](headerDocRef, header);
+ if (content) {
+ batch[writeCommand](contentDocRef, content);
+ }
+ return batch.commit();
+ })
+ .then(function () {
+ return Promise.resolve({
+ userID: userID,
+ fileID: fileID
+ });
+ })
+ .catch(function (error) {
+ return Promise.reject({
+ error: error,
+ userID: userID,
+ fileID: fileID
+ });
+ });
+}
+
+export default Save;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/schema.md
new file mode 100644
index 000000000..93c9f354d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/files/schema.md
@@ -0,0 +1,18 @@
+## Header (Required)
+
+-
+ - `userID` - Unique ID of owner
+ - `fileID` - Unique file ID of each owner
+ - `type` - 'header'
+ - `contentDocID` - Document ID of content
+ - ...
+
+## Content
+
+Optional
+
+-
+ - `userID` - Unique ID of owner
+ - `fileID` - Unique file ID of each owner
+ - `type` - 'content'
+ - ...
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Add.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Add.js
new file mode 100644
index 000000000..670c7df26
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Add.js
@@ -0,0 +1,19 @@
+import AddAliasTransaction from './AddAliasTransaction.js';
+
+var Add = function (id, alias) {
+ var self = this;
+ return this.getAlias(id)
+ .then(function (result) {
+ if (result.alias) {
+ if (result.alias === alias) {
+ return Promise.resolve({ id: id, alias: alias });
+ } else {
+ return Promise.reject({ id: id, alias: alias });
+ }
+ } else {
+ return AddAliasTransaction.call(self, id, alias);
+ }
+ });
+}
+
+export default Add;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddAliasTransaction.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddAliasTransaction.js
new file mode 100644
index 000000000..7b57d5fd0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddAliasTransaction.js
@@ -0,0 +1,15 @@
+var Add = function (id, alias) {
+ var self = this;
+ return this.database.runTransaction(function (transaction) {
+ var aliasRef = self.getAliasRef(alias);
+ return transaction.get(aliasRef).then(function (doc) {
+ if (!doc.exists) {
+ transaction.set(aliasRef, { id: id });
+ return Promise.resolve({ id: id, alias: alias });
+ } else {
+ return Promise.reject({ id: id, alias: alias });
+ }
+ });
+ });
+}
+export default Add;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddRandom.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddRandom.js
new file mode 100644
index 000000000..bc1b73aee
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/AddRandom.js
@@ -0,0 +1,26 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import GetRandomWord from '../../../utils/string/GetRandomWord.js';
+import RetryAddRandomAliasTransaction from './RetryAddRandomAliasTransaction.js';
+
+var AddRandom = function (id, config) {
+ var digits = GetValue(config, 'digits', 10);
+ var candidates = GetValue(config, 'candidates', '0123456789');
+ var retry = GetValue(config, 'retry', 1000);
+
+ var self = this;
+ return this.getAlias(id)
+ .then(function (result) {
+ if (result.alias) {
+ var alias = GetRandomWord(digits, digits, candidates);
+ if (result.alias === alias) {
+ return Promise.resolve({ id: id, alias: alias });
+ } else {
+ return Promise.reject({ id: id, alias: alias });
+ }
+ } else {
+ return RetryAddRandomAliasTransaction.call(self, id, digits, candidates, retry);
+ }
+ });
+}
+
+export default AddRandom;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.d.ts
new file mode 100644
index 000000000..79b5553a3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.d.ts
@@ -0,0 +1,5 @@
+import IdAlias from './IdAlias';
+
+export default function (
+ config: IdAlias.IConfig
+): IdAlias;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.js
new file mode 100644
index 000000000..cac7c575c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Factory.js
@@ -0,0 +1,11 @@
+import IdAlias from './IdAlias.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils//object/SetValue.js';
+
+ObjectFactory.register('idAlias', function (config) {
+ return new IdAlias(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.IdAlias', IdAlias);
+
+export default IdAlias;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetAlias.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetAlias.js
new file mode 100644
index 000000000..6283f21fb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetAlias.js
@@ -0,0 +1,16 @@
+var GetAlias = function (id) {
+ return this.rootRef.where('id', '==', id).limit(1).get()
+ .then(function (querySnapshot) {
+ var alias;
+ if (querySnapshot.size > 0) {
+ var doc = querySnapshot.docs[0];
+ alias = doc.id;
+ }
+ return Promise.resolve({
+ id: id,
+ alias: alias
+ });
+ });
+}
+
+export default GetAlias;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetId.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetId.js
new file mode 100644
index 000000000..88df087d8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetId.js
@@ -0,0 +1,15 @@
+var GetId = function (alias) {
+ return this.getAliasRef(alias).get()
+ .then(function (doc) {
+ var id;
+ if (doc.exists) {
+ id = doc.data().id;
+ }
+ return Promise.resolve({
+ id: id,
+ alias: alias
+ });
+ });
+}
+
+export default GetId;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetRandomAlias.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetRandomAlias.js
new file mode 100644
index 000000000..037d134ff
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/GetRandomAlias.js
@@ -0,0 +1,20 @@
+import GetValue from '../../../utils//object/GetValue.js';
+import RetryAddRandomAliasTransaction from './RetryAddRandomAliasTransaction.js';
+
+var GetRandomAlias = function (id, config) {
+ var digits = GetValue(config, 'digits', 10);
+ var candidates = GetValue(config, 'candidates', '0123456789');
+ var retry = GetValue(config, 'retry', 1000);
+
+ var self = this;
+ return this.getAlias(id)
+ .then(function (result) {
+ if (result.alias) {
+ return Promise.resolve(result);
+ } else {
+ return RetryAddRandomAliasTransaction.call(self, id, digits, candidates, retry);
+ }
+ })
+};
+
+export default GetRandomAlias;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.d.ts
new file mode 100644
index 000000000..cb65f3936
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.d.ts
@@ -0,0 +1,42 @@
+export default IdAlias;
+
+declare namespace IdAlias {
+ interface IConfig {
+ root?: string
+ }
+
+ interface IGetRandomAliasConfig {
+ digits?: number,
+ candidates?: string,
+ retry?: number
+ }
+
+ type ResultType = {
+ id: string | undefined,
+ alias: string | undefined
+ }
+}
+
+declare class IdAlias {
+ constructor(
+ config: IdAlias.IConfig
+ );
+
+ getRandomAlias(
+ id: string,
+ config?: IdAlias.IGetRandomAliasConfig
+ ): Promise;
+
+ add(
+ id: string, alias: string
+ ): Promise;
+
+ getId(
+ alias: string
+ ): Promise;
+
+ getAlias(
+ id: string
+ ): Promise;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.js
new file mode 100644
index 000000000..8ad278234
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/IdAlias.js
@@ -0,0 +1,47 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import Add from './Add.js';
+import AddRandom from './AddRandom.js';
+import GetId from './GetId.js';
+import GetAlias from './GetAlias.js';
+import GetRandomAlias from './GetRandomAlias.js';
+import Remove from './Remove.js';
+
+class IdAlias {
+ constructor(config) {
+ this.database = firebase.firestore();
+ this.setRootPath(GetValue(config, 'root', ''));
+ }
+
+ shutdown() {
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ setRootPath(rootPath) {
+ this.rootPath = rootPath;
+ this.rootRef = this.database.collection(rootPath);
+ return this;
+ }
+
+ getAliasRef(alias) {
+ return this.rootRef.doc(alias);
+ }
+}
+
+var methods = {
+ add: Add,
+ addRandom: AddRandom,
+ getId: GetId,
+ getAlias: GetAlias,
+ getRandomAlias: GetRandomAlias,
+ remove: Remove
+}
+
+Object.assign(
+ IdAlias.prototype,
+ methods
+);
+
+export default IdAlias;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Remove.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Remove.js
new file mode 100644
index 000000000..669f5343f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/Remove.js
@@ -0,0 +1,9 @@
+var Remove = function (id) {
+ var self = this;
+ return this.getAlias(id)
+ .then(function (alias) {
+ return self.getAliasRef(alias).delete();
+ })
+}
+
+export default Remove;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/RetryAddRandomAliasTransaction.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/RetryAddRandomAliasTransaction.js
new file mode 100644
index 000000000..8f32eb8c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/RetryAddRandomAliasTransaction.js
@@ -0,0 +1,19 @@
+import GetRandomWord from '../../../utils/string/GetRandomWord.js';
+import AddAliasTransaction from './AddAliasTransaction.js';
+
+var TryAdd = function (id, digits, candidates, retry) {
+ var alias = GetRandomWord(digits, digits, candidates);
+ if (retry <= 0) {
+ return Promise.reject({ id: id, alias: alias });
+ }
+ retry--;
+ var self = this;
+ return AddAliasTransaction.call(self, id, alias)
+ .catch(function () {
+ setTimeout(function () {
+ return TryAdd.call(self, id, digits, candidates, retry);
+ }, 0);
+ });
+}
+
+export default TryAdd;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/schema.md
new file mode 100644
index 000000000..addb1cc3d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/idalias/schema.md
@@ -0,0 +1,2 @@
+-
+ - `id` - ID
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Const.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Const.js
new file mode 100644
index 000000000..e0d91928b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Const.js
@@ -0,0 +1,29 @@
+var TimeTagKeys = {
+ d: 'tagD',
+ w: 'tagW',
+ m: 'tagM',
+ y: 'tagY',
+ a: 'tagA'
+}
+
+var ScoreKeys = {
+ d: 'scoreD',
+ w: 'scoreW',
+ m: 'scoreM',
+ y: 'scoreY',
+ a: 'scoreA'
+}
+
+var FullTimeName = {
+ d: 'day',
+ w: 'week',
+ m: 'month',
+ y: 'year',
+ a: 'all'
+}
+
+export {
+ TimeTagKeys,
+ ScoreKeys,
+ FullTimeName
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/DeleteMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/DeleteMethods.js
new file mode 100644
index 000000000..61947522f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/DeleteMethods.js
@@ -0,0 +1,25 @@
+import Delete from '../utils/query/Delete.js';
+
+var Methods = {
+ deleteUser(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+
+ var query = this.getRecordQuery(undefined, undefined, userID, undefined);
+ return Delete(query);
+ },
+
+ deleteBoard(boardID, tag) {
+ if (boardID === undefined) {
+ boardID = this.boardID;
+ }
+ if (tag === undefined) {
+ tag = this.tag;
+ }
+
+ var query = this.getRecordQuery(boardID, tag, undefined, undefined);
+ return Delete(query);
+ }
+}
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.d.ts
new file mode 100644
index 000000000..4c14fa0c2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.d.ts
@@ -0,0 +1,5 @@
+import LeaderBoard from './LeaderBoard';
+
+export default function (
+ config: LeaderBoard.IConfig
+): LeaderBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.js
new file mode 100644
index 000000000..b84a49f20
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Factory.js
@@ -0,0 +1,11 @@
+import LeaderBoard from './LeaderBoard.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('leaderBoard', function (config) {
+ return new LeaderBoard(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.LeaderBoard', LeaderBoard);
+
+export default LeaderBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetQueryMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetQueryMethods.js
new file mode 100644
index 000000000..3bd7de04e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetQueryMethods.js
@@ -0,0 +1,45 @@
+import { TimeTagKeys, ScoreKeys } from './Const.js';
+import GetTime from './GetTime.js';
+
+var Methods = {
+ getRecordQuery(boardID, customTag, userID, timeTagKey) {
+ var query = this.rootRef;
+ query = (boardID !== undefined) ? query.where('boardID', '==', boardID) : query;
+ query = (customTag !== undefined) ? query.where('tag', '==', customTag) : query;
+ query = (userID !== undefined) ? query.where('userID', '==', userID) : query;
+
+ if (timeTagKey !== undefined) {
+ query = query.where(timeTagKey[0], '==', timeTagKey[1]);
+ }
+ return query;
+ },
+
+ getMyRecordQuery(userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+ return this.getRecordQuery(this.boardID, this.tag, userID, undefined).limit(1);
+ },
+
+ getPageQuery() {
+ var timeTagKey, scoreKey;
+ if (this.timeFilters !== false) {
+ var t = this.timeFilterType[0];
+ timeTagKey = [TimeTagKeys[t], GetTime()[t]];
+ scoreKey = ScoreKeys[t];
+ } else { // No time filters
+ timeTagKey = undefined;
+ scoreKey = 'score';
+ }
+
+ var baseQuery = this.getRecordQuery(this.boardID, this.tag, undefined, timeTagKey);
+ var nextPageQuery = baseQuery.orderBy(scoreKey, 'desc');
+ var prevPageQuery = baseQuery.orderBy(scoreKey);
+ return {
+ next: nextPageQuery,
+ previous: prevPageQuery
+ }
+ }
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetRank.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetRank.js
new file mode 100644
index 000000000..d1da423ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetRank.js
@@ -0,0 +1,20 @@
+import FindFirst from '../utils/query/FindFirst.js';
+
+var GetRank = function (userID) {
+ if (userID === undefined) {
+ userID = this.userID;
+ }
+
+ var query = this.getPageQuery().next;
+ var testCallback = function (doc) {
+ var item = doc.data();
+ return (item.userID === userID);
+ }
+ return FindFirst(query, testCallback)
+ .then(function (result) {
+ return Promise.resolve({ userID: userID, rank: result.index });
+ })
+};
+
+
+export default GetRank;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetScore.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetScore.js
new file mode 100644
index 000000000..3b633f5e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetScore.js
@@ -0,0 +1,13 @@
+var GetScore = function (userID) {
+ return this.getMyRecordQuery(userID).get()
+ .then(function (querySnapshot) {
+ var item;
+ if (querySnapshot.size > 0) {
+ var doc = querySnapshot.docs[0];
+ item = doc.data();
+ }
+ return Promise.resolve(item);
+ });
+}
+
+export default GetScore;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetTime.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetTime.js
new file mode 100644
index 000000000..6554f800c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/GetTime.js
@@ -0,0 +1,17 @@
+var GetTime = function (timeStamp) {
+ var date = (timeStamp) ? (new Date(timeStamp)) : (new Date());
+ var y = date.getFullYear();
+ var m = date.getMonth() + 1;
+ var d = date.getDate();
+ var Jan1st = new Date(date.getFullYear(), 0, 1);
+ var w = Math.ceil((((date - Jan1st) / 86400000) + Jan1st.getDay() + 1) / 7);
+ return {
+ d: `${y}-${m}-${d}`, // day filter
+ w: `${y}-${w}`, // week filter
+ m: `${y}-${m}`, // month filter
+ y: `${y}`, // year filter
+ a: '' // all-time filter
+ };
+}
+
+export default GetTime;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.d.ts b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.d.ts
new file mode 100644
index 000000000..e7bd1f9fd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.d.ts
@@ -0,0 +1,98 @@
+export default LeaderBoard;
+
+declare namespace LeaderBoard {
+ type TimeFiltersType = {
+ day?: boolean, week?: boolean, month?: boolean, year?: boolean, all?: boolean,
+ }
+
+ type TimeFilterType = 'day' | 'd' | 'week' | 'w' | 'month' | 'm' | 'year' | 'y' | 'all' | 'a';
+
+ interface IConfig {
+ root?: string,
+ timeFilters?: boolean | TimeFiltersType,
+ timeFilterType?: TimeFilterType,
+ pageItemCount?: number
+ boardID?: string,
+ tag?: string,
+ }
+
+ interface IRecord {
+ userID: string; userName?: string;
+ boardID?: string; tag?: string;
+
+ score?: number;
+ tagD?: string; tagW?: string; tagM?: string; tagY?: string; tagA?: string;
+ scoreD?: number; scoreW?: number; scoreM?: number; scoreY?: number; scoreA?: number
+
+ // Other properties
+ [name: string]: any;
+ }
+
+ type RankResultType = {
+ userID: string,
+ rank: number
+ }
+}
+
+declare class LeaderBoard {
+ constructor(
+ config?: LeaderBoard.IConfig
+ );
+
+ setUser(userID: string, userName?: string): this;
+
+ setUser(
+ config: { userID: string, userName?: string }
+ ): this;
+
+ userID: string;
+ readonly userInfo: { userID?: string, userName?: string };
+
+ setBoardID(boardID?: string): this;
+ readonly boardID: string;
+
+ setTag(tag?: string): this;
+ readonly tag: string;
+
+ setTimeFilterType(type: LeaderBoard.TimeFilterType): this;
+ readonly timeFilters: boolean | LeaderBoard.TimeFiltersType;
+
+ post(
+ score: number,
+ extraData?: { [name: string]: any },
+ timestamp?: number
+ ): Promise;
+
+ getScore(
+ userID?: string
+ ): Promise;
+
+ getRank(
+ userID?: string
+ ): Promise;
+
+ loadFirstPage(
+ ): Promise;
+
+ loadNextPage(
+ ): Promise;
+
+ loadPreviousPage(
+ ): Promise;
+
+ loadCurrentPage(
+ ): Promise;
+
+ readonly pageIndex: number;
+ readonly isFirstPage: boolean;
+ readonly isLastPage: boolean;
+
+ deleteUserScore(
+ userID?: string
+ ): Promise;
+
+ deleteBoard(
+ boardID?: string, tag?: string
+ ): Promise;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.js
new file mode 100644
index 000000000..3b79ebbfa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LeaderBoard.js
@@ -0,0 +1,135 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Post from './Post.js';;
+import LoadMethods from './LoadMethods.js';
+import GetScore from './GetScore.js';
+import GetRank from './GetRank.js';
+import DeleteMethods from './DeleteMethods.js';
+import GetQueryMethods from './GetQueryMethods.js';
+import PageLoader from '../pageloader/PageLoader.js';
+
+class LeaderBoard {
+ constructor(config) {
+ this.database = firebase.firestore();
+ this.setRootPath(GetValue(config, 'root', ''));
+
+ this.userInfo = { userID: undefined, userName: undefined };
+ this.setUser(GetValue(config, 'userID', ''), GetValue(config, 'userName', undefined));
+ this.setBoardID(GetValue(config, 'boardID', undefined));
+ this.setTag(GetValue(config, 'tag', undefined));
+ this.setTimeFilters(GetValue(config, 'timeFilters', false));
+ this.setTimeFilterType(GetValue(config, 'timeFilterType', 'year'));
+
+ this.page = new PageLoader({
+ dataMode: 'dynamic',
+ itemCount: GetValue(config, 'pageItemCount', 100)
+ });
+ this.resetQueryFlag = true;
+ }
+
+ shutdown() {
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ setRootPath(rootPath) {
+ this.resetQueryFlag |= (this.rootPath !== rootPath);
+ this.rootPath = rootPath;
+ this.rootRef = this.database.collection(rootPath);
+ return this;
+ }
+
+ setUser(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ setBoardID(boardID) {
+ this.resetQueryFlag |= (this.boardID !== boardID);
+ this.boardID = boardID;
+ return this;
+ }
+
+ setTag(tag) {
+ this.resetQueryFlag |= (this.tag !== tag);
+ this.tag = tag;
+ return this;
+ }
+
+ setTimeFilters(filters) {
+ if (filters === false) {
+ this.timeFilters = false;
+ } else { // filters is true, or a plain object
+ this.timeFilters = {
+ d: GetValue(filters, 'day', true),
+ w: GetValue(filters, 'week', true),
+ m: GetValue(filters, 'month', true),
+ y: GetValue(filters, 'year', true),
+ a: GetValue(filters, 'all', true)
+ }
+ }
+ return this;
+ }
+
+ setTimeFilterType(type) {
+ this.resetQueryFlag |= (this.timeFilterType !== type);
+ this.timeFilterType = type;
+ return this;
+ }
+
+ setPageItemCount(count) {
+ this.page.setItemCount(count);
+ return this;
+ }
+
+ get pageIndex() {
+ return this.page.pageIndex;
+ }
+
+ get isFirstPage() {
+ return (this.page.pageIndex === 0);
+ }
+
+ get isLastPage() {
+ return (this.page.isFullPage === false);
+ }
+}
+
+var methods = {
+ post: Post,
+ getScore: GetScore,
+ getRank: GetRank
+}
+
+Object.assign(
+ LeaderBoard.prototype,
+ methods,
+ GetQueryMethods,
+ LoadMethods,
+ DeleteMethods
+);
+
+export default LeaderBoard;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LoadMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LoadMethods.js
new file mode 100644
index 000000000..2c54cd5cc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/LoadMethods.js
@@ -0,0 +1,85 @@
+import { TimeTagKeys, ScoreKeys } from './Const.js';
+
+var Methods = {
+ loadFirstPage() {
+ this.resetPageQuery();
+
+ var self = this;
+ return this.page.loadFirstPage()
+ .then(function (docs) {
+ return Promise.resolve(DocsToDataArray.call(self, docs));
+ })
+ },
+
+ loadNextPage() {
+ this.resetPageQuery();
+
+ var self = this;
+ return this.page.loadNextPage()
+ .then(function (docs) {
+ return Promise.resolve(DocsToDataArray.call(self, docs));
+ })
+ },
+
+ loadPreviousPage() {
+ this.resetPageQuery();
+
+ var self = this;
+ return this.page.loadPreviousPage()
+ .then(function (docs) {
+ return Promise.resolve(DocsToDataArray.call(self, docs));
+ })
+ },
+
+ loadCurrentPage() {
+ this.resetPageQuery();
+
+ var self = this;
+ return this.page.loadCurrentPage()
+ .then(function (docs) {
+ return Promise.resolve(DocsToDataArray.call(self, docs));
+ })
+ },
+
+ load(count, skip) {
+ this.resetPageQuery();
+
+ var self = this;
+ return this.page.load(count, skip)
+ .then(function (docs) {
+ return Promise.resolve(DocsToDataArray.call(self, docs));
+ })
+ },
+
+ resetPageQuery() {
+ if (!this.resetQueryFlag) {
+ return this;
+ }
+
+ this.resetQueryFlag = false;
+ this.page.setQuery(this.getPageQuery());
+ return this;
+ }
+}
+
+var DocsToDataArray = function (docs) {
+ var items = [], item;
+
+ var scoreKey = ScoreKeys[this.timeFilterType[0]];
+ for (var i = 0, cnt = docs.length; i < cnt; i++) {
+ item = docs[i].data();
+
+ if (this.timeFilters !== false) {
+ item.score = item[scoreKey];
+ // Remove timeFilterKeys, and scoreKeys
+ for (var t in this.timeFilters) {
+ delete item[TimeTagKeys[t]];
+ delete item[ScoreKeys[t]];
+ }
+ }
+ items.push(item);
+ }
+ return items;
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Post.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Post.js
new file mode 100644
index 000000000..039871bf4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/Post.js
@@ -0,0 +1,68 @@
+import GetTime from './GetTime.js';
+import { TimeTagKeys, ScoreKeys } from './Const.js';
+
+var Post = function (score, extraData, timeStamp) {
+ var newRecord = {
+ userID: this.userID
+ };
+ if (this.boardID !== undefined) {
+ newRecord.boardID = this.boardID;
+ }
+ if (this.userName) {
+ newRecord.userName = this.userName;
+ }
+ var curTimeData = GetTime(timeStamp);
+ if (this.timeFilters !== false) {
+ for (var t in this.timeFilters) {
+ if (!this.timeFilters[t]) {
+ continue;
+ }
+ newRecord[TimeTagKeys[t]] = curTimeData[t];
+ newRecord[ScoreKeys[t]] = score;
+ }
+ } else { // No time filters
+ newRecord.score = score;
+ }
+ if (this.tag) {
+ newRecord.tag = this.tag;
+ }
+ if (extraData) {
+ Object.assign(newRecord, extraData);
+ }
+
+ var self = this;
+ return this.getMyRecordQuery().get()
+ .then(function (querySnapshot) {
+ var prevRecord, docID;
+ if (querySnapshot.size > 0) {
+ var doc = querySnapshot.docs[0];
+ prevRecord = doc.data();
+ docID = doc.id;
+ }
+
+ if (prevRecord) {
+ if (self.timeFilters !== false) {
+ for (var t in self.timeFilters) {
+ if (!self.timeFilters[t]) {
+ continue;
+ }
+
+ var timeTagKey = TimeTagKeys[t];
+ if (prevRecord[timeTagKey] === newRecord[timeTagKey]) {
+ var scoreKey = ScoreKeys[t];
+ newRecord[scoreKey] = Math.max(prevRecord[scoreKey], newRecord[scoreKey]);
+ }
+ }
+ } else { // No time filters
+ newRecord.score = Math.max(prevRecord.score, newRecord.score);
+ }
+ }
+ if (docID === undefined) {
+ docID = self.rootRef.doc().id;
+ }
+ return self.rootRef.doc(docID)
+ .set(newRecord);
+ });
+}
+
+export default Post;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/schema.md
new file mode 100644
index 000000000..fb7a2b5ac
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/leaderboard/schema.md
@@ -0,0 +1,14 @@
+-
+ - `userID` - Unique ID of owner
+ - `boardID` - Unique ID of board, optional
+ - `tag` - Custom filter tag, optional
+ - `tagD` - Updated day of day score, filter key of day mode
+ - `tagW` - Updated week of week score, filter key of week mode
+ - `tagM` - Updated month of month score, filter key of month mode
+ - `tagY` - Updated year of year score, filter key of year mode
+ - `scoreD` - Day score, sorting key of day mode
+ - `scoreW` - Week score, sorting key of week mode
+ - `scoreM` - Month score, sorting key of month mode
+ - `scoreY` - Year score, sorting key of year mode
+ - `userName` - Name of the owner, optional
+ - ...
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Factory.js
new file mode 100644
index 000000000..950e4b7cf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Factory.js
@@ -0,0 +1,11 @@
+import Messages from './Messages.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('messages', function (config) {
+ return new Messages(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.Messages', Messages);
+
+export default Messages;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/GetQueryMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/GetQueryMethods.js
new file mode 100644
index 000000000..f12a400c9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/GetQueryMethods.js
@@ -0,0 +1,22 @@
+var Methods = {
+ getReceiverQuery(receiverID) {
+ if (receiverID === undefined) {
+ receiverID = this.receiverID;
+ }
+ var query = this.rootRef;
+ query = (receiverID !== undefined) ? query.where('receiverID', '==', receiverID) : query;
+ return query;
+ },
+
+ getPageQuery(receiverID) {
+ var baseQuery = this.getReceiverQuery(receiverID);
+ var nextPageQuery = baseQuery.orderBy('timestamp');
+ var prevPageQuery = baseQuery.orderBy('timestamp', 'desc');
+ return {
+ next: nextPageQuery,
+ previous: prevPageQuery
+ }
+ }
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Messages.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Messages.js
new file mode 100644
index 000000000..d24bde695
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Messages.js
@@ -0,0 +1,102 @@
+import EventEmitterMethods from '../../../utils/eventemitter/EventEmitterMethods.js';
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import Send from './Send.js';
+import ReceiveMethods from './ReceiveMethods.js';
+import GetQueryMethods from './GetQueryMethods.js';
+import PageLoader from '../pageloader/PageLoader.js';
+
+class Messages {
+ constructor(config) {
+ // Event emitter
+ var eventEmitter = GetValue(config, 'eventEmitter', undefined);
+ var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
+ this.setEventEmitter(eventEmitter, EventEmitterClass);
+
+ this.database = firebase.firestore();
+ this.setRootPath(GetValue(config, 'root', ''));
+
+ this.userInfo = { userID: '', userName: undefined };
+ this.setSender(GetValue(config, 'senderID', ''), GetValue(config, 'senderName', ''));
+ this.setReceiver(GetValue(config, 'receiverID', undefined));
+
+ this.skipFirst = true;
+ this.unsubscribe = undefined;
+ this.page = new PageLoader();
+ this.setPageItemCount(GetValue(config, 'pageItemCount', 100));
+ this.resetQueryFlag = true;
+ this.cacheMessages = [];
+ }
+
+ shutdown() {
+ this
+ .stopReceiving()
+ .destroyEventEmitter();
+ }
+
+ destroy() {
+ this.shutdown();
+ }
+
+ get userID() {
+ return this.userInfo.userID;
+ }
+
+ set userID(value) {
+ this.userInfo.userID = value;
+ }
+
+ get userName() {
+ return this.userInfo.userName;
+ }
+
+ set userName(value) {
+ this.userInfo.userName = value;
+ }
+
+ setRootPath(rootPath) {
+ this.resetQueryFlag |= (this.rootPath !== rootPath);
+ this.rootPath = rootPath;
+ this.rootRef = this.database.collection(rootPath);
+ return this;
+ }
+
+ setSender(userID, userName) {
+ if (IsPlainObject(userID)) {
+ this.userInfo = userID;
+ } else {
+ this.userID = userID;
+ this.userName = userName;
+ }
+ return this;
+ }
+
+ setReceiver(receiverID) {
+ this.resetQueryFlag |= (this.receiverID !== receiverID);
+ this.receiverID = receiverID;
+ return this;
+ }
+
+ setPageItemCount(count) {
+ this.page.setItemCount(count);
+ return this;
+ }
+
+ get hasPreviousMessage() {
+ return (this.page.isFullPage !== false);
+ }
+}
+
+var methods = {
+ send: Send
+}
+
+Object.assign(
+ Messages.prototype,
+ EventEmitterMethods,
+ methods,
+ ReceiveMethods,
+ GetQueryMethods
+);
+
+export default Messages;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/ReceiveMethods.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/ReceiveMethods.js
new file mode 100644
index 000000000..14e136719
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/ReceiveMethods.js
@@ -0,0 +1,89 @@
+var Methods = {
+ startReceiving() {
+ var query = this.getReceiverQuery(this.receiverID).orderBy('timestamp', 'desc').limit(1);
+ var self = this;
+ this.unsubscribe = query.onSnapshot(
+ {
+ includeMetadataChanges: true
+ },
+ function (querySnapshot) {
+ if (querySnapshot.size > 0) { // Load data
+ var doc = querySnapshot.docs[0];
+ if (doc.metadata.hasPendingWrites) { // Load local message
+ if (self.skipFirst) { // Local doc dose not have timestamp
+ self.skipFirst = false;
+ }
+ return;
+ }
+
+ self.resetPageQuery(self.receiverID, doc);
+
+ if (self.skipFirst) { // Load previos data
+ self.skipFirst = false;
+ } else {
+ var d = DocToMessage(doc);
+ self.cacheMessages.push(d);
+ self.emit('receive', d);
+ }
+ } else {
+ if (self.skipFirst) { // Start from an empty collection
+ self.skipFirst = false;
+ }
+ }
+ },
+ function (error) {
+ debugger
+ }
+ )
+
+ return this;
+ },
+
+ stopReceiving() {
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ }
+
+ // Reset to initial state
+ this.resetQueryFlag = true;
+ this.cacheMessages.length = 0;
+ return this;
+ },
+
+ loadPreviousMessages() {
+ this.resetPageQuery(this.receiverID);
+
+ var self = this;
+ return this.page.loadNextPage()
+ .then(function (docs) {
+ var messages = [];
+ for (var i = 0, cnt = docs.length; i < cnt; i++) {
+ messages.push(DocToMessage(docs[i]));
+ }
+
+ self.cacheMessages.splice(0, 0, ...messages);
+ return Promise.resolve(messages);
+ })
+ },
+
+ resetPageQuery(receiverID, baselineDoc) {
+ if (!this.resetQueryFlag) {
+ return this;
+ }
+
+ this.resetQueryFlag = false;
+ var baselineMode = (this.skipFirst) ? 'startAt' : 'startAfter';
+ this.page
+ .setBaselineDoc(baselineDoc, baselineMode)
+ .setQuery(this.getPageQuery(receiverID));
+ return this;
+ }
+}
+
+var DocToMessage = function (doc) {
+ var message = doc.data();
+ message.timestamp = message.timestamp.seconds * 1000;
+ return message;
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Send.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Send.js
new file mode 100644
index 000000000..89f2c03bd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/Send.js
@@ -0,0 +1,17 @@
+var Send = function (message) {
+ var d = {
+ senderID: this.userID,
+ message: message,
+ timestamp: firebase.firestore.FieldValue.serverTimestamp()
+ }
+ if (this.userName !== undefined) {
+ d.senderName = this.userName;
+ }
+ if (this.receiverID !== undefined) {
+ d.receiverID = this.receiverID;
+ }
+
+ return this.rootRef.add(d);
+}
+
+export default Send;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/schema.md b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/schema.md
new file mode 100644
index 000000000..967eff85d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/messages/schema.md
@@ -0,0 +1,6 @@
+-
+ - `senderID` - Unique ID of sender
+ - `senderName` - Name of sender
+ - `message` - Message
+ - `timestamp` - Server-timestamp
+ - `receiverID` - Unique ID of receiver, optional
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/Factory.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/Factory.js
new file mode 100644
index 000000000..e85842eec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/Factory.js
@@ -0,0 +1,11 @@
+import PageLoader from './PageLoader.js';
+import ObjectFactory from '../../ObjectFactory.js';
+import SetValue from '../../../utils/object/SetValue.js';
+
+ObjectFactory.register('pageLoader', function (config) {
+ return new PageLoader(config);
+});
+
+SetValue(window, 'RexPlugins.Fire.PageLoader', PageLoader);
+
+export default PageLoader;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadCurrentPage.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadCurrentPage.js
new file mode 100644
index 000000000..b348b1014
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadCurrentPage.js
@@ -0,0 +1,40 @@
+import Load from '../utils/query/Load.js';
+
+var LoadCurrentPage = function () {
+ if ((this.pageIndex === undefined) || (this.pageIndex === 0)) {
+ return this.loadFirstPage();
+ }
+
+ var callback = (this.dataMode === 0) ? LoadStaticPage : LoadDynamicPage;
+ return callback.call(this);
+}
+
+var LoadStaticPage = function () {
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, 0, this.prevPageEndDocRef, 'startAfter')
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ // Doc reference for paging
+ self.currPageStartDocRef = docs[0];
+ self.currPageEndDocRef = docs[docCount - 1];
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+var LoadDynamicPage = function () {
+ var skip = this.pageIndex * this.itemCount;
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, skip, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+export default LoadCurrentPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadFirstPage.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadFirstPage.js
new file mode 100644
index 000000000..da308675a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadFirstPage.js
@@ -0,0 +1,40 @@
+import Load from '../utils/query/Load.js';
+
+var LoadFirstPage = function () {
+ var callback = (this.dataMode === 0) ? LoadStaticPage : LoadDynamicPage;
+ return callback.call(this);
+}
+
+var LoadStaticPage = function () {
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, 0, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex = 0;
+ self.startItemIndex = 0;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ // Doc reference for paging
+ self.prevPageEndDocRef = undefined;
+ self.currPageStartDocRef = docs[0];
+ self.currPageEndDocRef = docs[docCount - 1];
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+var LoadDynamicPage = function () {
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, 0, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex = 0;
+ self.startItemIndex = 0;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+export default LoadFirstPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadInRange.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadInRange.js
new file mode 100644
index 000000000..f1503afb8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadInRange.js
@@ -0,0 +1,21 @@
+import Load from '../utils/query/Load.js';
+
+var LoadInRange = function (count, skip) {
+ if (skip === undefined) {
+ skip = 0;
+ }
+
+ var self = this;
+ return Load(this.nextQuery, count, skip, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex = undefined; // Not in Page mode
+ self.startItemIndex = skip;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (count === undefined) ? true : (docCount === count);
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+export default LoadInRange;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadNextPage.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadNextPage.js
new file mode 100644
index 000000000..699e50646
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadNextPage.js
@@ -0,0 +1,45 @@
+import Load from '../utils/query/Load.js';
+
+var LoadNextPage = function () {
+ if (this.pageIndex === undefined) {
+ return this.loadFirstPage();
+ }
+
+ var callback = (this.dataMode === 0) ? LoadStaticPage : LoadDynamicPage;
+ return callback.call(this);
+}
+
+var LoadStaticPage = function () {
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, 0, this.currPageEndDocRef, 'startAfter')
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex += 1;
+ self.startItemIndex = self.endItemIndex + 1;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ // Doc reference for paging
+ self.prevPageEndDocRef = self.currPageEndDocRef;
+ self.currPageStartDocRef = docs[0];
+ self.currPageEndDocRef = docs[docCount - 1];
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+var LoadDynamicPage = function () {
+ var skip = (this.pageIndex + 1) * this.itemCount;
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, skip, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex += 1;
+ self.startItemIndex = self.endItemIndex + 1;
+ self.endItemIndex = self.startItemIndex + docCount - 1;
+ self.isFullPage = (docCount === self.itemCount);
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+export default LoadNextPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadPreviousPage.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadPreviousPage.js
new file mode 100644
index 000000000..8c4ebc92e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/LoadPreviousPage.js
@@ -0,0 +1,49 @@
+import Load from '../utils/query/Load.js';
+
+var LoadPreviousPage = function () {
+ if ((this.pageIndex === undefined) || (this.pageIndex === 1)) {
+ return this.loadFirstPage();
+ }
+
+ var callback = (this.dataMode === 0) ? LoadStaticPage : LoadDynamicPage;
+ return callback.call(this);
+}
+
+var LoadStaticPage = function () {
+ var self = this;
+ return Load(this.prevQuery, (this.itemCount + 1), 0, this.currPageStartDocRef, 'startAfter')
+ .then(function (docs) {
+ // Get one more document for previous page end
+ var docCount = docs.length - 1;
+ self.cacheItems = docs;
+ self.cacheItems.pop(); // Pop up endDoc of previous page
+ self.cacheItems.reverse();
+ self.pageIndex -= 1;
+ self.endItemIndex = self.startItemIndex - 1;
+ self.startItemIndex = self.endItemIndex - docCount + 1;
+ self.isFullPage = (docCount === self.itemCount);
+ // Doc reference for paging
+ self.prevPageEndDocRef = docs[docCount];
+ self.currPageStartDocRef = docs[docCount - 1];
+ self.currPageEndDocRef = docs[0];
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+var LoadDynamicPage = function () {
+ var skip = (this.pageIndex - 1) * this.itemCount;
+ var self = this;
+ return Load(this.nextQuery, this.itemCount, skip, this.baselineDocRef, this.baselineMode)
+ .then(function (docs) {
+ // Get one more document for previous page end
+ var docCount = docs.length;
+ self.cacheItems = docs;
+ self.pageIndex -= 1;
+ self.endItemIndex = self.startItemIndex - 1;
+ self.startItemIndex = self.endItemIndex - docCount + 1;
+ self.isFullPage = (docCount === self.itemCount);
+ return Promise.resolve(self.cacheItems);
+ })
+}
+
+export default LoadPreviousPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/PageLoader.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/PageLoader.js
new file mode 100644
index 000000000..10b302192
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/pageloader/PageLoader.js
@@ -0,0 +1,81 @@
+import GetValue from '../../../utils/object/GetValue.js';
+import IsPlainObject from '../../../utils/object/IsPlainObject.js';
+import LoadFirstPage from './LoadFirstPage.js';
+import LoadNextPage from './LoadNextPage.js';
+import LoadPreviousPage from './LoadPreviousPage.js';
+import LoadCurrentPage from './LoadCurrentPage.js';
+import LoadInRange from './LoadInRange.js';
+
+class PageLoader {
+ constructor(config) {
+ this.setItemCount(GetValue(config, 'itemCount', 100));
+ this.setQuery(GetValue(config, 'query', undefined));
+ this.setDataMode(GetValue(config, 'dataMode', 0));
+ this.setBaselineDoc(GetValue(config, 'baselineDoc', undefined), GetValue(config, 'baselineMode', undefined));
+ this.pageIndex = undefined;
+ this.baselineDocRef = undefined;
+ this.baselineMode = 'startAt';
+ this.startItemIndex = undefined;
+ this.endItemIndex = undefined;
+ this.cacheItems = undefined;
+ this.isFullPage = undefined;
+ }
+
+ setItemCount(count) {
+ this.itemCount = count;
+ return this;
+ }
+
+ setQuery(nextQuery, prevQuery) {
+ if (IsPlainObject(nextQuery)) {
+ var config = nextQuery;
+ this.nextQuery = config.next;
+ this.prevQuery = config.previous;
+ } else {
+ this.nextQuery = nextQuery;
+ this.prevQuery = prevQuery;
+ }
+
+ this.pageIndex = undefined;
+ this.isFullPage = undefined;
+ return this;
+ }
+
+ setDataMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = DATAMODE[mode];
+ }
+ this.dataMode = mode;
+ return this;
+ }
+
+ setBaselineDoc(doc, mode) {
+ if (doc) {
+ this.baselineDocRef = doc.ref;
+ this.baselineMode = mode; // 'startAt' or 'startAfter'
+ } else {
+ this.baselineDocRef = undefined;
+ }
+ return this;
+ }
+}
+
+var methods = {
+ loadFirstPage: LoadFirstPage,
+ loadNextPage: LoadNextPage,
+ loadPreviousPage: LoadPreviousPage,
+ loadCurrentPage: LoadCurrentPage,
+ load: LoadInRange
+}
+
+Object.assign(
+ PageLoader.prototype,
+ methods
+);
+
+const DATAMODE = {
+ static: 0,
+ dynamic: 1
+}
+
+export default PageLoader;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Delete.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Delete.js
new file mode 100644
index 000000000..a47b87097
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Delete.js
@@ -0,0 +1,34 @@
+import Load from './Load.js';
+
+var Delete = function (query) {
+ return Load(query)
+ .then(function (docs) {
+ if (docs.length === 0) { // Last page, task done
+ return Promise.resolve();
+ }
+
+ var tasks = [];
+ var batch, actionCount;
+ for (var i = 0, cnt = docs.length; i < cnt; i++) {
+ if (batch === undefined) {
+ batch = firebase.firestore().batch();
+ actionCount = 0;
+ }
+
+ batch.delete(docs[i].ref);
+ actionCount++;
+ if (actionCount >= 500) {
+ tasks.push(batch.commit());
+ batch = undefined;
+ }
+ }
+
+ if (batch) {
+ tasks.push(batch.commit());
+ }
+
+ return Promise.all(tasks);
+ })
+}
+
+export default Delete;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/FindFirst.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/FindFirst.js
new file mode 100644
index 000000000..bae5c0f9b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/FindFirst.js
@@ -0,0 +1,30 @@
+import Query from './Query';
+
+var FindFirst = function (query, testCallback) {
+ var out = {
+ doc: undefined,
+ index: undefined
+ }
+ var startIndex = 0;
+ return Query({
+ query: query,
+ forEachPageCallback: function (querySnapshot) {
+ var docs = querySnapshot.docs,
+ doc;
+ for (var i = 0, cnt = docs.length; i < cnt; i++) {
+ doc = docs[i];
+ if (testCallback(doc)) {
+ out.doc = doc;
+ out.index = startIndex + i;
+ return true;
+ }
+ }
+ startIndex += querySnapshot.size;
+ },
+ resolveCallback: function () {
+ return out;
+ }
+ });
+}
+
+export default FindFirst;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Load.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Load.js
new file mode 100644
index 000000000..e6a07b0b5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Load.js
@@ -0,0 +1,38 @@
+import Query from './Query';
+
+var Load = function (query, count, skip, startDocRef, startMode) {
+ if (count === undefined) {
+ count = Infinity;
+ }
+ if (skip === undefined) {
+ skip = 0;
+ }
+
+ var out = [];
+ var startIndex = 0;
+ return Query({
+ query: query,
+ totalLines: (skip + count),
+ startDocRef: startDocRef,
+ startMode: startMode,
+ forEachPageCallback: function (querySnapshot) {
+ var validDocs;
+ var docCount = querySnapshot.size;
+ var localStart = skip - startIndex;
+ if (localStart <= 0) {
+ validDocs = querySnapshot.docs;
+ } else if (localStart < docCount) {
+ validDocs = querySnapshot.docs.slice(localStart, docCount);
+ }
+ if (validDocs) {
+ out.push(...validDocs);
+ }
+ startIndex += docCount;
+ },
+ resolveCallback: function () {
+ return out;
+ }
+ });
+}
+
+export default Load;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Query.js b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Query.js
new file mode 100644
index 000000000..f641a91ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/firestore/utils/query/Query.js
@@ -0,0 +1,42 @@
+var Query = function (config) {
+ if (config.totalLines === undefined) {
+ config.totalLines = Infinity;
+ }
+ if (config.linesPerPage === undefined) {
+ config.linesPerPage = 1000;
+ }
+ config.remainderLines = config.totalLines;
+
+ return QueryNextPage(config);
+}
+
+var QueryNextPage = function (config) {
+ var query = config.query;
+ if (config.startDocRef) {
+ query = query[config.startMode](config.startDocRef);
+ }
+
+ var lineCount = Math.min(config.remainderLines, config.linesPerPage);
+ config.remainderLines -= lineCount;
+ return query.limit(lineCount).get()
+ .then(function (querySnapshot) {
+ var done = (config.remainderLines === 0) || (querySnapshot.size < lineCount); // Is last page
+ if (config.forEachPageCallback) {
+ done |= !!config.forEachPageCallback(querySnapshot);
+ }
+
+ if (done) {
+ var out;
+ if (config.resolveCallback) {
+ out = config.resolveCallback();
+ }
+ return Promise.resolve(out);
+ } else {
+ config.startDocRef = querySnapshot.docs[querySnapshot.size - 1];
+ config.startMode = 'startAfter';
+ return QueryNextPage(config);
+ }
+ })
+}
+
+export default Query;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/preload/AvailableTest.js b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/AvailableTest.js
new file mode 100644
index 000000000..9aa3e52e6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/AvailableTest.js
@@ -0,0 +1,39 @@
+import Delay from '../../utils/promise/Delay.js';
+
+var AvailableTestPromise = function (config) {
+ if (AvailableTest(config)) {
+ return Promise.resolve();
+ }
+
+ // console.log('test again')
+ return Delay(10)
+ .then(function () {
+ return AvailableTestPromise(config);
+ });
+}
+
+var AvailableTest = function (config) {
+ var testCallback;
+ for (var k in config) {
+ if (!config[k]) {
+ continue;
+ }
+ testCallback = TestCallbacks[k];
+ if (testCallback && !testCallback()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+var TestCallbacks = {
+ database: function () {
+ return (firebase.database !== undefined);
+ },
+
+ firestore: function () {
+ return (firebase.firestore !== undefined);
+ }
+}
+
+export default AvailableTestPromise;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/preload/GetDefaultUrl.js b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/GetDefaultUrl.js
new file mode 100644
index 000000000..6c9c76b98
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/GetDefaultUrl.js
@@ -0,0 +1,23 @@
+const VERSION = '7.19.0';
+
+var GetDefaultUrl = function (version) {
+ if (version === undefined) {
+ version = VERSION
+ }
+ return {
+ app: `https://www.gstatic.com/firebasejs/${version}/firebase-app.js`,
+
+ // auth: `https://www.gstatic.com/firebasejs/${version}/firebase-auth.js`,
+ database: `https://www.gstatic.com/firebasejs/${version}/firebase-database.js`,
+ firestore: `https://www.gstatic.com/firebasejs/${version}/firebase-firestore.js`,
+ // storage: `https://www.gstatic.com/firebasejs/${version}/firebase-storage.js`,
+
+ // analytics: `https://www.gstatic.com/firebasejs/${version}/firebase-analytics.js`,
+ // functions: `https://www.gstatic.com/firebasejs${version}/firebase-functions.js`,
+ // messaging: `https://www.gstatic.com/firebasejs/${version}/firebase-messaging.js`,
+ // performance: `https://www.gstatic.com/firebasejs/${version}/firebase-performance.js`,
+ // 'remote-config': `https://www.gstatic.com/firebasejs/${version}/firebase-remote-config.js`
+ }
+}
+
+export default GetDefaultUrl;
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/preload/LoaderCallback.js b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/LoaderCallback.js
new file mode 100644
index 000000000..438b1a5dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/LoaderCallback.js
@@ -0,0 +1,19 @@
+import Preload from './Preload.js';
+import AwaitFile from '../../loader/awaitloader/AwaitFile.js';
+
+const LoaderCallback = function (urlConfig, firebaseConfig) {
+ var callback = function (successCallback, failureCallback) {
+ return Preload(urlConfig, firebaseConfig)
+ .then(function () {
+ setTimeout(successCallback, 0);
+ })
+ .catch(failureCallback)
+ }
+
+ this.addFile(new AwaitFile(this, {
+ config: { callback: callback }
+ }));
+ return this;
+}
+
+export default LoaderCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/firebase/preload/Preload.js b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/Preload.js
new file mode 100644
index 000000000..7d0b1bb65
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/firebase/preload/Preload.js
@@ -0,0 +1,45 @@
+import GetDefaultUrl from './GetDefaultUrl.js';
+import MergeRight from '../../utils/object/MergeRight.js';
+import LoadScriptPromise from '../../utils/loader/LoadScriptPromise.js';
+import AvailableTest from './AvailableTest.js';
+
+var Preload = function (urlConfig, firebaseConfig) {
+ if (typeof (urlConfig) === 'string') { // Get specific version
+ urlConfig = GetDefaultUrl(urlConfig);
+ } else { // Get default version
+ urlConfig = MergeRight(GetDefaultUrl(), urlConfig);
+ }
+
+ return LoadScriptPromise(urlConfig.app) // Load firebase-app
+ .then(function () { // Load other SDK
+ var promises = [];
+ var url;
+ for (var k in urlConfig) {
+ if (k === 'app') {
+ continue;
+ }
+ url = urlConfig[k];
+ if (!url) {
+ continue;
+ }
+ promises.push(LoadScriptPromise(url))
+ }
+
+ if (promises.length === 0) {
+ return Promise.resolve();
+ } else {
+ return Promise.all(promises);
+ }
+ })
+ .then(function () { // Wait until all vairalbe are available
+ return AvailableTest(urlConfig);
+ })
+ .then(function () {
+ if (firebaseConfig !== undefined) {
+ firebase.initializeApp(firebaseConfig);
+ }
+ return Promise.resolve();
+ })
+}
+
+export default Preload;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.d.ts
new file mode 100644
index 000000000..57e40d170
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.d.ts
@@ -0,0 +1,29 @@
+// import * as Phaser from 'phaser';
+import FishEyePostFxPipeline from './fisheyepipeline';
+
+export default FishEyePipelinePlugin;
+
+declare namespace FishEyePipelinePlugin {
+
+ interface IConfig extends FishEyePostFxPipeline.IConfig {
+ name?: string
+ }
+
+}
+
+declare class FishEyePipelinePlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: FishEyePipelinePlugin.IConfig
+ ): FishEyePostFxPipeline;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): this;
+
+ get(
+ gameObject: Phaser.GameObjects.GameObject,
+ name?: string
+ ): FishEyePostFxPipeline | FishEyePostFxPipeline[];
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.js b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.js
new file mode 100644
index 000000000..760b98fec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline-plugin.js
@@ -0,0 +1,14 @@
+import FishEyePostFxPipeline from './fisheyepipeline';
+import BasePostFxPipelinePlugin from './utils/renderer/postfxpipeline/BasePostFxPipelinePlugin.js';
+import SetValue from './utils/object/SetValue.js';
+
+class FishEyePipelinePlugin extends BasePostFxPipelinePlugin {
+ constructor(pluginManager) {
+ super(pluginManager);
+ this.setPostPipelineClass(FishEyePostFxPipeline, 'rexFishEyePostFx');
+ }
+}
+
+SetValue(window, 'RexPlugins.Pipelines.FishEyePostFx', FishEyePostFxPipeline);
+
+export default FishEyePipelinePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.d.ts b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.d.ts
new file mode 100644
index 000000000..801a23a50
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.d.ts
@@ -0,0 +1,2 @@
+import FishEyePostFxPipeline from './shaders/fisheye/FishEyePostFxPipeline';
+export default FishEyePostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.js b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.js
new file mode 100644
index 000000000..801a23a50
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fisheyepipeline.js
@@ -0,0 +1,2 @@
+import FishEyePostFxPipeline from './shaders/fisheye/FishEyePostFxPipeline';
+export default FishEyePostFxPipeline;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flash-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/flash-plugin.d.ts
new file mode 100644
index 000000000..0cca0fde0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flash-plugin.d.ts
@@ -0,0 +1,9 @@
+import Flash from './flash';
+
+export default class FlashPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Flash.IConfig
+ ): Flash;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flash-plugin.js b/ui/src/phaser3-rex-plugins/plugins/flash-plugin.js
new file mode 100644
index 000000000..f237c2458
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flash-plugin.js
@@ -0,0 +1,19 @@
+import Flash from './flash.js';
+
+class FlashPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Flash(gameObject, config);
+ }
+}
+
+export default FlashPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flash.d.ts b/ui/src/phaser3-rex-plugins/plugins/flash.d.ts
new file mode 100644
index 000000000..fecc0a093
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flash.d.ts
@@ -0,0 +1,2 @@
+import Flash from './behaviors/flash/Flash';
+export default Flash;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flash.js b/ui/src/phaser3-rex-plugins/plugins/flash.js
new file mode 100644
index 000000000..651b3a238
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flash.js
@@ -0,0 +1,2 @@
+import Flash from './behaviors/flash/Flash.js';
+export default Flash;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flip-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/flip-plugin.d.ts
new file mode 100644
index 000000000..87966f3d5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flip-plugin.d.ts
@@ -0,0 +1,9 @@
+import Flip from './flip';
+
+export default class FlipPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ gameObject: Phaser.GameObjects.GameObject,
+ config?: Flip.IConfig
+ ): Flip;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flip-plugin.js b/ui/src/phaser3-rex-plugins/plugins/flip-plugin.js
new file mode 100644
index 000000000..9b82b16fa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flip-plugin.js
@@ -0,0 +1,19 @@
+import Flip from './flip.js';
+
+class FlipPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(gameObject, config) {
+ return new Flip(gameObject, config);
+ }
+}
+
+export default FlipPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flip.d.ts b/ui/src/phaser3-rex-plugins/plugins/flip.d.ts
new file mode 100644
index 000000000..a78302543
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flip.d.ts
@@ -0,0 +1,2 @@
+import Flip from './behaviors/flip/Flip';
+export default Flip;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/flip.js b/ui/src/phaser3-rex-plugins/plugins/flip.js
new file mode 100644
index 000000000..2f9e05f43
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/flip.js
@@ -0,0 +1,2 @@
+import Flip from './behaviors/flip/Flip.js';
+export default Flip;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.d.ts
new file mode 100644
index 000000000..9bbd028b2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.d.ts
@@ -0,0 +1,8 @@
+import FSM from './fsm';
+
+export default class FSMPlugin extends Phaser.Plugins.BasePlugin {
+ add(
+ config?: FSM.IConfig
+ ): FSM;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.js b/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.js
new file mode 100644
index 000000000..28343fc4c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fsm-plugin.js
@@ -0,0 +1,23 @@
+import FSM from './fsm.js';
+import SetValue from './utils/object/SetValue.js';
+
+class FSMPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(config) {
+ return new FSM(config);
+ }
+
+}
+
+SetValue(window, 'RexPlugins.FSM', FSM);
+
+export default FSMPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fsm.d.ts b/ui/src/phaser3-rex-plugins/plugins/fsm.d.ts
new file mode 100644
index 000000000..46db5644d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fsm.d.ts
@@ -0,0 +1,2 @@
+import FSM from './logic/fsm/FSM';
+export default FSM;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fsm.js b/ui/src/phaser3-rex-plugins/plugins/fsm.js
new file mode 100644
index 000000000..8f1a7603b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fsm.js
@@ -0,0 +1,2 @@
+import FSM from './logic/fsm/FSM.js';
+export default FSM;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle-plugin.js b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle-plugin.js
new file mode 100644
index 000000000..674d310ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle-plugin.js
@@ -0,0 +1,23 @@
+import Factory from './gameobjects/shape/fullwindowrectangle/Factory.js';
+import Creator from './gameobjects/shape/fullwindowrectangle/Creator.js';
+import FullWindowRectangle from './gameobjects/shape/fullwindowrectangle/FullWindowRectangle.js';
+import SetValue from './utils/object/SetValue.js';
+
+class FullWindowRectanglePlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+
+ // Register our new Game Object type
+ pluginManager.registerGameObject('rexFullWindowRectangle', Factory, Creator);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+}
+
+SetValue(window, 'RexPlugins.GameObjects.FullWindowRectangle', FullWindowRectangle);
+
+export default FullWindowRectanglePlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.d.ts b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.d.ts
new file mode 100644
index 000000000..a7c7ffddb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.d.ts
@@ -0,0 +1,2 @@
+import FullWindowRectangle from './gameobjects/shape/fullwindowrectangle/FullWindowRectangle.js';
+export default FullWindowRectangle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.js b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.js
new file mode 100644
index 000000000..a7c7ffddb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fullwindowrectangle.js
@@ -0,0 +1,2 @@
+import FullWindowRectangle from './gameobjects/shape/fullwindowrectangle/FullWindowRectangle.js';
+export default FullWindowRectangle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.d.ts b/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.d.ts
new file mode 100644
index 000000000..5979eafd5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.d.ts
@@ -0,0 +1,5 @@
+import BuildFuzzyModule from './fuzzy';
+
+export default class FuzzyPlugin extends Phaser.Plugins.BasePlugin {
+ add: typeof BuildFuzzyModule
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.js b/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.js
new file mode 100644
index 000000000..986e71ec6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fuzzy-plugin.js
@@ -0,0 +1,20 @@
+import BuildFuzzyModule from './fuzzy';
+
+class FuzzyPlugin extends Phaser.Plugins.BasePlugin {
+
+ constructor(pluginManager) {
+ super(pluginManager);
+ }
+
+ start() {
+ var eventEmitter = this.game.events;
+ eventEmitter.on('destroy', this.destroy, this);
+ }
+
+ add(config) {
+ return BuildFuzzyModule(config);
+ }
+
+}
+
+export default FuzzyPlugin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fuzzy.d.ts b/ui/src/phaser3-rex-plugins/plugins/fuzzy.d.ts
new file mode 100644
index 000000000..62100a8f4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fuzzy.d.ts
@@ -0,0 +1,2 @@
+import BuildFuzzyModule from './math/fuzzy/BuildFuzzyModule';
+export default BuildFuzzyModule;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/fuzzy.js b/ui/src/phaser3-rex-plugins/plugins/fuzzy.js
new file mode 100644
index 000000000..85ea4ea58
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/fuzzy.js
@@ -0,0 +1,2 @@
+import BuildFuzzyModule from './math/fuzzy/BuildFuzzyModule.js';
+export default BuildFuzzyModule;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/BitmapText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/BitmapText.js
new file mode 100644
index 000000000..60a8fb5e0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/BitmapText.js
@@ -0,0 +1,51 @@
+import Blitter from '../blitterbase/BlitterBase.js';
+import Methods from './methods/Methods.js';
+import PenManager from './penmanager/PenManager.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class BitmapText extends Blitter {
+ constructor(scene, x, y, font, text, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ font = GetValue(config, 'font', '');
+ text = GetValue(config, 'text', '');
+ }
+
+ super(scene, x, y);
+ this.type = 'rexBitmapText';
+
+ this.fontData = undefined;
+ this.fromAtlas = undefined;
+ this._fontSize = 0;
+ this._text = '';
+ this.penManager = new PenManager(this, config);
+
+ this.setFixedSize(GetValue(config, 'fixedWidth', 0), GetValue(config, 'fixedHeight', 0));
+ this.setPadding(GetValue(config, 'padding', 0));
+ this.setLetterSpacing(GetValue(config, 'letterSpacing', 0));
+
+
+ this.setFont(font);
+
+ this.setText(text);
+ }
+
+ get text() {
+ return this._text;
+ }
+
+ set text(value) {
+ this.setText(text);
+ }
+}
+
+Object.assign(
+ BitmapText.prototype,
+ Methods
+);
+
+export default BitmapText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/AppendText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/AppendText.js
new file mode 100644
index 000000000..2f3fffbcb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/AppendText.js
@@ -0,0 +1,21 @@
+var AppendText = function (value) {
+ if (value == null) {
+ value = '';
+ } else if (Array.isArray(value)) {
+ value = value.join('\n');
+ } else {
+ value = value.toString();
+ }
+
+ if (value === '') {
+ return this;
+ }
+
+ this._text += value;
+
+ this.penManager.addTextPens(value);
+
+ return this;
+}
+
+export default AppendText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/Methods.js
new file mode 100644
index 000000000..b3287a5af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/Methods.js
@@ -0,0 +1,25 @@
+import SetFixedSize from './SetFixedSize.js';
+import SetPadding from './SetPadding.js';
+import SetLetterSpacing from './SetLetterSpacing.js';
+
+import SetFont from './SetFont.js';
+import SetText from './SetText.js';
+import AppendText from './AppendText.js';
+import SetWrapConfig from './SetWrapConfig.js';
+import UpdateText from './UpdateText.js';
+
+const Methods = {
+ setFixedSize: SetFixedSize,
+ setPadding: SetPadding,
+ setLetterSpacing: SetLetterSpacing,
+
+ setFont: SetFont,
+ setText: SetText,
+ appendText: AppendText,
+ setWrapConfig: SetWrapConfig,
+ updateText: UpdateText,
+
+
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunVerticalWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunVerticalWrap.js
new file mode 100644
index 000000000..9cf287920
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunVerticalWrap.js
@@ -0,0 +1,5 @@
+var RunVerticalWrap = function () {
+
+}
+
+export default RunVerticalWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunWordWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunWordWrap.js
new file mode 100644
index 000000000..00f133642
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/RunWordWrap.js
@@ -0,0 +1,5 @@
+var RunWordWrap = function () {
+
+}
+
+export default RunWordWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFixedSize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFixedSize.js
new file mode 100644
index 000000000..1dfe78593
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFixedSize.js
@@ -0,0 +1,15 @@
+var SetFixedSize = function (width, height) {
+ if (width === undefined) {
+ width = 0;
+ }
+ if (height === undefined) {
+ height = 0;
+ }
+
+ this.fixedWidth = width;
+ this.fixedHeight = height;
+
+ return this;
+}
+
+export default SetFixedSize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFont.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFont.js
new file mode 100644
index 000000000..7224d02f9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetFont.js
@@ -0,0 +1,30 @@
+var SetFont = function (key) {
+ if (key === this.font) {
+ return this;
+ }
+
+ var entry = this.scene.sys.cache.bitmapFont.get(key);
+
+ this.font = key;
+
+ if (entry) {
+ this.fontData = entry.data;
+ this.fromAtlas = (entry.fromAtlas === true);
+ this._fontSize = this.fontData.size;
+
+ this.setTexture(entry.texture, entry.frame);
+ } else {
+ console.warn(`Invalid BitmapText key: ${key}`);
+
+ this.fontData = undefined;
+ this.fromAtlas = undefined;
+ this._fontSize = 0;
+ this.setTexture();
+ }
+
+ this.updateText();
+
+ return this;
+}
+
+export default SetFont;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetLetterSpacing.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetLetterSpacing.js
new file mode 100644
index 000000000..44e34ecdc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetLetterSpacing.js
@@ -0,0 +1,6 @@
+var SetLetterSpacing = function (spacing) {
+ this.letterSpacing = spacing;
+ return this;
+}
+
+export default SetLetterSpacing;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetPadding.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetPadding.js
new file mode 100644
index 000000000..55e2a752a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetPadding.js
@@ -0,0 +1,9 @@
+import { SetPadding as SetPaddingBase } from '../../../../utils/padding/PaddingMethods.js';
+
+var SetPadding = function (key, value) {
+ SetPaddingBase(this.padding, key, value);
+
+ return this;
+};
+
+export default SetPadding;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetText.js
new file mode 100644
index 000000000..5677eedb6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetText.js
@@ -0,0 +1,21 @@
+var SetText = function (value) {
+ if (value == null) {
+ value = '';
+ } else if (Array.isArray(value)) {
+ value = value.join('\n');
+ } else {
+ value = value.toString();
+ }
+
+ if (value === this._text) {
+ return this;
+ }
+
+ this._text = value;
+
+ this.penManager.setTextPens(value);
+
+ return this;
+}
+
+export default SetText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetWrapConfig.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetWrapConfig.js
new file mode 100644
index 000000000..46856752e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/SetWrapConfig.js
@@ -0,0 +1,10 @@
+var SetWrapConfig = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ this.wrapConfig = config;
+ return this;
+}
+
+export default SetWrapConfig;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateCharacterDataManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateCharacterDataManager.js
new file mode 100644
index 000000000..152adbde3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateCharacterDataManager.js
@@ -0,0 +1,13 @@
+var UpdateCharacterDataManager = function (text, wrapMode, wrapWidth, lineHeight, characterDataManager) {
+ if (characterDataManager === undefined) {
+ characterDataManager = this.characterDataManager;
+ }
+ characterDataManager.clear();
+ if (text === "") {
+ return characterDataManager;
+ }
+
+
+}
+
+export default UpdateCharacterDataManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateText.js
new file mode 100644
index 000000000..b90d5a7bb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/methods/UpdateText.js
@@ -0,0 +1,4 @@
+var UpdateText = function () {
+}
+
+export default UpdateText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/PenManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/PenManager.js
new file mode 100644
index 000000000..78b9dafe5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/PenManager.js
@@ -0,0 +1,30 @@
+import Methods from './methods/Methods.js';
+import Pool from '../../../../pool.js';
+
+const GetFastValue = Phaser.Utils.Objects.GetFastValue;
+
+var PensPool = new Pool(); // default pens pool
+var LinesPool = new Pool(); // default lines pool
+
+class PenManager {
+ constructor(bitmapText, config) {
+ this.bitmapText = bitmapText;
+ this.pens = [];
+ this.lines = []; // pens in lines [ [],[],[],.. ]
+ this.maxLinesWidth = undefined;
+
+ this.PensPool = GetFastValue(config, 'pensPool', PensPool);
+ this.LinesPool = GetFastValue(config, 'linesPool', LinesPool);
+ }
+
+ destroy() {
+ this.clear();
+ }
+}
+
+Object.assign(
+ PenManager.prototype,
+ Methods
+);
+
+export default PenManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/AddTextPens.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/AddTextPens.js
new file mode 100644
index 000000000..b672193a1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/AddTextPens.js
@@ -0,0 +1,13 @@
+import CharPen from '../pen/CharPen.js';
+
+var AddTextPens = function (text) {
+ for (var i = 0, cnt = text.length; i < cnt; i++) {
+ var pen = new CharPen(this)
+ .setChar(text.charAt(i))
+ this.pens.push(pen);
+ }
+
+ return this;
+}
+
+export default AddTextPens;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/Methods.js
new file mode 100644
index 000000000..0759ad1be
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/Methods.js
@@ -0,0 +1,19 @@
+import RemovePenMethods from './RemovePenMethods.js';
+import AddTextPens from './AddTextPens.js';
+import SetTextPens from './SetTextPens.js';
+import RunWordWrap from './runwordwrap/RunWordWrap.js';
+import RunVerticalWrap from './runverticalwrap/RunVerticalWrap.js';
+
+var Methods = {
+ addTextPens: AddTextPens,
+ setTextPens: SetTextPens,
+ runWordWrap: RunWordWrap,
+ runVerticalWrap: RunVerticalWrap,
+}
+
+Object.assign(
+ Methods,
+ RemovePenMethods
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/RemovePenMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/RemovePenMethods.js
new file mode 100644
index 000000000..0e963b9c5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/RemovePenMethods.js
@@ -0,0 +1,56 @@
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+var Methods = {
+ free(pen) {
+ if (!this.PensPool) {
+ return this;
+ }
+ this.PensPool.push(pen);
+ pen.onFree();
+
+ for (var i = 0, cnt = this.lines.length; i < cnt; i++) {
+ RemoveItem(this.lines[i], pen);
+ }
+ return this;
+ },
+
+ freeMultiple(pens) {
+ if (!this.PensPool) {
+ return this;
+ }
+
+ for (var i = 0, cnt = pens.length; i < cnt; i++) {
+ this.free(pens[i]);
+ }
+
+ return this;
+ },
+
+ clear() {
+ // 1. Remove/recycle all children of blitter
+ this.parent.removeChildren();
+ // 2. Free all pens.
+ for (var i = 0, cnt = this.pens.length; i < cnt; i++) {
+ this.pens[i].onFree();
+ }
+ // 3. Free all lines
+ for (var i = 0, len = this.lines.length; i < len; i++) {
+ this.lines[i].length = 0;
+ }
+
+ // 4.
+ this.PensPool.pushMultiple(this.pens);
+ this.LinesPool.pushMultiple(this.lines);
+ this.maxLinesWidth = undefined;
+
+ return this;
+ },
+
+ remove(pen) {
+ this.free(pen);
+
+ return this;
+ }
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/SetTextPens.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/SetTextPens.js
new file mode 100644
index 000000000..edbd6d134
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/SetTextPens.js
@@ -0,0 +1,9 @@
+var SetTextPens = function (text) {
+ this
+ .clear()
+ .addTextPens(text);
+
+ return this;
+}
+
+export default SetTextPens;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runverticalwrap/RunVerticalWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runverticalwrap/RunVerticalWrap.js
new file mode 100644
index 000000000..fb84c3210
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runverticalwrap/RunVerticalWrap.js
@@ -0,0 +1,5 @@
+var RunVerticalWrap = function (config) {
+
+}
+
+export default RunVerticalWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/GetWord.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/GetWord.js
new file mode 100644
index 000000000..a405dd30b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/GetWord.js
@@ -0,0 +1,36 @@
+var GetWord = function (pens, startIndex, charMode, result) {
+ if (result === undefined) {
+ result = { word: [], width: 0 };
+ }
+
+ result.word.length = 0;
+
+ var endIndex = pens.length;
+ var currentIndex = startIndex;
+ var word = result.word, wordWidth = 0;
+ while (currentIndex < endIndex) {
+ var pen = pens[currentIndex];
+ var char = pen.char;
+ if ((char !== undefined) && (char !== ' ') && (char !== '\n')) {
+ word.push(pen);
+ wordWidth += pen.outerWidth;
+ currentIndex++;
+ // Continue
+ } else { // Get non-text pen, a space, or a new-line
+ if (currentIndex === startIndex) { // Single pen
+ word.push(pen);
+ wordWidth += pen.outerWidth;
+ }
+ break;
+ }
+
+ if (charMode) { // Word only contains 1 character
+ break;
+ }
+ }
+
+ result.width = wordWidth;
+ return result;
+}
+
+export default GetWord;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/RunWordWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/RunWordWrap.js
new file mode 100644
index 000000000..82214c45c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/methods/runwordwrap/RunWordWrap.js
@@ -0,0 +1,17 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var RunWordWrap = function () {
+ var bitmapText = this.bitmapText;
+
+ var startX = bitmapText.padding.left,
+ startY = bitmapText.padding.top,
+ x = startX,
+ y = startY;
+
+ var pens = this.pens;
+ for (var i = 0, cnt = pens.length; i < cnt; i++) {
+
+ }
+}
+
+export default RunWordWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/Base.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/Base.js
new file mode 100644
index 000000000..f6ed91f13
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/Base.js
@@ -0,0 +1,46 @@
+import BobBase from '../../../blitterbase/bob/Base.js';
+
+class Base {
+ constructor(parent) {
+ this
+ .setParent(parent)
+
+ this.bobs = undefined; // bob, or dictionary of bobs
+ }
+
+ setParent(parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ get bitmapText() {
+ if (this.parent) {
+ return this.parent.bitmapText;
+ } else {
+ return undefined;
+ }
+ }
+
+ onFree() {
+ this
+ .setParent();
+
+ if (this.bobs instanceof BobBase) {
+ this.bobs.destroy();
+ this.bobs = undefined;
+ } else {
+ var bobs = this.bobs;
+ for (var key in bobs) {
+ bobs[key].destroy();
+ delete bobs[key];
+ }
+ }
+ }
+
+ destroy() {
+ this.parent.remove(this);
+ }
+
+}
+
+export default Base;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/CharPen.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/CharPen.js
new file mode 100644
index 000000000..ccd6b8c79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/CharPen.js
@@ -0,0 +1,24 @@
+import ImagePen from './ImagePen.js';
+
+class CharPen extends ImagePen {
+ onFree() {
+ this.char = undefined;
+ super.onFree();
+ }
+
+ setChar(char) {
+ this.char = char;
+ var fontData = this.bitmapText.fontData;
+ if (!fontData) {
+ return this;
+ }
+
+ var frame = fontData.chars[char.charCodeAt(0)];
+ this.setFrame(frame);
+
+ return this;
+ }
+
+}
+
+export default CharPen;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/ImagePen.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/ImagePen.js
new file mode 100644
index 000000000..47bf0243d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/bitmaptext/penmanager/pen/ImagePen.js
@@ -0,0 +1,127 @@
+import Base from './Base.js';
+import AddImage from '../../../blitterbase/utils/AddImage.js';
+
+class ImagePen extends Base {
+ constructor(parent) {
+ super(parent);
+ this.bobs = {};
+
+ this._x = 0;
+ this._y = 0;
+ this.leftSpace = 0;
+ this.rightSpace = 0;
+
+ this.addImage('main', 0);
+ }
+
+ onFree() {
+ this.frame = undefined;
+
+ this
+ .setLeftSpace(0)
+ .setRightSpace(0);
+
+ super.onFree();
+ }
+
+ setFrame(frame) {
+ this.frame = frame;
+
+ var bobs = this.bobs;
+ for (var key in bobs) {
+ bobs[key].setFrame(frame);
+ }
+
+ return this;
+ }
+
+ addImage(key, depth) {
+ var bob = AddImage(this.bitmapText, this.frame)
+ .setPosition(this.x, this.y)
+ .setDepth(depth);
+
+ this.bobs[key] = bob;
+
+ return this;
+ }
+
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ var dx = value - this._x;
+ this._x = value;
+
+ var bobs = this.bobs;
+ for (var key in bobs) {
+ bobs[key].x += dx;
+ }
+ }
+
+ get y() {
+ return this._y;
+ }
+
+ set y(value) {
+ var dy = value - this._y;
+ this._y = value;
+
+ var bobs = this.bobs;
+ for (var key in bobs) {
+ bobs[key].y += dy;
+ }
+ }
+
+ setPosition(x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ setLeftSpace(value) {
+ this.leftSpace = value;
+ return this;
+ }
+
+ setRightSpace(value) {
+ this.rightSpace = value;
+ return this;
+ }
+
+ get width() {
+ return this.bobs.main.width;
+ }
+
+ get outerWidth() {
+ return this.width + this.leftSpace + this.rightSpace;
+ }
+
+ setShadow(x, y, color, alpha) {
+ this.shadowX = x;
+ this.shadowY = y;
+ this.shadowColor = color;
+ this.shadowAlpha = alpha;
+
+ if (!this.bobs.shadow) {
+ this.addImage('shadow', -1);
+ }
+
+ var bob = this.bobs.shadow;
+ if (x === undefined) {
+ bob.setActive(false);
+ } else {
+ bob
+ .setActive(true)
+ .setPosition(this.x + this.shadowX, this.y + this.shadowY)
+ .setAlpha(alpha)
+ .setColor(color)
+ .setTintEffect(2)
+ }
+
+ return this;
+ }
+
+}
+
+export default ImagePen;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.d.ts
new file mode 100644
index 000000000..5fec746b4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.d.ts
@@ -0,0 +1,14 @@
+import BlitterBase from '../blitterbase/BlitterBase.js';
+import ImageData from '../blitterbase/bob/image/ImageData';
+
+export default Blitter;
+
+declare class Blitter extends BlitterBase {
+ addImage(
+ frame: string
+ ): this;
+
+ addImage(
+ config: ImageData.IModifyConfig
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.js
new file mode 100644
index 000000000..5c00bdcd5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Blitter.js
@@ -0,0 +1,11 @@
+import BlitterBase from '../blitterbase/BlitterBase.js';
+import AddImage from '../blitterbase/utils/AddImage.js';
+
+class Blitter extends BlitterBase {
+ addImage(config) {
+ AddImage(this, config);
+ return this;
+ }
+}
+
+export default Blitter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Creator.js
new file mode 100644
index 000000000..cfadd8ec5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Creator.js
@@ -0,0 +1,16 @@
+import Blitter from './Blitter.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var key = GetAdvancedValue(config, 'key', null);
+ var frame = GetAdvancedValue(config, 'frame', null);
+ var gameObject = new Blitter(this.scene, 0, 0, key, frame, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Factory.js
new file mode 100644
index 000000000..f07560a19
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitter/Factory.js
@@ -0,0 +1,7 @@
+import Blitter from './Blitter.js';
+
+export default function (x, y, texture, frame, config) {
+ var gameObject = new Blitter(this.scene, x, y, texture, frame, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.d.ts
new file mode 100644
index 000000000..b5f7e4ce7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.d.ts
@@ -0,0 +1,150 @@
+import BobBase from './bob/Base';
+
+export default BlitterBase;
+
+declare namespace BlitterBase {
+ interface IConfig {
+ reuseBob?: boolean,
+ }
+}
+
+declare class BlitterBase extends Phaser.GameObjects.GameObject {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number,
+ y?: number,
+ texture?: string,
+ frame?: string,
+ config?: BlitterBase.IConfig
+ );
+
+ children: Phaser.Structs.List;
+
+ texture: Phaser.Textures.Texture | Phaser.Textures.CanvasTexture;
+ frame: Phaser.Textures.Frame;
+ setTexture(
+ key: string,
+ frame?: string | number | undefined,
+ ): this;
+
+ resize(width: number, height: number): this;
+ setSize(width: number, height: number): this;
+
+ addChild(
+ bob: BobBase
+ ): this;
+
+ removeChild(
+ bob: BobBase
+ ): this;
+
+ removeChildren(): this;
+
+ clear(): this;
+
+ getLastAppendedChildren(
+ ): BobBase[];
+
+ getChildren(
+ ): BobBase[];
+
+
+ setTint(tint: number): this;
+ setTintFill(tint: number): this;
+ clearTint(): this;
+ tint: number;
+ tintFill: boolean;
+
+ // Components
+ clearAlpha(): this;
+ setAlpha(topLeft?: number, topRight?: number, bottomLeft?: number, bottomRight?: number): this;
+ alpha: number;
+ alphaTopLeft: number;
+ alphaTopRight: number;
+ alphaBottomLeft: number;
+ alphaBottomRight: number;
+
+ blendMode: Phaser.BlendModes | string;
+ setBlendMode(value: string | Phaser.BlendModes): this;
+
+ width: number;
+ height: number;
+ displayWidth: number;
+ displayHeight: number;
+ setDisplaySize(width: number, height: number): this;
+
+ depth: number;
+ setDepth(value: number): this;
+
+ getCenter(output?: O): O;
+ getTopLeft(output?: O, includeParent?: boolean): O;
+ getTopCenter(output?: O, includeParent?: boolean): O;
+ getTopRight(output?: O, includeParent?: boolean): O;
+ getLeftCenter(output?: O, includeParent?: boolean): O;
+ getRightCenter(output?: O, includeParent?: boolean): O;
+ getBottomLeft(output?: O, includeParent?: boolean): O;
+ getBottomCenter(output?: O, includeParent?: boolean): O;
+ getBottomRight(output?: O, includeParent?: boolean): O;
+ getBounds(output?: O): O;
+
+ mask: Phaser.Display.Masks.BitmapMask | Phaser.Display.Masks.GeometryMask;
+ setMask(mask: Phaser.Display.Masks.BitmapMask | Phaser.Display.Masks.GeometryMask): this;
+ clearMask(destroyMask?: boolean): this;
+ createBitmapMask(renderable?: Phaser.GameObjects.GameObject): Phaser.Display.Masks.BitmapMask;
+ createGeometryMask(graphics?: Phaser.GameObjects.Graphics): Phaser.Display.Masks.GeometryMask;
+
+ originX: number;
+ originY: number;
+ displayOriginX: number;
+ displayOriginY: number;
+ setOrigin(x?: number, y?: number): this;
+ setOriginFromFrame(): this;
+ setDisplayOrigin(x?: number, y?: number): this;
+ updateDisplayOrigin(): this;
+
+ defaultPipeline: Phaser.Renderer.WebGL.WebGLPipeline;
+ pipeline: Phaser.Renderer.WebGL.WebGLPipeline;
+ hasPostPipeline: boolean;
+ postPipelines: Phaser.Renderer.WebGL.Pipelines.PostFXPipeline[];
+ pipelineData: object;
+ initPipeline(pipeline: string | Phaser.Renderer.WebGL.WebGLPipeline): boolean;
+ setPipeline(pipeline: string | Phaser.Renderer.WebGL.WebGLPipeline, pipelineData?: object, copyData?: boolean): this;
+ setPostPipeline(pipelines: string | string[] | Function | Function[] | Phaser.Renderer.WebGL.Pipelines.PostFXPipeline | Phaser.Renderer.WebGL.Pipelines.PostFXPipeline[], pipelineData?: object, copyData?: boolean): this;
+ setPipelineData(key: string, value?: any): this;
+ getPostPipeline(pipeline: string | Function | Phaser.Renderer.WebGL.Pipelines.PostFXPipeline): Phaser.Renderer.WebGL.Pipelines.PostFXPipeline | Phaser.Renderer.WebGL.Pipelines.PostFXPipeline[];
+ resetPipeline(resetPostPipelines?: boolean, resetData?: boolean): boolean;
+ resetPostPipeline(resetData?: boolean): void;
+ removePostPipeline(pipeline: string | Phaser.Renderer.WebGL.Pipelines.PostFXPipeline): this;
+ getPipelineName(): string;
+
+ scrollFactorX: number;
+ scrollFactorY: number;
+ setScrollFactor(x: number, y?: number): this;
+
+ x: number;
+ y: number;
+ z: number;
+ w: number;
+ scale: number;
+ scaleX: number;
+ scaleY: number;
+ angle: number;
+ rotation: number;
+ setPosition(x?: number, y?: number, z?: number, w?: number): this;
+ copyPosition(source: Phaser.Types.Math.Vector2Like | Phaser.Types.Math.Vector3Like | Phaser.Types.Math.Vector4Like): this;
+ setRandomPosition(x?: number, y?: number, width?: number, height?: number): this;
+ setRotation(radians?: number): this;
+ setAngle(degrees?: number): this;
+ setScale(x: number, y?: number): this;
+ setX(value?: number): this;
+ setY(value?: number): this;
+ setZ(value?: number): this;
+ setW(value?: number): this;
+ getLocalTransformMatrix(tempMatrix?: Phaser.GameObjects.Components.TransformMatrix): Phaser.GameObjects.Components.TransformMatrix;
+ getWorldTransformMatrix(tempMatrix?: Phaser.GameObjects.Components.TransformMatrix, parentMatrix?: Phaser.GameObjects.Components.TransformMatrix): Phaser.GameObjects.Components.TransformMatrix;
+ getLocalPoint(x: number, y: number, point?: Phaser.Math.Vector2, camera?: Phaser.Cameras.Scene2D.Camera): Phaser.Math.Vector2;
+ getParentRotation(): number;
+
+ visible: boolean;
+ setVisible(value: boolean): this;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.js
new file mode 100644
index 000000000..7f39928f6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/BlitterBase.js
@@ -0,0 +1,114 @@
+import Render from './render/Render.js';
+import Methods from './methods/Methods.js';
+import PoolManager from './poolmanager/PoolManager.js';
+
+const GameObject = Phaser.GameObjects.GameObject;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const List = Phaser.Structs.List;
+const StableSort = Phaser.Utils.Array.StableSort;
+
+class Blitter extends GameObject {
+ constructor(scene, x, y, texture, frame, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ texture = GetValue(config, 'texture');
+ frame = GetValue(config, 'frame');
+ }
+
+ if (x === undefined) {
+ x = 0;
+ }
+ if (y === undefined) {
+ y = 0;
+ }
+
+ super(scene, 'rexBlitter');
+
+ this.children = new List();
+ this.renderList = [];
+ this.displayListDirty = false;
+ this.lastAppendedChildren = [];
+
+ var reuseBob = GetValue(config, 'reuseBob', true);
+ this.poolManager = (reuseBob) ? (new PoolManager(config)) : undefined;
+
+ this
+ .setTexture(texture, frame)
+ .setPosition(x, y)
+ .setOrigin(0, 0)
+ .clearTint()
+ .initPipeline()
+
+ }
+
+ preDestroy() {
+ this.removeChildren();
+ this.children.destroy();
+ this.renderList.length = 0;
+
+ if (this.poolManager) {
+ this.poolManager.destroy();
+ }
+ }
+
+ getRenderList() {
+ if (this.displayListDirty) {
+ this.renderList.length = 0;
+ var needDepthSort = false;
+
+ var children = this.children.list;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (ChildCanRender(child)) {
+ this.renderList.push(child);
+
+ if (!needDepthSort) {
+ needDepthSort = (child.depth !== 0);
+ }
+ }
+ }
+
+ if (needDepthSort) {
+ StableSort(this.renderList, SortByDepth)
+ }
+
+ this.displayListDirty = false;
+ }
+
+ return this.renderList;
+ }
+}
+
+var ChildCanRender = function (child) {
+ return (child.active && child.visible && (child.alpha > 0));
+}
+
+var SortByDepth = function (childA, childB) {
+ return childA._depth - childB._depth;
+}
+
+const Components = Phaser.GameObjects.Components;
+Phaser.Class.mixin(Blitter,
+ [
+ Components.Alpha,
+ Components.BlendMode,
+ Components.ComputedSize,
+ Components.Depth,
+ Components.GetBounds,
+ Components.Mask,
+ Components.Origin,
+ Components.Pipeline,
+ Components.ScrollFactor,
+ Components.Transform,
+ Components.Visible,
+ Render,
+
+ Methods
+ ]
+);
+
+
+export default Blitter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.d.ts
new file mode 100644
index 000000000..2c10560f2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.d.ts
@@ -0,0 +1,23 @@
+import BlitterBase from '../BlitterBase';
+import DataMethods from '../../../../utils/data/DataMethods';
+
+export default BobBase;
+
+declare class BobBase extends DataMethods{
+ constructor(
+ parent: BlitterBase,
+ type: string
+ );
+
+ parent: BlitterBase;
+ setParent(
+ parent?: BlitterBase
+ ): this;
+
+ active: boolean;
+ setActive(
+ active?: boolean
+ ): this;
+
+ reset(): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.js
new file mode 100644
index 000000000..418b7f903
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Base.js
@@ -0,0 +1,81 @@
+import DataMethods from '../../../../utils/data/DataMethods.js'
+
+class Base {
+ constructor(parent, type) {
+ this.type = type;
+
+ this.data = undefined;
+
+ this
+ .setParent(parent)
+ .reset()
+ .setActive();
+
+ }
+
+ destroy() {
+ if (this.parent) {
+ this.parent.removeChild(this);
+ // Remove this bob from blitter, and free it (bob.onFree())
+ // Will set this.parent to undefined
+ }
+ }
+
+ setParent(parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ // get scene() {
+ // if (this.parent) {
+ // return this.parent.scene;
+ // } else {
+ // return null;
+ // }
+ // }
+
+ setDisplayListDirty(displayListDirty) {
+ if (displayListDirty && this.parent) {
+ this.parent.displayListDirty = true;
+ }
+ return this;
+ }
+
+ get active() {
+ return this._active;
+ }
+
+ set active(value) {
+ this.setDisplayListDirty(this._active != value);
+ this._active = value;
+ }
+
+ setActive(active) {
+ if (active === undefined) {
+ active = true;
+ }
+ this.active = active;
+ return this;
+ }
+
+ modifyPorperties(o) {
+ return this;
+ }
+
+ // Override
+ reset() {
+ this.clearData();
+ }
+
+ // Override
+ onFree() {
+ this.reset().setActive(false).setParent();
+ }
+}
+
+Object.assign(
+ Base.prototype,
+ DataMethods
+);
+
+export default Base;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.d.ts
new file mode 100644
index 000000000..662f3d70e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.d.ts
@@ -0,0 +1,81 @@
+import BobBase from './Base';
+
+export default RenderBase;
+
+declare namespace RenderBase {
+ interface IModifyConfig {
+ x?: number, y?: number,
+
+ rotation?: number, angle?: number,
+
+ alpha?: number,
+
+ width?: number, height?: number,
+ scale?: number, scaleX?: number, scaleY?: number,
+ displayWidth?: number, displayHeight?: number,
+
+ origin?: number, originX?: number, originY?: number,
+
+ depth?: number
+ }
+}
+
+declare class RenderBase extends BobBase {
+ visible: boolean;
+ setVisible(visible?: boolean): this;
+
+ alpha: number;
+ setAlpha(alpha: number): this;
+
+ x: number;
+ y: number;
+ setX(x: number): this;
+ setY(y: number): this;
+ setPosition(x: number, y: number): this;
+
+ rotation: number;
+ angle: number;
+ setRotation(rotation: number): this;
+ setAngle(angle: number): this;
+
+ width: number;
+ height: number;
+ setWidth(
+ width: number,
+ keepAspectRatio?: boolean
+ ): this;
+ setHeight(
+ height: number,
+ keepAspectRatio?: boolean
+ ): this;
+
+ scaleX: number;
+ scaleY: number;
+ setScaleX(scaleX: number): this;
+ setScaleY(scaleY: number): this;
+ setScale(scaleX: number, scaleY: number): this;
+
+ displayWidth: number;
+ displayHeight: number;
+ setDisplayWidth(
+ width: number,
+ keepAspectRatio?: boolean
+ ): this;
+ setDisplayHeight(
+ height: number,
+ keepAspectRatio?: boolean
+ ): this;
+
+ originX: number;
+ originY: number;
+ setOriginX(originX: number): this;
+ setOriginY(originY: number): this;
+ setOrigin(originX: number, originY: number): this;
+
+ depth: number;
+ setDepth(depth?: number): this;
+
+ modifyPorperties(
+ o?: RenderBase.IModifyConfig
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.js
new file mode 100644
index 000000000..63c40b151
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/RenderBase.js
@@ -0,0 +1,319 @@
+import Base from './Base.js';
+
+const DegToRad = Phaser.Math.DegToRad;
+const RadToDeg = Phaser.Math.RadToDeg;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class RenderBase extends Base {
+
+ get visible() {
+ return this._visible;
+ }
+
+ set visible(value) {
+ this.setDisplayListDirty(this._visible != value);
+ this._visible = value;
+ }
+
+ setVisible(visible) {
+ if (visible === undefined) {
+ visible = true;
+ }
+
+ this.visible = visible;
+ return this;
+ }
+
+ get alpha() {
+ return this._alpha;
+ }
+
+ set alpha(value) {
+ this.setDisplayListDirty(!!this._alpha !== !!value);
+ this._alpha = value;
+ }
+
+ setAlpha(alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ setX(x) {
+ this.x = x;
+ return this;
+ }
+
+ setY(y) {
+ this.y = y;
+ return this;
+ }
+
+ setPosition(x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ setRotation(rotation) {
+ this.rotation = rotation;
+ return this;
+ }
+
+ get angle() {
+ return RadToDeg(this.rotation);
+ }
+
+ set angle(value) {
+ this.rotation = DegToRad(value);
+ }
+
+ setAngle(angle) {
+ this.angle = angle;
+ return this;
+ }
+
+ setScaleX(scaleX) {
+ this.scaleX = scaleX;
+ return this;
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ this._width = value;
+ }
+
+ setWidth(width, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+ this.width = width;
+
+ if (keepAspectRatio) {
+ this.scaleY = this.scaleX;
+ }
+ return this;
+ }
+
+ setScaleY(scaleY) {
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ setScale(scaleX, scaleY) {
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ this._height = value;
+ }
+
+ setHeight(height, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+ this.height = height;
+
+ if (keepAspectRatio) {
+ this.scaleX = this.scaleY;
+ }
+ return this;
+ }
+
+ setScale(scaleX, scaleY) {
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ get displayWidth() {
+ return this._width * this.scaleX;
+ }
+
+ set displayWidth(value) {
+ this.scaleX = value / this._width;
+ }
+
+ setDisplayWidth(width, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+
+ this.displayWidth = width;
+
+ if (keepAspectRatio) {
+ this.scaleY = this.scaleX;
+ }
+ return this;
+ }
+
+ get displayHeight() {
+ return this._height * this.scaleY;
+ }
+
+ set displayHeight(value) {
+ this.scaleY = value / this._height;
+ }
+
+ setDisplayHeight(height, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+
+ this.displayHeight = height;
+
+ if (keepAspectRatio) {
+ this.scaleX = this.scaleY;
+ }
+ return this;
+ }
+
+ setOriginX(originX) {
+ this.originX = originX;
+ this._displayOriginX = this.width * originX;
+ return this;
+ }
+
+ setOriginY(originY) {
+ this.originY = originY;
+ this._displayOriginY = this.height * originY;
+ return this;
+ }
+
+ setOrigin(originX, originY) {
+ if (originY === undefined) {
+ originY = originX;
+ }
+ this.setOriginX(originX).setOriginY(originY);
+ return this;
+ }
+
+ get depth() {
+ return this._depth;
+ }
+
+ set depth(value) {
+ this.setDisplayListDirty(this._depth != value);
+ this._depth = value;
+ }
+
+ setDepth(depth) {
+ if (depth === undefined) {
+ depth = 0;
+ }
+
+ this.depth = depth;
+ return this;
+ }
+
+ modifyPorperties(o) {
+ if (!o) {
+ return this;
+ }
+
+ if (o.hasOwnProperty('x')) {
+ this.setX(o.x);
+ }
+ if (o.hasOwnProperty('y')) {
+ this.setY(o.y);
+ }
+
+ if (o.hasOwnProperty('rotation')) {
+ this.setRotation(o.rotation);
+ } else if (o.hasOwnProperty('angle')) {
+ this.setAngle(o.angle);
+ }
+
+ if (o.hasOwnProperty('alpha')) {
+ this.setAlpha(o.alpha);
+ }
+
+ // ScaleX, ScaleY
+ var width = GetValue(o, 'width', undefined);
+ var height = GetValue(o, 'height', undefined);
+ var scale = GetValue(o, 'scale', undefined);
+ var scaleX = GetValue(o, 'scaleX', scale);
+ var scaleY = GetValue(o, 'scaleY', scale);
+
+ if (width !== undefined) {
+ if ((height === undefined) && (scaleY === undefined)) {
+ this.setWidth(width, true);
+ } else {
+ this.setWidth(width);
+ }
+ } else if (scaleX !== undefined) {
+ this.setScaleX(scaleX);
+ } else if (o.hasOwnProperty('displayWidth')) {
+ this.setDisplayWidth(o.displayWidth);
+ }
+
+ if (height !== undefined) {
+ if ((width === undefined) && (scaleX === undefined)) {
+ this.setHeight(height, true);
+ } else {
+ this.setHeight(height);
+ }
+ } else if (scaleY !== undefined) {
+ this.setScaleY(scaleY);
+ } else if (o.hasOwnProperty('displayHeight')) {
+ this.setDisplayHeight(o.displayHeight);
+ }
+
+ var origin = GetValue(o, 'origin', undefined);
+ if (origin !== undefined) {
+ this.setOrigin(origin);
+ } else {
+ if (o.hasOwnProperty('originX')) {
+ this.setOriginX(o.originX);
+ }
+ if (o.hasOwnProperty('originY')) {
+ this.setOriginY(o.originY);
+ }
+ }
+
+ if (o.hasOwnProperty('depth')) {
+ this.setDepth(o.depth);
+ }
+
+ return this;
+ }
+
+ reset() {
+ super.reset();
+
+ this
+ .setVisible()
+ .setAlpha(1)
+ .setPosition(0, 0)
+ .setRotation(0)
+ .setScale(1, 1)
+ .setOrigin(0)
+ .setDepth(0)
+
+ return this;
+ }
+
+ // Override
+ webglRender(pipeline, calcMatrix, alpha, dx, dy, texture, textureUnit, roundPixels) {
+ }
+ // Override
+ canvasRender(ctx, dx, dy, roundPixels) {
+ }
+}
+
+export default RenderBase;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Types.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Types.js
new file mode 100644
index 000000000..5b1111d9f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/Types.js
@@ -0,0 +1,5 @@
+const ImageTypeName = 'image';
+
+export {
+ ImageTypeName,
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/CanvasRender.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/CanvasRender.js
new file mode 100644
index 000000000..ef740db9c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/CanvasRender.js
@@ -0,0 +1,45 @@
+var CanvasRender = function (ctx, dx, dy, roundPixels) {
+
+ ctx.save();
+
+ var width = this._width,
+ height = this._height;
+ var displayOriginX = width * this.originX,
+ displayOriginY = height * this.originY;
+ var x = this.x - displayOriginX,
+ y = this.y - displayOriginY;
+
+ var flipX = 1;
+ var flipY = 1;
+
+ if (this.flipX) {
+ x += width;
+ flipX = -1;
+ }
+ if (this.flipY) {
+ y += height;
+ flipY = -1;
+ }
+
+ if (roundPixels) {
+ x = Math.round(x);
+ y = Math.round(y);
+ }
+
+ ctx.translate(x, y);
+
+ ctx.rotate(this.rotation);
+
+ ctx.scale(this.scaleX * flipX, this.scaleY * flipY);
+
+ var frame = this.frame;
+ ctx.drawImage(
+ frame.source.image,
+ frame.cutX, frame.cutY, width, height,
+ 0, 0, width, height,
+ );
+
+ ctx.restore();
+
+}
+export default CanvasRender;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.d.ts
new file mode 100644
index 000000000..722c8751d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.d.ts
@@ -0,0 +1,46 @@
+import RenderBase from '../RenderBase';
+
+export default ImageData;
+
+declare namespace ImageData {
+ interface IFrame {
+ width: number,
+ height: number,
+ u0: number,
+ v0: number,
+ u1: number,
+ v1: number,
+ }
+
+ interface IModifyConfig extends RenderBase.IModifyConfig {
+ frame?: string | IFrame,
+
+ flipX?: boolean, flipY?: boolean,
+
+ tint?: number, tintFill?: number
+ }
+}
+
+declare class ImageData extends RenderBase {
+ setFrame(
+ frame?: string | ImageData.IFrame
+ ): this;
+
+
+ flipX: boolean;
+ flipY: boolean;
+ setFlipX(flipX?: boolean): this;
+ setFlipY(flipY?: boolean): this;
+ resetFlip(): this;
+
+ tint: number;
+ tintFill: boolean;
+ setTint(value: number): this;
+ setTintFill(value: number): this;
+ clearTint(): this;
+ resetTint(): this;
+
+ modifyPorperties(
+ o?: ImageData.IModifyConfig
+ ): this;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.js
new file mode 100644
index 000000000..61222729a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/ImageData.js
@@ -0,0 +1,172 @@
+import RenderBase from '../RenderBase.js';
+import { ImageTypeName } from '../Types.js';
+import WebglRender from './WebglRender.js';
+import CanvasRender from './CanvasRender.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+
+class ImageData extends RenderBase {
+ constructor(parent, frame) {
+ super(parent, ImageTypeName);
+
+ this.setFrame(frame);
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ }
+
+ setFrame(frame) {
+ if (arguments.length > 0 && !IsPlainObject(frame)) {
+ frame = this.parent.texture.get(frame);
+ }
+ this.frame = frame;
+ this._width = (frame) ? frame.width : 0;
+ this._height = (frame) ? frame.height : 0;
+ return this;
+ }
+
+ setFlipX(flipX) {
+ if (flipX === undefined) {
+ flipX = true;
+ }
+ this.flipX = flipX;
+ return this;
+ }
+
+ setFlipY(flipY) {
+ if (flipY === undefined) {
+ flipY = true;
+ }
+ this.flipY = flipY;
+ return this;
+ }
+
+ resetFlip() {
+ this.flipX = false;
+ this.flipY = false;
+ return this;
+ }
+
+ get tint() {
+ if (this._tint === undefined) {
+ return this.parent.tint;
+ } else {
+ return this._tint;
+ }
+ }
+
+ set tint(value) {
+ this._tint = value;
+ }
+
+
+ setTint(value) {
+ this.tint = value;
+ this.tintFill = false;
+ return this;
+ }
+
+ setTintFill(value) {
+ this.tint = value;
+ this.tintFill = true;
+ return this;
+ }
+
+ clearTint() {
+ this.setTint(0xffffff);
+ return this;
+ }
+
+ resetTint() {
+ this.tint = undefined;
+ this.tintFill = undefined;
+ return this;
+ }
+
+ get tintFill() {
+ if (this._tintFill === undefined) {
+ return this.parent.tintFill;
+ } else {
+ return this._tintFill;
+ }
+ }
+
+ set tintFill(value) {
+ this._tintFill = value;
+ }
+
+ reset() {
+ super.reset();
+
+ this
+ .resetFlip()
+ .resetTint()
+ .setFrame();
+
+ return this;
+ }
+
+ modifyPorperties(o) {
+ if (!o) {
+ return this;
+ }
+
+ // Size of Image is equal to frame size,
+ // Move width, height properties to displayWidth,displayHeight
+ if (o.hasOwnProperty('width')) {
+ o.displayWidth = o.width;
+ delete o.width;
+ }
+ if (o.hasOwnProperty('height')) {
+ o.displayHeight = o.height;
+ delete o.height;
+ }
+
+ if (o.hasOwnProperty('frame')) {
+ this.setFrame(o.frame);
+ }
+
+ super.modifyPorperties(o);
+
+ if (o.hasOwnProperty('flipX')) {
+ this.setFlipX(o.flipX);
+ }
+ if (o.hasOwnProperty('flipY')) {
+ this.setFlipY(o.flipY);
+ }
+
+ if (o.hasOwnProperty('tint')) {
+ this.setTint(o.tint);
+ }
+
+ if (o.hasOwnProperty('tintFill')) {
+ this.setTintFill(o.tintFill);
+ }
+
+ return this;
+ }
+
+}
+
+var methods = {
+ webglRender: WebglRender,
+ canvasRender: CanvasRender,
+}
+
+Object.assign(
+ ImageData.prototype,
+ methods
+);
+
+export default ImageData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/WebglRender.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/WebglRender.js
new file mode 100644
index 000000000..ccccc9c6a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/bob/image/WebglRender.js
@@ -0,0 +1,55 @@
+const TransformMatrix = Phaser.GameObjects.Components.TransformMatrix;
+const GetTint = Phaser.Renderer.WebGL.Utils.getTintAppendFloatAlpha;
+
+var FrameMatrix = new TransformMatrix();
+
+var WebglRender = function (pipeline, calcMatrix, alpha, dx, dy, texture, textureUnit, roundPixels) {
+ var width = this._width,
+ height = this._height;
+ var displayOriginX = width * this.originX,
+ displayOriginY = height * this.originY;
+ var x = this.x - dx,
+ y = this.y - dy;
+
+ var flipX = 1;
+ var flipY = 1;
+
+ if (this.flipX) {
+ x += width - (displayOriginX * 2);
+ flipX = -1;
+ }
+ if (this.flipY) {
+ y += height - (displayOriginY * 2);
+ flipY = -1;
+ }
+
+ FrameMatrix.applyITRS(x, y, this.rotation, this.scaleX * flipX, this.scaleY * flipY);
+ calcMatrix.multiply(FrameMatrix, FrameMatrix);
+
+ var tx = -displayOriginX;
+ var ty = -displayOriginY;
+ var tw = tx + width;
+ var th = ty + height;
+
+ var quad = FrameMatrix.setQuad(tx, ty, tw, th, roundPixels);
+
+ var u0 = this.frame.u0;
+ var v0 = this.frame.v0;
+ var u1 = this.frame.u1;
+ var v1 = this.frame.v1;
+
+ var tint = GetTint(this.tint, this.alpha * alpha);
+
+ pipeline.batchQuad(
+ this.parent,
+ quad[0], quad[1], quad[2], quad[3], quad[4], quad[5], quad[6], quad[7],
+ u0, v0,
+ u1, v1,
+ tint, tint, tint, tint,
+ this.tintFill,
+ texture,
+ textureUnit
+ );
+}
+
+export default WebglRender;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/AddChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/AddChild.js
new file mode 100644
index 000000000..482525bba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/AddChild.js
@@ -0,0 +1,15 @@
+var AddChild = function (bob) {
+ this.lastAppendedChildren.length = 0;
+
+ if (Array.isArray(bob)) {
+ this.children.add(bob)
+ this.lastAppendedChildren.push(...bob);
+ } else {
+ this.children.add(bob);
+ this.lastAppendedChildren.push(bob);
+ }
+
+ return this;
+}
+
+export default AddChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetChildren.js
new file mode 100644
index 000000000..169eaa94f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetChildren.js
@@ -0,0 +1,5 @@
+var GetChildren = function () {
+ return this.children.list;
+}
+
+export default GetChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetLastAppendedChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetLastAppendedChildren.js
new file mode 100644
index 000000000..d09335ca2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/GetLastAppendedChildren.js
@@ -0,0 +1,5 @@
+var GetLastAppendedChildren = function () {
+ return this.lastAppendedChildren;
+}
+
+export default GetLastAppendedChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Methods.js
new file mode 100644
index 000000000..d793706a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Methods.js
@@ -0,0 +1,27 @@
+import SetTexture from './SetTexture.js';
+import Resize from './Resize.js';
+import AddChild from './AddChild.js';
+import RemoveChild from './RemoveChild.js';
+import RemoveChildren from './RemoveChildren.js';
+import GetLastAppendedChildren from './GetLastAppendedChildren.js';
+import GetChildren from './GetChildren.js';
+import TintMethods from './TintMethods.js';
+
+var methods = {
+ setTexture: SetTexture,
+ resize: Resize,
+ setSize: Resize,
+ addChild: AddChild,
+ removeChild: RemoveChild,
+ removeChildren: RemoveChildren,
+ clear: RemoveChildren,
+ getLastAppendedChildren: GetLastAppendedChildren,
+ getChildren: GetChildren,
+}
+
+Object.assign(
+ methods,
+ TintMethods
+)
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/PopReusedBob.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/PopReusedBob.js
new file mode 100644
index 000000000..6bd1864c5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/PopReusedBob.js
@@ -0,0 +1,6 @@
+var PopReusedBob = function (typeName) {
+ var bob = (this.poolManager) ? this.poolManager.allocate(typeName) : null;
+ return bob;
+}
+
+export default PopReusedBob;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChild.js
new file mode 100644
index 000000000..b1d4a86c6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChild.js
@@ -0,0 +1,16 @@
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+var RemoveChild = function (bob) {
+ if (this.poolManager) {
+ // Free this bob (bob.onFree())
+ this.poolManager.free(bob);
+ }
+
+ // Remove this bob from blitter
+ RemoveItem(this.children.list, bob);
+ this.lastAppendedChildren.length = 0;
+ this.dirty = true;
+ return this;
+}
+
+export default RemoveChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChildren.js
new file mode 100644
index 000000000..5b55c6bff
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/RemoveChildren.js
@@ -0,0 +1,14 @@
+var RemoveChildren = function () {
+ if (this.poolManager) {
+ // Free all bobs (bob.onFree())
+ this.poolManager.freeMultiple(this.children.list);
+ }
+
+ // Remove all bobs from blitter
+ this.children.list.length = 0;
+ this.lastAppendedChildren.length = 0;
+ this.dirty = true;
+ return this;
+}
+
+export default RemoveChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Resize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Resize.js
new file mode 100644
index 000000000..b64482063
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/Resize.js
@@ -0,0 +1,21 @@
+var Resize = function (width, height) {
+ if ((this.width === width) && (this.height === height)) {
+ return this;
+ }
+
+ this.width = width;
+ this.height = height;
+
+ this.updateDisplayOrigin();
+
+ var input = this.input;
+
+ if (input && !input.customHitArea) {
+ input.hitArea.width = width;
+ input.hitArea.height = height;
+ }
+
+ return this;
+}
+
+export default Resize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/SetTexture.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/SetTexture.js
new file mode 100644
index 000000000..317c3f3f0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/SetTexture.js
@@ -0,0 +1,7 @@
+var SetTexture = function (key, frame) {
+ this.texture = this.scene.sys.textures.get(key);
+ this.frame = this.texture.get(frame);
+ return this;
+}
+
+export default SetTexture;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/TintMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/TintMethods.js
new file mode 100644
index 000000000..811883fbd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/methods/TintMethods.js
@@ -0,0 +1,20 @@
+export default {
+ setTint(tint) {
+ // 0: Solid tint + texture alpha
+ this.tint = tint;
+ this.tintFill = false;
+ return this;
+ },
+
+ setTintFill(tint) {
+ // 1: Solid tint, no texture
+ this.tint = tint;
+ this.tintFill = true;
+ return this;
+ },
+
+ clearTint() {
+ this.setTint(0xffffff);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/poolmanager/PoolManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/poolmanager/PoolManager.js
new file mode 100644
index 000000000..6c3c5f647
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/poolmanager/PoolManager.js
@@ -0,0 +1,48 @@
+import Pool from '../../../../pool.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var Pools = {};
+class PoolManager {
+ constructor(config) {
+ this.pools = GetValue(config, 'pools', Pools);
+ }
+
+ destroy() {
+ this.pools = undefined;
+ }
+
+ free(bob) {
+ if (!this.pools) {
+ return this;
+ }
+
+ var bobType = bob.type;
+ if (!this.pools.hasOwnProperty(bobType)) {
+ this.pools[bobType] = new Pool();
+ }
+ this.pools[bobType].push(bob);
+ bob.onFree();
+ return this;
+ }
+
+ freeMultiple(bobs) {
+ if (!this.pools) {
+ return this;
+ }
+
+ for (var i = 0, cnt = bobs.length; i < cnt; i++) {
+ this.free(bobs[i]);
+ }
+ return this;
+ }
+
+ allocate(bobType) {
+ if (!this.pools || !this.pools.hasOwnProperty(bobType)) {
+ return null;
+ }
+ return this.pools[bobType].pop();
+ }
+}
+
+export default PoolManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/CanvasRenderer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/CanvasRenderer.js
new file mode 100644
index 000000000..19294f3af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/CanvasRenderer.js
@@ -0,0 +1,28 @@
+const SetTransform = Phaser.Renderer.Canvas.SetTransform;
+
+var CanvasRenderer = function (renderer, src, camera, parentMatrix) {
+ var ctx = renderer.currentContext;
+
+ var bobs = src.getRenderList();
+ if ((bobs.length === 0) || (!SetTransform(renderer, ctx, src, camera, parentMatrix))) {
+ return;
+ }
+
+ camera.addToRenderList(src);
+
+ var roundPixels = camera.roundPixels;
+
+ var dx = -src._displayOriginX,
+ dy = -src._displayOriginY;
+
+ ctx.translate(dx, dy);
+
+ for (var i = 0, cnt = bobs.length; i < cnt; i++) {
+ bobs[i].canvasRender(ctx, dx, dy, roundPixels);
+ }
+
+ // Restore the context saved in SetTransform
+ ctx.restore();
+};
+
+export default CanvasRenderer;
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/Render.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/Render.js
new file mode 100644
index 000000000..404fc7750
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/Render.js
@@ -0,0 +1,8 @@
+import WebGLRenderer from './WebGLRenderer.js';
+import CanvasRenderer from './CanvasRenderer.js';
+
+export default {
+ renderWebGL: WebGLRenderer,
+ renderCanvas: CanvasRenderer
+
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/WebGLRenderer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/WebGLRenderer.js
new file mode 100644
index 000000000..5bad4fe94
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/render/WebGLRenderer.js
@@ -0,0 +1,37 @@
+const GetCalcMatrix = Phaser.GameObjects.GetCalcMatrix;
+
+var WebGLRenderer = function (renderer, src, camera, parentMatrix) {
+ var bobs = src.getRenderList();
+ if (bobs.length === 0) {
+ return;
+ }
+
+ camera.addToRenderList(src);
+
+ var pipeline = renderer.pipelines.set(src.pipeline);
+
+ var texture = src.frame.glTexture;
+
+ var textureUnit = pipeline.setGameObject(src);
+
+ var roundPixels = camera.roundPixels;
+
+ var result = GetCalcMatrix(src, camera, parentMatrix);
+
+ var calcMatrix = pipeline.calcMatrix.copyFrom(result.calc);
+
+ var dx = src._displayOriginX;
+ var dy = src._displayOriginY;
+
+ var alpha = camera.alpha * src.alpha;
+
+ renderer.pipelines.preBatch(src);
+
+ for (var i = 0, cnt = bobs.length; i < cnt; i++) {
+ bobs[i].webglRender(pipeline, calcMatrix, alpha, dx, dy, texture, textureUnit, roundPixels);
+ }
+
+ renderer.pipelines.postBatch(src);
+};
+
+export default WebGLRenderer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/utils/AddImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/utils/AddImage.js
new file mode 100644
index 000000000..e7d5e6846
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/blitterbase/utils/AddImage.js
@@ -0,0 +1,24 @@
+import { ImageTypeName } from '../../blitterbase/bob/Types.js';
+import ImageData from '../../blitterbase/bob/image/ImageData.js';
+
+var AddImage = function (blitter, config) {
+ if (typeof (config) === 'string') {
+ config = {
+ frame: config
+ }
+ }
+
+ var bob = (blitter.poolManager) ? blitter.poolManager.allocate(ImageTypeName) : null;
+ if (bob === null) {
+ bob = new ImageData(blitter);
+ } else {
+ bob.setParent(blitter).setActive();
+ }
+ bob.modifyPorperties(config);
+
+ blitter.addChild(bob);
+
+ return bob;
+}
+
+export default AddImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Creator.js
new file mode 100644
index 000000000..515ea877b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Creator.js
@@ -0,0 +1,13 @@
+import NinePatch from './NinePatch.js';
+
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var gameObject = new NinePatch(this.scene, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Factory.js
new file mode 100644
index 000000000..aaa324623
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Factory.js
@@ -0,0 +1,7 @@
+import NinePatch from './NinePatch.js';
+
+export default function (x, y, width, height, key, baseFrame, columns, rows, config) {
+ var gameObject = new NinePatch(this.scene, x, y, width, height, key, baseFrame, columns, rows, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Methods.js
new file mode 100644
index 000000000..feab6cee2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/Methods.js
@@ -0,0 +1,7 @@
+import DrawImage from './texture/DrawImage.js';
+import DrawTileSprite from './texture/DrawTileSprite.js';
+
+export default {
+ _drawImage: DrawImage,
+ _drawTileSprite: DrawTileSprite,
+};
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.d.ts
new file mode 100644
index 000000000..f5e46eb07
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.d.ts
@@ -0,0 +1,115 @@
+// import * as Phaser from 'phaser';
+import Blitter from '../blitter/Blitter';
+
+export default NinePatch;
+
+declare namespace NinePatch {
+
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+
+ key?: string, baseFrame?: string,
+ getFrameNameCallback?: (colIndex: number, rowIndex: number, baseFrame: string) => (string | undefined),
+
+ columns?: (number | undefined)[],
+ rows?: (number | undefined)[],
+
+ stretchMode?: 0 | 1 | 'scale' | 'repeat' |
+ {
+ edge?: 0 | 1 | 'scale' | 'repeat',
+ internal?: 0 | 1 | 'scale' | 'repeat',
+ },
+
+ maxFixedPartScale?: number,
+ maxFixedPartScaleX?: number,
+ maxFixedPartScaleY?: number,
+
+ preserveRatio?: boolean,
+ }
+
+}
+
+declare class NinePatch extends Blitter {
+ constructor(
+ scene: Phaser.Scene,
+ config?: NinePatch.IConfig
+ )
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ config?: NinePatch.IConfig
+ )
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config?: NinePatch.IConfig
+ )
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ key: string,
+ config?: NinePatch.IConfig
+ )
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ key: string,
+ columns: (number | undefined)[], rows: (number | undefined)[],
+ config?: NinePatch.IConfig
+ )
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ key: string, baseFrame: string,
+ columns: (number | undefined)[], rows: (number | undefined)[],
+ config?: NinePatch.IConfig
+ )
+
+ resize(width: number, height: number): this;
+
+ setBaseTexture(
+ key: string,
+ baseFrame: string | undefined,
+ columns: (number | undefined)[],
+ rows: (number | undefined)[]
+ ): this;
+
+ setStretchMode(
+ mode: 0 | 1 | 'scale' | 'repeat' |
+ {
+ edge?: 0 | 1 | 'scale' | 'repeat',
+ internal?: 0 | 1 | 'scale' | 'repeat',
+ }
+ ): this;
+
+ setGetFrameNameCallback(
+ callback: (colIndex: number, rowIndex: number, baseFrame: string) => (string | undefined)
+ ): this;
+
+ updateTexture(): this;
+
+ setPreserveRatio(enable?: boolean): this;
+ preserveRatio: boolean;
+
+ setMaxFixedPartScale(scaleX: number, scaleY?: number): this;
+ maxFixedPartScaleX: number;
+ maxFixedPartScaleY: number;
+
+ readonly minWidth: number;
+
+ readonly minHeight: number;
+
+ readonly fixedPartScaleX: number;
+
+ readonly fixedPartScaleY: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.js
new file mode 100644
index 000000000..5be3ea4cb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/NinePatch.js
@@ -0,0 +1,18 @@
+import Blitter from '../blitterbase/BlitterBase.js'
+import NinePatchBase from '../../../utils/ninepatch/NinePatch.js';
+import Methods from './Methods.js';
+
+class NinePatch extends NinePatchBase(Blitter, 'rexNinePatch2') {
+ setBaseTexture(key, baseFrameName, columns, rows) {
+ this.setTexture(key, baseFrameName);
+ super.setBaseTexture(key, baseFrameName, columns, rows);
+ return this;
+ }
+}
+
+Object.assign(
+ NinePatch.prototype,
+ Methods
+);
+
+export default NinePatch;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawImage.js
new file mode 100644
index 000000000..c011a417c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawImage.js
@@ -0,0 +1,13 @@
+import AddImage from '../../blitterbase/utils/AddImage.js';
+
+var DrawImage = function (key, frame, x, y, width, height) {
+ AddImage(this, {
+ frame: frame,
+ x: x,
+ y: y,
+ width: width,
+ height: height
+ })
+}
+
+export default DrawImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawTileSprite.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawTileSprite.js
new file mode 100644
index 000000000..9bb9094a0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/blitter/ninepatch/texture/DrawTileSprite.js
@@ -0,0 +1,24 @@
+import AddImage from '../../blitterbase/utils/AddImage.js';
+
+var DrawTileSprite = function (key, frame, x, y, width, height) {
+ var frameObj = this.texture.get(frame);
+ var frameWidth = frameObj.width,
+ frameHeight = frameObj.height;
+ var colCount = Math.floor(width / frameWidth),
+ rowCount = Math.floor(height / frameHeight);
+ // Align images at center
+ x += (width - (colCount * frameWidth)) / 2;
+ y += (height - (rowCount * frameHeight)) / 2;
+ for (var colIndex = 0; colIndex < colCount; colIndex++) {
+ for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+ AddImage(this, {
+ frame: frame,
+ x: x + (colIndex * frameWidth),
+ y: y + (rowIndex * frameHeight),
+ })
+ }
+ }
+
+}
+
+export default DrawTileSprite;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.d.ts
new file mode 100644
index 000000000..bf49ef8c4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.d.ts
@@ -0,0 +1,31 @@
+import Canvas from '../canvas/Canvas';
+
+export default AlphaMaskImage;
+
+declare namespace AlphaMaskImage {
+
+ interface IConfig {
+ mask: {
+ key: string,
+ frame?: string,
+ invertAlpha?: boolean,
+ scale?: number,
+ },
+
+ backgroundColor?: string,
+ }
+}
+
+declare class AlphaMaskImage extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ key?: string, frame?: string,
+ config?: AlphaMaskImage.IConfig
+ );
+
+ setTexture(
+ key?: string, frame?: string,
+ config?: AlphaMaskImage.IConfig
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.js
new file mode 100644
index 000000000..475bf20bf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/AlphaMaskImage.js
@@ -0,0 +1,102 @@
+import Canvas from '../canvasbase/Canvas.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class AlphaMaskImage extends Canvas {
+ constructor(scene, x, y, key, frame, config) {
+ super(scene, x, y);
+
+ this.type = 'rexAlphaMaskImage';
+ this.maskFrame = null;
+ this.setTexture(key, frame, config);
+ }
+
+ setTexture(key, frame, config) {
+ if (typeof (frame) === 'object') {
+ config = frame;
+ frame = undefined;
+ }
+
+ if (typeof (config) === 'string') {
+ config = {
+ mask: {
+ key: config
+ }
+ }
+ }
+
+ var maskKey = GetValue(config, 'mask.key');
+ var maskFrame = GetValue(config, 'mask.frame');
+ var invertMaskAlpha = GetValue(config, 'mask.invertAlpha', false);
+ var maskScale = GetValue(config, 'mask.scale');
+ var backgroundColor = GetValue(config, 'backgroundColor');
+
+ if (maskKey) {
+ this._maskKey = maskKey;
+ this._maskFrame = maskFrame;
+ this._maskScale = maskScale;
+
+ var texture = (maskKey) ? this.scene.sys.textures.get(maskKey) : null;
+ this.maskFrame = (texture) ? texture.get(maskFrame) : null;
+ }
+
+ this._textureKey = key;
+ this._frameName = frame;
+
+ var maskTextureFrame = this.maskFrame;
+ if (maskTextureFrame === null) {
+ this.loadTexture(key, frame);
+ this.dirty = true;
+ return this;
+ }
+
+ var hasBackgroundColor = (backgroundColor != null);
+ this.loadTexture(key, frame);
+
+ // Draw mask
+ var canvas = this.canvas,
+ ctx = this.context;
+ var width = canvas.width,
+ height = canvas.height;
+
+ ctx.save();
+ ctx.globalCompositeOperation = (invertMaskAlpha) ? 'destination-out' : 'destination-in';
+
+ var maskWidth, maskHeight;
+ if (this._maskScale != null) {
+ maskWidth = maskTextureFrame.cutWidth * this._maskScale;
+ maskHeight = maskTextureFrame.cutHeight * this._maskScale;
+ } else {
+ maskWidth = width;
+ maskHeight = height;
+ }
+ var maskX = (width - maskWidth) / 2;
+ var maskY = (height - maskHeight) / 2;
+
+ this.drawFrame(
+ this._maskKey, this._maskFrame,
+ maskX, maskY, maskWidth, maskHeight
+ );
+
+ ctx.restore();
+
+ if (hasBackgroundColor) {
+ ctx.save();
+ ctx.globalCompositeOperation = 'destination-over';
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+ }
+
+ this.dirty = true;
+ return this;
+ }
+
+ resize(width, height) {
+ // Don't draw content again.
+ this.setDisplaySize(width, height);
+ return this;
+ }
+}
+
+export default AlphaMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Creator.js
new file mode 100644
index 000000000..f6dfcfc79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Creator.js
@@ -0,0 +1,16 @@
+import AlphaMaskImage from './AlphaMaskImage.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var key = GetAdvancedValue(config, 'key', undefined);
+ var frame = GetAdvancedValue(config, 'frame', undefined);
+ var gameObject = new AlphaMaskImage(this.scene, 0, 0, key, frame, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Factory.js
new file mode 100644
index 000000000..ed7c507b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/alphamaskimage/Factory.js
@@ -0,0 +1,7 @@
+import AlphaMaskImage from './AlphaMaskImage.js';
+
+export default function (x, y, key, frame, config) {
+ var gameObject = new AlphaMaskImage(this.scene, x, y, key, frame, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.d.ts
new file mode 100644
index 000000000..79cf8a554
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.d.ts
@@ -0,0 +1,22 @@
+import CanvasBase from '../canvasbase/Canvas';
+
+export default class Canvas extends CanvasBase {
+ loadFromURL(
+ url: string,
+ callback?: () => void
+ ): this;
+
+ loadFromURLPromise(
+ url: string
+ ): Promise;
+
+ loadFromFile(
+ file: File,
+ callback?: () => void
+ ): this;
+
+ loadFromFilePromise(
+ file: File
+ ): Promise;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.js
new file mode 100644
index 000000000..ed1254996
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Canvas.js
@@ -0,0 +1,13 @@
+import CanvasBase from '../canvasbase/Canvas.js';
+import LoadImageMethods from './LoadImageMethods.js';
+
+class Canvas extends CanvasBase {
+
+}
+
+Object.assign(
+ Canvas.prototype,
+ LoadImageMethods,
+)
+
+export default Canvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Creator.js
new file mode 100644
index 000000000..9088365ba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Creator.js
@@ -0,0 +1,18 @@
+import Canvas from './Canvas.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', 256);
+ var height = GetAdvancedValue(config, 'height', width);
+ var gameObject = new Canvas(this.scene, 0, 0, width, height);
+ BuildGameObject(this.scene, gameObject, config);
+ var fillColor = GetAdvancedValue(config, 'fill', null);
+ gameObject.fill(fillColor);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Factory.js
new file mode 100644
index 000000000..7493058f9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/Factory.js
@@ -0,0 +1,7 @@
+import Canvas from './Canvas.js';
+
+export default function (x, y, width, height) {
+ var gameObject = new Canvas(this.scene, x, y, width, height);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/LoadImageMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/LoadImageMethods.js
new file mode 100644
index 000000000..9c0e58f1e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvas/LoadImageMethods.js
@@ -0,0 +1,51 @@
+export default {
+ loadFromURL(url, callback) {
+ var self = this;
+ var img = new Image();
+ img.onload = function () {
+ if ((self.width !== img.width) || (self.height !== img.height)) {
+ self.resize(img.width, img.height);
+ } else {
+ self.clear();
+ }
+ self.context.drawImage(img, 0, 0);
+ self.updateTexture();
+
+ if (callback) {
+ callback();
+ }
+
+ img.onload = null;
+ img.src = '';
+ img.remove();
+ }
+ img.src = url;
+ return this;
+ },
+
+ loadFromURLPromise(url) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.loadFromURL(url, resolve);
+ });
+ },
+
+ loadFromFile(file, callback) {
+ var url = URL.createObjectURL(file);
+ this.loadFromURL(url, function () {
+ URL.revokeObjectURL(url);
+ if (callback) {
+ callback();
+ }
+ })
+
+ return this;
+ },
+
+ loadFromFilePromise(file) {
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ self.loadFromFile(file, resolve);
+ });
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.d.ts
new file mode 100644
index 000000000..f28ad906c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.d.ts
@@ -0,0 +1,72 @@
+// import * as Phaser from 'phaser';
+import CanvasGameObjectBase from '../../../utils/types/CanvasGameObjectBase';
+
+export default class Canvas extends CanvasGameObjectBase {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ width?: number, height?: number
+ );
+
+ setSize(width: number, height: number): this;
+ resize(width: number, height: number): this;
+ setCanvasSize(width: number, height: number): this;
+
+ getCanvas(readOnly?: boolean): HTMLCanvasElement;
+ canvas: HTMLCanvasElement;
+ getContext(readOnly?: boolean): CanvasRenderingContext2D;
+ context: CanvasRenderingContext2D;
+
+ needRedraw(): this;
+ dirty: boolean;
+
+ clear(): this;
+ fill(color: string): this;
+
+ updateTexture(
+ callback?: (canvasElem: HTMLCanvasElement, context: CanvasRenderingContext2D) => void,
+ scope?: object
+ ): this;
+
+ generateTexture(
+ key: string | number,
+ x?: number, y?: number,
+ width?: number, height?: number
+ ): this;
+
+ loadTexture(
+ key: string,
+ frame?: string,
+ ): this;
+
+ drawFrame(
+ key: string,
+ frame?: string,
+ dx?: number,
+ dy?: number,
+ dWidth?: number,
+ dHeight?: number,
+ sxOffset?: number,
+ syOffset?: number,
+ sWidth?: number,
+ sHeight?: number,
+ ): this;
+
+ getDataURL(
+ type?: string,
+ encoderOptions?: number
+ ): string;
+
+ getPixel(
+ x: number, y: number
+ ): Phaser.Display.Color;
+
+ setPixel(
+ x: number, y: number,
+ r: number | Phaser.Display.Color,
+ g?: number,
+ b?: number,
+ a?: number
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.js
new file mode 100644
index 000000000..06814bbcf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/Canvas.js
@@ -0,0 +1,181 @@
+import Render from './render/Render.js';
+import CanvasMethods from './CanvasMethods.js';
+import TextureMethods from './TextureMethods.js';
+
+const CanvasPool = Phaser.Display.Canvas.CanvasPool;
+const GameObject = Phaser.GameObjects.GameObject;
+
+class Canvas extends GameObject {
+ constructor(scene, x, y, width, height) {
+ if (x === undefined) {
+ x = 0;
+ }
+ if (y === undefined) {
+ y = 0;
+ }
+ if (width === undefined) {
+ width = 1;
+ }
+ if (height === undefined) {
+ height = 1;
+ }
+
+ super(scene, 'rexCanvas');
+
+ this.renderer = scene.sys.game.renderer;
+
+ this.resolution = 1;
+ this._width = width;
+ this._height = height;
+ width = Math.max(Math.ceil(width * this.resolution), 1);
+ height = Math.max(Math.ceil(height * this.resolution), 1);
+ this.canvas = CanvasPool.create(this, width, height);
+ this.context = this.canvas.getContext('2d', { willReadFrequently: true });
+ this.dirty = false;
+
+ this.setPosition(x, y);
+ this.setOrigin(0.5, 0.5);
+ this.initPipeline();
+
+ this._crop = this.resetCropObject();
+
+ // Create a Texture for this Text object
+ this.texture = scene.sys.textures.addCanvas(null, this.canvas, true);
+
+ // Get the frame
+ this.frame = this.texture.get();
+
+ // Set the resolution
+ this.frame.source.resolution = this.resolution;
+
+ if (this.renderer && this.renderer.gl) {
+ // Clear the default 1x1 glTexture, as we override it later
+ this.renderer.deleteTexture(this.frame.source.glTexture);
+ this.frame.source.glTexture = null;
+ }
+
+ this.dirty = true;
+ }
+
+ preDestroy() {
+ CanvasPool.remove(this.canvas);
+ this.texture.destroy();
+
+ this.canvas = null;
+ this.context = null;
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ this.setSize(value, this._height);
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ this.setSize(this._width, value);
+ }
+
+ setCanvasSize(width, height) {
+ if ((this._width === width) && (this._height === height)) {
+ return this;
+ }
+
+ this._width = width;
+ this._height = height;
+
+ this.updateDisplayOrigin();
+
+ width = Math.max(Math.ceil(width * this.resolution), 1);
+ height = Math.max(Math.ceil(height * this.resolution), 1);
+ this.canvas.width = width;
+ this.canvas.height = height;
+
+ this.frame.setSize(width, height);
+
+ this.dirty = true;
+ return this;
+ }
+
+ // setSize might be override
+ setSize(width, height) {
+ this.setCanvasSize(width, height);
+ return this;
+ }
+
+ get displayWidth() {
+ return this.scaleX * this._width;
+ }
+
+ set displayWidth(value) {
+ this.scaleX = value / this._width;
+ }
+
+ get displayHeight() {
+ return this.scaleY * this._height;
+ }
+
+ set displayHeight(value) {
+ this.scaleY = value / this._height;
+ }
+
+ setDisplaySize(width, height) {
+ this.displayWidth = width;
+ this.displayHeight = height;
+ return this;
+ }
+
+ getCanvas(readOnly) {
+ if (!readOnly) {
+ this.dirty = true;
+ }
+ return this.canvas;
+ }
+
+ getContext(readOnly) {
+ if (!readOnly) {
+ this.dirty = true;
+ }
+ return this.context;
+ }
+
+ needRedraw() {
+ this.dirty = true;
+ return this;
+ }
+
+ resize(width, height) {
+ this.setSize(width, height);
+ return this;
+ }
+}
+
+const Components = Phaser.GameObjects.Components;
+Phaser.Class.mixin(Canvas,
+ [
+ Components.Alpha,
+ Components.BlendMode,
+ Components.Crop,
+ Components.Depth,
+ Components.Flip,
+ Components.GetBounds,
+ Components.Mask,
+ Components.Origin,
+ Components.Pipeline,
+ Components.PostPipeline,
+ Components.ScrollFactor,
+ Components.Tint,
+ Components.Transform,
+ Components.Visible,
+ Render,
+ CanvasMethods,
+ TextureMethods,
+ ]
+);
+
+export default Canvas;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/CanvasMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/CanvasMethods.js
new file mode 100644
index 000000000..23ca31efe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/CanvasMethods.js
@@ -0,0 +1,85 @@
+const Color = Phaser.Display.Color;
+
+export default {
+ clear() {
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ this.dirty = true;
+ return this;
+ },
+
+ fill(color) {
+ this.context.fillStyle = color;
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+ this.dirty = true;
+ return this;
+ },
+
+ drawFrame(key, frame, dx, dy, dWidth, dHeight, sxOffset, syOffset, sWidth, sHeight) {
+
+ var textureFrame = this.scene.sys.textures.getFrame(key, frame);
+ if (!textureFrame) {
+ return this;
+ }
+
+ var frameWidth = textureFrame.cutWidth,
+ frameHeight = textureFrame.cutHeight;
+
+ if (dx === undefined) { dx = 0; }
+ if (dy === undefined) { dy = 0; }
+ if (dWidth === undefined) { dWidth = frameWidth; }
+ if (dHeight === undefined) { dHeight = frameHeight; }
+ if (sxOffset === undefined) { sxOffset = 0; }
+ if (syOffset === undefined) { syOffset = 0; }
+ if (sWidth === undefined) { sWidth = frameWidth; }
+ if (sHeight === undefined) { sHeight = frameHeight; }
+
+ var sx = textureFrame.cutX + sxOffset;
+ var sy = textureFrame.cutY + syOffset;
+
+ this.context.drawImage(
+ textureFrame.source.image, // image
+ sx, sy, sWidth, sHeight,
+ dx, dy, dWidth, dHeight
+ );
+
+ this.dirty = true;
+
+ return this;
+ },
+
+ getDataURL(type, encoderOptions) {
+ return this.canvas.toDataURL(type, encoderOptions);
+ },
+
+ getPixel(x, y, out) {
+ if (out === undefined) {
+ out = new Color();
+ }
+ var rgb = this.context.getImageData(x, y, 1, 1);
+ out.setTo(rgb.data[0], rgb.data[1], rgb.data[2], rgb.data[3])
+ return out;
+ },
+
+ setPixel(x, y, r, g, b, a) {
+ if (typeof (r) !== 'number') {
+ var color = r;
+ r = color.red;
+ g = color.green;
+ b = color.blue;
+ a = color.alpha;
+ }
+
+ if (a === undefined) {
+ a = ((r !== 0) || (g !== 0) || (b !== 0)) ? 255 : 0;
+ }
+
+ var imgData = this.context.createImageData(1, 1);
+ imgData.data[0] = r;
+ imgData.data[1] = g;
+ imgData.data[2] = b;
+ imgData.data[3] = a;
+ this.context.putImageData(imgData, x, y);
+ this.dirty = true;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/TextureMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/TextureMethods.js
new file mode 100644
index 000000000..17b787884
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/TextureMethods.js
@@ -0,0 +1,65 @@
+import CopyCanvasToTexture from '../../../utils/texture/CopyCanvasToTexture.js';
+
+export default {
+ updateTexture(callback, scope) {
+ if (callback) {
+ if (scope) {
+ callback.call(scope, this.canvas, this.context);
+ } else {
+ callback(this.canvas, this.context);
+ }
+ }
+
+ if ((this.canvas.width !== this.frame.width) || (this.canvas.height !== this.frame.height)) {
+ this.frame.setSize(this.canvas.width, this.canvas.height);
+ }
+ if (this.renderer && this.renderer.gl) {
+ this.frame.source.glTexture = this.renderer.canvasToTexture(this.canvas, this.frame.source.glTexture, true);
+ this.frame.glTexture = this.frame.source.glTexture;
+ }
+ this.dirty = false;
+
+ var input = this.input;
+ if (input && !input.customHitArea) {
+ input.hitArea.width = this.width;
+ input.hitArea.height = this.height;
+ }
+ return this;
+ },
+
+ generateTexture(key, x, y, width, height) {
+ var srcCanvas = this.canvas;
+ if (width === undefined) {
+ width = srcCanvas.width;
+ } else {
+ width *= this.resolution;
+ }
+ if (height === undefined) {
+ height = srcCanvas.height;
+ } else {
+ height *= this.resolution;
+ }
+
+ CopyCanvasToTexture(this.scene, srcCanvas, key, x, y, width, height);
+
+ return this;
+ },
+
+ loadTexture(key, frame) {
+ var textureFrame = this.scene.sys.textures.getFrame(key, frame);
+ if (!textureFrame) {
+ return this;
+ }
+
+ if ((this.width !== textureFrame.cutWidth) || (this.height !== textureFrame.cutHeight)) {
+ this.setSize(textureFrame.cutWidth, textureFrame.cutHeight);
+ } else {
+ this.clear();
+ }
+
+ this.drawFrame(key, frame);
+ this.dirty = true;
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/CanvasRenderer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/CanvasRenderer.js
new file mode 100644
index 000000000..6999ed8d8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/CanvasRenderer.js
@@ -0,0 +1,18 @@
+// copy from Phaser.GameObjects.Text
+
+var CanvasRenderer = function (renderer, src, camera, parentMatrix) {
+ if (src.dirty) {
+ src.updateTexture();
+ src.dirty = false;
+ }
+
+ if ((src.width === 0) || (src.height === 0)) {
+ return;
+ }
+
+ camera.addToRenderList(src);
+
+ renderer.batchSprite(src, src.frame, camera, parentMatrix);
+};
+
+export default CanvasRenderer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/Render.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/Render.js
new file mode 100644
index 000000000..404fc7750
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/Render.js
@@ -0,0 +1,8 @@
+import WebGLRenderer from './WebGLRenderer.js';
+import CanvasRenderer from './CanvasRenderer.js';
+
+export default {
+ renderWebGL: WebGLRenderer,
+ renderCanvas: CanvasRenderer
+
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/WebGLRenderer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/WebGLRenderer.js
new file mode 100644
index 000000000..e49f9ca5e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/canvasbase/render/WebGLRenderer.js
@@ -0,0 +1,53 @@
+// copy from Phaser.GameObjects.Text
+
+const Utils = Phaser.Renderer.WebGL.Utils;
+
+var WebGLRenderer = function (renderer, src, camera, parentMatrix) {
+ if (src.dirty) {
+ src.updateTexture();
+ src.dirty = false;
+ }
+
+ if ((src.width === 0) || (src.height === 0)) {
+ return;
+ }
+
+ camera.addToRenderList(src);
+
+ var frame = src.frame;
+ var width = frame.width;
+ var height = frame.height;
+ var getTint = Utils.getTintAppendFloatAlpha;
+ var pipeline = renderer.pipelines.set(src.pipeline, src);
+ var textureUnit = pipeline.setTexture2D(frame.glTexture, src);
+
+ renderer.pipelines.preBatch(src);
+
+ pipeline.batchTexture(
+ src,
+ frame.glTexture,
+ width, height,
+ src.x, src.y,
+ width / src.resolution, height / src.resolution,
+ src.scaleX, src.scaleY,
+ src.rotation,
+ src.flipX, src.flipY,
+ src.scrollFactorX, src.scrollFactorY,
+ src.displayOriginX, src.displayOriginY,
+ 0, 0, width, height,
+ getTint(src.tintTopLeft, camera.alpha * src._alphaTL),
+ getTint(src.tintTopRight, camera.alpha * src._alphaTR),
+ getTint(src.tintBottomLeft, camera.alpha * src._alphaBL),
+ getTint(src.tintBottomRight, camera.alpha * src._alphaBR),
+ src.tintFill,
+ 0, 0,
+ camera,
+ parentMatrix,
+ false,
+ textureUnit
+ );
+
+ renderer.pipelines.postBatch(src);
+};
+
+export default WebGLRenderer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.d.ts
new file mode 100644
index 000000000..0598d5dc5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.d.ts
@@ -0,0 +1,41 @@
+import Canvas from '../canvas/Canvas';
+
+export default CircleMaskImage;
+
+declare namespace CircleMaskImage {
+
+ interface IConfig {
+ maskType?: null | 0 | 1 | 2 | 'circle' | 'ellipse' | 'roundRectangle',
+ radius?: number |
+ { x?: number, y?: number } |
+ {
+ tl?: number | { x?: number, y?: number },
+ tr?: number | { x?: number, y?: number },
+ bl?: number | { x?: number, y?: number },
+ br?: number | { x?: number, y?: number }
+ },
+
+ backgroundColor?: string,
+
+ strokeColor?: string,
+ strokeLineWidth?: number,
+ }
+}
+
+declare class CircleMaskImage extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ key?: string, frame?: string,
+ config?:
+ null | 0 | 1 | 2 | 'circle' | 'ellipse' | 'roundRectangle' |
+ CircleMaskImage.IConfig
+ );
+
+ setTexture(
+ key?: string, frame?: string,
+ config?:
+ null | 0 | 1 | 2 | 'circle' | 'ellipse' | 'roundRectangle' |
+ CircleMaskImage.IConfig
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.js
new file mode 100644
index 000000000..5d8f85900
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/CircleMaskImage.js
@@ -0,0 +1,134 @@
+import Canvas from '../canvasbase/Canvas.js';
+import AddRoundRectanglePath from '../../../utils/canvas/AddRoundRectanglePath.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class CircleMaskImage extends Canvas {
+ constructor(scene, x, y, key, frame, config) {
+ super(scene, x, y);
+
+ this.type = 'rexCircleMaskImage';
+ this.setTexture(key, frame, config);
+ }
+
+ setTexture(key, frame, config) {
+ if (typeof (frame) === 'object') {
+ config = frame;
+ frame = undefined;
+ }
+
+ if (typeof (config) === 'string') {
+ config = {
+ maskType: config
+ };
+ }
+
+ var maskType = GetValue(config, 'maskType', 0);
+ var backgroundColor = GetValue(config, 'backgroundColor', undefined);
+ var strokeColor = GetValue(config, 'strokeColor', undefined);
+
+ var defaultStrokeWidth = (strokeColor != null) ? 10 : 0;
+ var strokeWidth = GetValue(config, 'strokeWidth', defaultStrokeWidth);
+
+ if (maskType === undefined) {
+ maskType = 0;
+ } else if (typeof (maskType) === 'string') {
+ maskType = MASKTYPE[maskType];
+ }
+
+ this._textureKey = key;
+ this._frameName = frame;
+
+ if (maskType === null) {
+ this.loadTexture(key, frame);
+ this.dirty = true;
+ return this;
+ }
+
+ var textureFrame = this.scene.sys.textures.getFrame(key, frame);
+ if (!textureFrame) {
+ return this;
+ }
+ // Resize to frame size
+ if ((textureFrame.cutWidth !== this.width) || (textureFrame.cutHeight !== this.height)) {
+ this.setCanvasSize(textureFrame.cutWidth, textureFrame.cutHeight);
+ } else {
+ this.clear();
+ }
+
+ var canvas = this.canvas,
+ ctx = this.context;
+ var width = canvas.width,
+ height = canvas.height;
+
+ // Fill background
+ if (backgroundColor != null) {
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, width, height);
+ }
+
+ ctx.save();
+ ctx.beginPath();
+
+ // Build clip path
+ var halfStrokeLineWidth = strokeWidth / 2;
+ switch (maskType) {
+ case 1: // ellipse
+ var centerX = Math.floor(width / 2);
+ var centerY = Math.floor(height / 2);
+ var radiusX = centerX - halfStrokeLineWidth;
+ var radiusY = centerY - halfStrokeLineWidth;
+ ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, (2 * Math.PI));
+ break;
+
+ case 2:
+ var radiusConfig = GetValue(config, 'radius', 0);
+ var iteration = GetValue(config, 'iteration', undefined);
+
+ AddRoundRectanglePath(
+ ctx,
+ halfStrokeLineWidth, halfStrokeLineWidth,
+ width - strokeWidth, height - strokeWidth,
+ radiusConfig,
+ iteration
+ );
+ break;
+
+ default: // circle
+ var centerX = Math.floor(width / 2);
+ var centerY = Math.floor(height / 2);
+ var radius = Math.min(centerX, centerY) - halfStrokeLineWidth;
+ ctx.arc(centerX, centerY, radius, 0, (2 * Math.PI));
+ break;
+ }
+
+ // Draw stroke line
+ if (strokeColor != null) {
+ ctx.strokeStyle = strokeColor;
+ ctx.lineWidth = strokeWidth;
+ ctx.stroke();
+ }
+
+ // Clip frame image
+ ctx.clip();
+ this.loadTexture(key, frame);
+ ctx.restore();
+
+ this.dirty = true;
+ return this;
+ }
+
+ resize(width, height) {
+ // Don't draw content again.
+ this.setDisplaySize(width, height);
+ return this;
+ }
+}
+
+const MASKTYPE = {
+ circle: 0,
+ ellipse: 1,
+ roundRectangle: 2
+}
+
+export default CircleMaskImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Creator.js
new file mode 100644
index 000000000..795410967
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Creator.js
@@ -0,0 +1,16 @@
+import CircleMaskImage from './CircleMaskImage.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var key = GetAdvancedValue(config, 'key', undefined);
+ var frame = GetAdvancedValue(config, 'frame', undefined);
+ var gameObject = new CircleMaskImage(this.scene, 0, 0, key, frame, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Factory.js
new file mode 100644
index 000000000..166e88177
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circlemaskimage/Factory.js
@@ -0,0 +1,7 @@
+import CircleMaskImage from './CircleMaskImage.js';
+
+export default function (x, y, key, frame, config) {
+ var gameObject = new CircleMaskImage(this.scene, x, y, key, frame, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.d.ts
new file mode 100644
index 000000000..42e60da44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.d.ts
@@ -0,0 +1,114 @@
+import Canvas from '../canvas/Canvas';
+
+export default CircularProgressCanvas;
+
+declare namespace CircularProgressCanvas {
+
+ type ValueChangeCallbackType = (
+ newValue: number,
+ oldValue: number,
+ circularProgress: CircularProgressCanvas
+ ) => void;
+
+
+ interface IConfig {
+ x?: number, y?: number,
+ radius?: number,
+
+ barColor?: string | number,
+ trackColor?: string | number,
+ centerColor?: string | number,
+ thickness?: number,
+ startAngle?: number,
+ anticlockwise?: boolean,
+
+ textColor?: string | number,
+ textStrokeColor?: string | number,
+ textStrokeThickness?: number,
+ textSize?: string,
+ textFamily?: string,
+ textStyle?: string,
+ textFormatCallback?: (value: number) => string,
+ textFormatCallbackScope?: object,
+
+ value?: number,
+
+ easeValue?: {
+ duration?: number,
+ ease?: string
+ },
+
+ valuechangeCallback: ValueChangeCallbackType,
+
+ }
+
+ namespace Events {
+ type ValueChangeCallbackType = (
+ newValue: number,
+ oldValue: number,
+ circularProgress: CircularProgressCanvas
+ ) => void;
+ }
+}
+
+declare class CircularProgressCanvas extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ config?: CircularProgressCanvas.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ radius?: number,
+ barColor?: string | number,
+ value?: number,
+ config?: CircularProgressCanvas.IConfig
+ );
+
+ value: number;
+ getValue(min?: number, max?: number): number;
+ setValue(value?: number, min?: number, max?: number): this;
+ addValue(inc?: number, min?: number, max?: number): this;
+
+ easeValueTo(value?: number, min?: number, max?: number): this;
+ stopEaseValue(): this;
+ setEaseValueDuration(duration: number): this;
+ setEaseValueFunction(ease: string): this;
+
+ radius: number;
+ setRadius(radius: number): this;
+
+ trackColor: string;
+ setTrackColor(trackColor?: string | number): this;
+
+ setThickness(thickness: number): this;
+
+ barColor: string;
+ setBarColor(barColor?: string | number): this;
+
+ startAngle: number;
+ setStartAngle(startAngle: number): this;
+
+ anticlockwise: boolean;
+ setAnticlockwise(anticlockwise: boolean): this;
+
+ centerColor: string;
+ setCenterColor(centerColor?: string | number): this;
+
+ textColor: string;
+ setTextColor(color?: string | number): this;
+
+ textStrokeColor: string;
+ textStrokeThickness: number;
+ setTextStrokeColor(color?: string | number, thickness?: number): this;
+
+ textFont: string;
+ setTextFont(fontSize: string, fontFamily: string, fontStyle: string): this;
+ setTextFont(font: string): this;
+
+ setTextFormatCallback(
+ callback: (value: number) => string,
+ scope?: object
+ ): this
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.js
new file mode 100644
index 000000000..2240f27c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/CircularProgress.js
@@ -0,0 +1,281 @@
+import Canvas from '../canvasbase/Canvas.js';
+import ProgressBase from '../../../utils/progressbase/ProgressBase.js';
+import GetStyle from '../../../utils/canvas/GetStyle.js';
+import DrawContent from './DrawContent.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const Clamp = Phaser.Math.Clamp;
+
+const DefaultStartAngle = Phaser.Math.DegToRad(270);
+
+class CircularProgress extends ProgressBase(Canvas) {
+ constructor(scene, x, y, radius, barColor, value, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ radius = GetValue(config, 'radius', 1);
+ barColor = GetValue(config, 'barColor', undefined);
+ value = GetValue(config, 'value', 0);
+ }
+ var width = radius * 2;
+ super(scene, x, y, width, width);
+ this.type = 'rexCircularProgressCanvas';
+
+ this.bootProgressBase(config);
+
+ this.setRadius(radius);
+ this.setTrackColor(GetValue(config, 'trackColor', undefined));
+ this.setBarColor(barColor);
+ this.setCenterColor(GetValue(config, 'centerColor', undefined));
+
+ this.setThickness(GetValue(config, 'thickness', 0.2));
+ this.setStartAngle(GetValue(config, 'startAngle', DefaultStartAngle));
+ this.setAnticlockwise(GetValue(config, 'anticlockwise', false));
+
+ this.setTextColor(GetValue(config, 'textColor', undefined));
+ this.setTextStrokeColor(
+ GetValue(config, 'textStrokeColor', undefined),
+ GetValue(config, 'textStrokeThickness', undefined)
+ );
+
+ var textFont = GetValue(config, 'textFont', undefined);
+ if (textFont) {
+ this.setTextFont(textFont);
+ } else {
+ this.setTextFont(
+ GetValue(config, 'textSize', '16px'),
+ GetValue(config, 'textFamily', 'Courier'),
+ GetValue(config, 'textStyle', '')
+ );
+ }
+ this.setTextFormatCallback(
+ GetValue(config, 'textFormatCallback', undefined),
+ GetValue(config, 'textFormatCallbackScope', undefined)
+ );
+
+ this.setValue(value);
+ }
+
+ resize(width, height) {
+ width = Math.floor(Math.min(width, height));
+ if (width === this.width) {
+ return this;
+ }
+
+ super.resize(width, width);
+ this.setRadius(width / 2);
+ return this;
+ }
+
+ get radius() {
+ return this._radius;
+ }
+
+ set radius(value) {
+ this.dirty = this.dirty || (this._radius != value);
+ this._radius = value;
+ var width = value * 2;
+ this.resize(width, width);
+ }
+
+ setRadius(radius) {
+ this.radius = radius;
+ return this;
+ }
+
+ get trackColor() {
+ return this._trackColor;
+ }
+
+ set trackColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._trackColor != value);
+ this._trackColor = value;
+ }
+
+ setTrackColor(color) {
+ this.trackColor = color;
+ return this;
+ }
+
+ get barColor() {
+ return this._barColor;
+ }
+
+ set barColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._barColor != value);
+ this._barColor = value;
+ }
+
+ setBarColor(color) {
+ this.barColor = color;
+ return this;
+ }
+
+ get startAngle() {
+ return this._startAngle;
+ }
+
+ set startAngle(value) {
+ this.dirty = this.dirty || (this._startAngle != value);
+ this._startAngle = value;
+ }
+
+ setStartAngle(angle) {
+ this.startAngle = angle;
+ return this;
+ }
+
+ get anticlockwise() {
+ return this._anticlockwise;
+ }
+
+ set anticlockwise(value) {
+ this.dirty = this.dirty || (this._anticlockwise != value);
+ this._anticlockwise = value;
+ }
+
+ setAnticlockwise(anticlockwise) {
+ if (anticlockwise === undefined) {
+ anticlockwise = true;
+ }
+ this.anticlockwise = anticlockwise;
+ return this;
+ }
+
+ get thickness() {
+ return this._thickness;
+ }
+
+ set thickness(value) {
+ value = Clamp(value, 0, 1);
+ this.dirty = this.dirty || (this._thickness != value);
+ this._thickness = value;
+ }
+
+ setThickness(thickness) {
+ this.thickness = thickness;
+ return this;
+ }
+
+ get centerColor() {
+ return this._centerColor;
+ }
+
+ set centerColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._centerColor != value);
+ this._centerColor = value;
+ }
+
+ get centerColor2() {
+ return this._centerColor2;
+ }
+
+ set centerColor2(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._centerColor2 != value);
+ this._centerColor2 = value;
+ }
+
+ setCenterColor(color, color2) {
+ this.centerColor = color;
+ this.centerColor2 = color2;
+ return this;
+ }
+
+ get textColor() {
+ return this._textColor;
+ }
+
+ set textColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._textColor != value);
+ this._textColor = value;
+ }
+
+ setTextColor(color) {
+ this.textColor = color;
+ return this;
+ }
+
+ get textStrokeColor() {
+ return this._textStrokeColor;
+ }
+
+ set textStrokeColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._textStrokeColor != value);
+ this._textStrokeColor = value;
+ }
+
+ get textStrokeThickness() {
+ return this._textStrokeThickness;
+ }
+
+ set textStrokeThickness(value) {
+ this.dirty = this.dirty || (this._textStrokeThickness != value);
+ this._textStrokeThickness = value;
+ }
+
+ setTextStrokeColor(color, thickness) {
+ if (thickness === undefined) {
+ thickness = 2;
+ }
+ this.textStrokeColor = color;
+ this.textStrokeThickness = thickness;
+ return this;
+ }
+
+ get textFont() {
+ return this._textFont;
+ }
+
+ set textFont(value) {
+ this.dirty = this.dirty || (this._textFont != value);
+ this._textFont = value;
+ }
+
+ setTextFont(fontSize, fontFamily, fontStyle) {
+ var font;
+ if (fontFamily === undefined) {
+ font = fontSize;
+ } else {
+ font = fontStyle + ' ' + fontSize + ' ' + fontFamily;
+ }
+ this.textFont = font;
+ return this;
+ }
+
+ setTextFormatCallback(callback, scope) {
+ this.textFormatCallback = callback;
+ this.textFormatCallbackScope = scope;
+ return this;
+ }
+
+ updateTexture() {
+ this.clear();
+ DrawContent.call(this);
+ super.updateTexture();
+ return this;
+ }
+
+ getFormatText(value) {
+ if (value === undefined) {
+ value = this.value;
+ }
+
+ var text;
+ if (this.textFormatCallbackScope) {
+ text = this.textFormatCallback(value);
+ } else {
+ text = this.textFormatCallback.call(this.textFormatCallbackScope, value);
+ }
+ return text;
+ }
+}
+
+export default CircularProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Creator.js
new file mode 100644
index 000000000..ddb2252eb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Creator.js
@@ -0,0 +1,13 @@
+import CircularProgress from './CircularProgress.js';
+
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var gameObject = new CircularProgress(this.scene, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/DrawContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/DrawContent.js
new file mode 100644
index 000000000..f76b323ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/DrawContent.js
@@ -0,0 +1,99 @@
+import DrawCicle from '../../../utils/canvas/DrawCircle.js';
+import DrawText from '../../../utils/canvas/DrawText.js';
+
+
+var DrawContent = function () {
+ var x = this.radius;
+ var lineWidth = this.thickness * this.radius;
+ var barRadius = this.radius - (lineWidth / 2);
+ var centerRadius = this.radius - lineWidth;
+ var canvas = this.canvas,
+ context = this.context;
+
+ // Draw track
+ if (this.trackColor && (lineWidth > 0)) {
+ context.save();
+
+ DrawCicle(
+ canvas, context,
+ x, x,
+ barRadius, barRadius,
+ undefined,
+ this.trackColor,
+ lineWidth
+ );
+
+ context.restore();
+ }
+
+ // Draw bar
+ if ((this.barColor) && (barRadius > 0)) {
+ var anticlockwise, startAngle, endAngle;
+ if (this.value === 1) {
+ anticlockwise = false;
+ startAngle = 0;
+ endAngle = 2 * Math.PI;
+ } else {
+ anticlockwise = this.anticlockwise;
+ startAngle = this.startAngle;
+ var deltaAngle = 2 * Math.PI * ((anticlockwise) ? (1 - this.value) : this.value);
+ endAngle = deltaAngle + startAngle;
+ }
+
+ context.save();
+
+ DrawCicle(
+ canvas, context,
+ x, x,
+ barRadius, barRadius,
+ undefined,
+ this.barColor,
+ lineWidth,
+ startAngle, endAngle, anticlockwise
+ );
+
+ context.restore();
+ }
+
+ // Draw center
+ if (this.centerColor && (centerRadius > 0)) {
+ var fillStyle;
+ if (this.centerColor2) {
+ fillStyle = this.context.createRadialGradient(x, x, 0, x, x, centerRadius);
+ fillStyle.addColorStop(0, this.centerColor);
+ fillStyle.addColorStop(1, this.centerColor2);
+ } else {
+ fillStyle = this.centerColor;
+ }
+
+ context.save();
+
+ DrawCicle(
+ canvas, context,
+ x, x,
+ centerRadius, centerRadius,
+ fillStyle
+ );
+
+ context.restore();
+ }
+
+ // Draw text
+ if (this.textFormatCallback && (this.textColor || this.textStrokeColor)) {
+
+ context.save();
+
+ DrawText(
+ canvas, context,
+ x, x,
+ this.getFormatText(), this.textFont,
+ this.textColor, this.textStrokeColor, this.textStrokeThickness,
+ 'center', // textAlign
+ 'middle' // textBaseline
+ )
+
+ context.restore();
+ }
+}
+
+export default DrawContent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Factory.js
new file mode 100644
index 000000000..15c3a0717
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/circularprogress/Factory.js
@@ -0,0 +1,7 @@
+import CircularProgress from './CircularProgress.js';
+
+export default function (x, y, radius, barColor, value, config) {
+ var gameObject = new CircularProgress(this.scene, x, y, radius, barColor, value, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Creator.js
new file mode 100644
index 000000000..c1555fb7c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Creator.js
@@ -0,0 +1,13 @@
+import LineProgress from './LineProgress.js';
+
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var gameObject = new LineProgress(this.scene, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/DrawContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/DrawContent.js
new file mode 100644
index 000000000..f6e3db92c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/DrawContent.js
@@ -0,0 +1,144 @@
+import DrawPolygon from '../../../utils/canvas/DrawPolygon.js';
+
+var DrawContent = function () {
+ var skewX = this.skewX;
+ var width = this.width - Math.abs(skewX);
+ var height = this.height;
+ var canvas = this.canvas,
+ context = this.context;
+
+ // Has track
+ if (this.trackColor || this.trackStrokeColor) {
+ BuildPolygon(
+ 0, 0, // x0, y0
+ width, height, // x1, y1
+ skewX, // skewX
+ this.trackPoints
+ )
+ }
+
+ // Has bar
+ var barX0, barX1;
+ if (this.barColor) {
+ if (!this.rtl) {
+ barX0 = 0;
+ barX1 = width * this.value;
+ } else {
+ barX0 = width * (1 - this.value);
+ barX1 = width;
+ }
+
+ BuildPolygon(
+ barX0, 0, // x0, y0
+ barX1, height, // x1, y1
+ skewX, // skewX
+ this.barPoints
+ )
+ }
+
+ if (this.trackColor) {
+ context.save();
+
+ DrawPolygon(
+ canvas, context,
+ this.trackPoints,
+ this.trackColor,
+ )
+
+ context.restore();
+ }
+
+ if (this.barColor) {
+ context.save();
+
+ var style;
+ if (this.barColor2) {
+ var grd;
+ if (this.isHorizontalGradient) {
+ var helfHeight = height / 2;
+ grd = context.createLinearGradient(barX0, helfHeight, barX1, helfHeight);
+ } else {
+ var helfWidth = width / 2;
+ grd = context.createLinearGradient(helfWidth, 0, helfWidth, height);
+ }
+ grd.addColorStop(0, (this.rtl) ? this.barColor : this.barColor2);
+ grd.addColorStop(1, (this.rtl) ? this.barColor2 : this.barColor);
+ style = grd;
+ } else {
+ style = this.barColor;
+ }
+
+ DrawPolygon(
+ canvas, context,
+ this.barPoints,
+ style,
+ )
+
+ context.restore();
+ }
+
+ if (this.trackStrokeColor && this.trackStrokeThickness > 0) {
+ context.save();
+
+ DrawPolygon(
+ canvas, context,
+ this.trackPoints,
+ undefined,
+ this.trackStrokeColor, this.trackStrokeThickness,
+ )
+
+ context.restore();
+ }
+}
+
+var BuildPolygon = function (x0, y0, x1, y1, skewX, out) {
+ if (out === undefined) {
+ out = [];
+ }
+ out.length = 4;
+
+ for (var i = 0; i < 4; i++) {
+ if (!out[i]) {
+ out[i] = {};
+ }
+ }
+
+ var p;
+ if (skewX >= 0) {
+ p = out[0];
+ p.x = x0 + skewX;
+ p.y = y0;
+
+ p = out[1];
+ p.x = x1 + skewX;
+ p.y = y0;
+
+ p = out[2];
+ p.x = x1;
+ p.y = y1;
+
+ p = out[3];
+ p.x = x0;
+ p.y = y1;
+ } else {
+ p = out[0];
+ p.x = x0;
+ p.y = y0;
+
+ p = out[1];
+ p.x = x1;
+ p.y = y0;
+
+ p = out[2];
+ p.x = x1 - skewX;
+ p.y = y1;
+
+ p = out[3];
+ p.x = x0 - skewX;
+ p.y = y1;
+ }
+
+ return out;
+}
+
+export default DrawContent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Factory.js
new file mode 100644
index 000000000..084a4206d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/Factory.js
@@ -0,0 +1,7 @@
+import LineProgress from './LineProgress.js';
+
+export default function (x, y, width, height, barColor, value, config) {
+ var gameObject = new LineProgress(this.scene, x, y, width, height, barColor, value, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.d.ts
new file mode 100644
index 000000000..4cafcdb67
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.d.ts
@@ -0,0 +1,104 @@
+import Canvas from '../canvas/Canvas';
+
+// import * as Phaser from 'phaser';
+export default LineProgress;
+
+declare namespace LineProgress {
+
+ type ValueChangeCallbackType = (
+ newValue: number,
+ oldValue: number,
+ circularProgress: LineProgress
+ ) => void;
+
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+
+ trackColor?: string | number,
+ trackThickness?: number,
+ trackStrokeColor?: string | number,
+ barColor?: string | number,
+ barColor2?: string | number,
+ isHorizontalGradient?: boolean,
+
+ skewX?: number,
+
+ rtl?: boolean,
+
+ value?: number,
+
+ easeValue?: {
+ duration?: number,
+ ease?: string
+ },
+
+ valuechangeCallback: ValueChangeCallbackType,
+ }
+
+ namespace Events {
+ type ValueChangeCallbackType = (
+ newValue: number,
+ oldValue: number,
+ circularProgress: LineProgress
+ ) => void;
+ }
+}
+
+declare class LineProgress extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ config?: LineProgress.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ width?: number, height?: number,
+ config?: LineProgress.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ width?: number, height?: number,
+ barColor?: string | number,
+ value?: number,
+ config?: LineProgress.IConfig
+ );
+
+ value: number;
+ getValue(min?: number, max?: number): number;
+ setValue(value?: number, min?: number, max?: number): this;
+ addValue(inc?: number, min?: number, max?: number): this;
+
+ easeValueTo(value?: number, min?: number, max?: number): this;
+ stopEaseValue(): this;
+ setEaseValueDuration(duration: number): this;
+ setEaseValueFunction(ease: string): this;
+
+ trackColor: string;
+ setTrackColor(radius?: string | number): this;
+
+ trackStrokeThickness: number;
+ trackStrokeColor: string;
+ setTrackStroke(
+ lineWidth?: number,
+ color?: string | number
+ ): this;
+
+ barColor: string;
+ barColor2: string;
+ isHorizontalGradient: boolean;
+ setBarColor(
+ barColor?: string | number,
+ barColor2?: string | number,
+ isHorizontalGradient?: boolean,
+ ): this;
+
+ skewX: number;
+ setSkewX(skewX: number): this;
+
+ rtl: boolean;
+ setRTL(enable?: boolean): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.js
new file mode 100644
index 000000000..7774a57de
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/lineprogress/LineProgress.js
@@ -0,0 +1,172 @@
+import Canvas from '../canvasbase/Canvas.js';
+import ProgressBase from '../../../utils/progressbase/ProgressBase.js';
+import GetStyle from '../../../utils/canvas/GetStyle.js';
+import DrawContent from './DrawContent.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+
+class LineProgress extends ProgressBase(Canvas) {
+ constructor(scene, x, y, width, height, barColor, value, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ width = GetValue(config, 'width', 2);
+ height = GetValue(config, 'height', 2);
+ barColor = GetValue(config, 'barColor', undefined);
+ value = GetValue(config, 'value', 0);
+ } else if (IsPlainObject(width)) {
+ config = width;
+ width = GetValue(config, 'width', 2);
+ height = GetValue(config, 'height', 2);
+ barColor = GetValue(config, 'barColor', undefined);
+ value = GetValue(config, 'value', 0);
+ } else if (IsPlainObject(barColor)) {
+ config = barColor;
+ barColor = GetValue(config, 'barColor', undefined);
+ value = GetValue(config, 'value', 0);
+ }
+ super(scene, x, y, width, height);
+ this.type = 'rexLineProgressCanvas';
+ this.trackPoints = [];
+ this.barPoints = [];
+
+ this.bootProgressBase(config);
+
+ this.setTrackColor(GetValue(config, 'trackColor', undefined));
+ this.setBarColor(
+ barColor,
+ GetValue(config, 'barColor2', undefined),
+ GetValue(config, 'isHorizontalGradient', undefined)
+ );
+ this.setTrackStroke(GetValue(config, 'trackStrokeThickness', 2), GetValue(config, 'trackStrokeColor', undefined));
+
+ this.setSkewX(GetValue(config, 'skewX', 0));
+
+ this.setRTL(GetValue(config, 'rtl', false));
+
+ this.setValue(value);
+ }
+
+ get trackColor() {
+ return this._trackColor;
+ }
+
+ set trackColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._trackColor != value);
+ this._trackColor = value;
+ }
+
+ setTrackColor(color) {
+ this.trackColor = color;
+ return this;
+ }
+
+ get trackStrokeColor() {
+ return this._trackStrokeColor;
+ }
+
+ set trackStrokeColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._trackStrokeColor != value);
+ this._trackStrokeColor = value;
+ }
+
+ get trackStrokeThickness() {
+ return this._trackStrokeThickness;
+ }
+
+ set trackStrokeThickness(value) {
+ this.dirty = this.dirty || (this._trackStrokeThickness != value);
+ this._trackStrokeThickness = value;
+ }
+
+ setTrackStroke(lineWidth, color) {
+ this.trackStrokeThickness = lineWidth;
+ this.trackStrokeColor = color;
+ return this;
+ }
+
+ get barColor() {
+ return this._barColor;
+ }
+
+ set barColor(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._barColor != value);
+ this._barColor = value;
+ }
+
+ get barColor2() {
+ return this._barColor2;
+ }
+
+ set barColor2(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty = this.dirty || (this._barColor2 != value);
+ this._barColor2 = value;
+ }
+
+ get isHorizontalGradient() {
+ return this._isHorizontalGradient;
+ }
+
+ set isHorizontalGradient(value) {
+ this.dirty |= (this._isHorizontalGradient != value);
+ this._isHorizontalGradient = value;
+ }
+
+ setBarColor(color, color2, isHorizontalGradient) {
+ if (isHorizontalGradient === undefined) {
+ isHorizontalGradient = true;
+ }
+
+ this.barColor = color;
+ this.barColor2 = color2;
+ this.isHorizontalGradient = isHorizontalGradient;
+ return this;
+ }
+
+ get skewX() {
+ return this._skewX;
+ }
+
+ set skewX(value) {
+ this.dirty = this.dirty || (this._skewX != value);
+ this._skewX = value;
+ }
+
+ setSkewX(value) {
+ this.skewX = value;
+ return this;
+ }
+
+ get rtl() {
+ return this._rtl;
+ }
+
+ set rtl(value) {
+ value = !!value;
+ this.dirty = this.dirty || (this._rtl != value);
+ this._rtl = value;
+ }
+
+ setRTL(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.rtl = enable;
+ return this;
+ }
+
+ updateTexture() {
+ this.clear();
+ DrawContent.call(this);
+ super.updateTexture();
+ return this;
+ }
+}
+
+export default LineProgress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Creator.js
new file mode 100644
index 000000000..9f2cc596a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Creator.js
@@ -0,0 +1,22 @@
+import RoundRectangle from './RoundRectangle.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var radius = GetAdvancedValue(config, 'radius', undefined);
+ var fillStyle = GetAdvancedValue(config, 'fillStyle', undefined);
+ var strokeStyle = GetAdvancedValue(config, 'strokeStyle', undefined);
+ var lineWidth = GetAdvancedValue(config, 'lineWidth', undefined);
+ var fillColor2 = GetAdvancedValue(config, 'fillColor2', undefined);
+ var isHorizontalGradient = GetAdvancedValue(config, 'isHorizontalGradient', true);
+ var gameObject = new RoundRectangle(this.scene, 0, 0, width, height, radius, fillStyle, strokeStyle, lineWidth, fillColor2, isHorizontalGradient);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/DrawContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/DrawContent.js
new file mode 100644
index 000000000..f09dbe3e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/DrawContent.js
@@ -0,0 +1,16 @@
+import DrawRoundRectangleBackground from '../utils/DrawRoundRectangleBackground.js';
+
+var DrawContent = function () {
+ DrawRoundRectangleBackground(
+ this,
+ this.fillStyle,
+ this.strokeStyle,
+ this.lineWidth,
+ this.radius,
+ this.fillColor2,
+ this.isHorizontalGradient,
+ this.iteration
+ );
+}
+
+export default DrawContent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Factory.js
new file mode 100644
index 000000000..8346195e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/Factory.js
@@ -0,0 +1,7 @@
+import RoundRectangle from './RoundRectangle.js';
+
+export default function (x, y, width, height, radius, fillStyle, strokeStyle, lineWidth, fillColor2, isHorizontalGradient) {
+ var gameObject = new RoundRectangle(this.scene, x, y, width, height, radius, fillStyle, strokeStyle, lineWidth, fillColor2, isHorizontalGradient);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.d.ts
new file mode 100644
index 000000000..caff6375f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.d.ts
@@ -0,0 +1,62 @@
+import Canvas from '../canvas/Canvas';
+
+export default RoundRectangle;
+
+declare namespace RoundRectangle {
+
+ interface IRadiusConfig {
+ tl?: (number | { x?: number, y?: number }),
+ tr?: (number | { x?: number, y?: number }),
+ bl?: (number | { x?: number, y?: number }),
+ br?: (number | { x?: number, y?: number }),
+
+ x?: number,
+ y?: number,
+ }
+
+}
+
+declare class RoundRectangle extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ radiusConfig?: number | RoundRectangle.IRadiusConfig |
+ ({
+ radius?: (number | RoundRectangle.IRadiusConfig),
+ iteration?: number
+ }),
+ fillStyle?: number | string | null,
+ strokeStyle?: number | string | null,
+ lineWidth?: number,
+
+ fillColor2?: number | string | null,
+ isHorizontalGradient?: boolean
+ );
+
+ fillStyle: string;
+ fillColor2: string;
+ isHorizontalGradient: boolean;
+ setFillStyle(
+ fillStyle?: number | string | null,
+ fillColor2?: number | string | null,
+ isHorizontalGradient?: boolean,
+ ): this;
+
+ strokeStyle: string;
+ lineWidth: number;
+ setStrokeStyle(
+ strokeStyle?: number | string | null,
+ lineWidth?: number,
+ ): this;
+
+ radius: number | RoundRectangle.IRadiusConfig;
+ setRadius(
+ value?: number | RoundRectangle.IRadiusConfig
+ ): this;
+
+ iteration: number;
+ setIteration(value?: number): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.js
new file mode 100644
index 000000000..be4c0dfaf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/roundrectangle/RoundRectangle.js
@@ -0,0 +1,126 @@
+import Canvas from '../canvasbase/Canvas.js';
+import GetStyle from '../../../utils/canvas/GetStyle.js';
+import DrawContent from './DrawContent.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class RoundRectangle extends Canvas {
+ constructor(scene, x, y, width, height, radiusConfig, fillStyle, strokeStyle, lineWidth, fillColor2, isHorizontalGradient) {
+ if (x === undefined) { x = 0; }
+ if (y === undefined) { y = 0; }
+ if (width === undefined) { width = 1; }
+ if (height === undefined) { height = width; }
+ if (radiusConfig === undefined) { radiusConfig = 0; }
+
+ super(scene, x, y, width, height);
+ this.type = 'rexRoundRectangleCanvas';
+
+ var radius = GetValue(radiusConfig, 'radius', radiusConfig);
+ var iteration = GetValue(radiusConfig, 'iteration', undefined);
+ this.setRadius(radius);
+ this.setIteration(iteration);
+ this.setFillStyle(fillStyle, fillColor2, isHorizontalGradient);
+ this.setStrokeStyle(strokeStyle, lineWidth);
+ }
+
+ get radius() {
+ return this._radius;
+ }
+
+ set radius(value) {
+ this.dirty |= (this._radius != value);
+ this._radius = value;
+ }
+
+ setRadius(radius) {
+ this.radius = radius;
+ return this;
+ }
+
+ get iteration() {
+ return this._iteration;
+ }
+
+ set iteration(value) {
+ this.dirty |= (this._iteration != value);
+ this._iteration = value;
+ }
+
+ setIteration(iteration) {
+ this.iteration = iteration;
+ return this;
+ }
+
+ get fillStyle() {
+ return this._fillStyle;
+ }
+
+ set fillStyle(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty |= (this._fillStyle != value);
+ this._fillStyle = value;
+ }
+
+ get fillColor2() {
+ return this._fillColor2;
+ }
+
+ set fillColor2(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty |= (this._fillColor2 != value);
+ this._fillColor2 = value;
+ }
+
+ get isHorizontalGradient() {
+ return this._isHorizontalGradient;
+ }
+
+ set isHorizontalGradient(value) {
+ this.dirty |= (this._isHorizontalGradient != value);
+ this._isHorizontalGradient = value;
+ }
+
+ setFillStyle(fillStyle, fillColor2, isHorizontalGradient) {
+ if (isHorizontalGradient === undefined) {
+ isHorizontalGradient = true;
+ }
+ this.fillStyle = fillStyle;
+ this.fillColor2 = fillColor2;
+ this.isHorizontalGradient = isHorizontalGradient;
+ return this;
+ }
+
+ get strokeStyle() {
+ return this._strokeStyle;
+ }
+
+ set strokeStyle(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.dirty |= (this._strokeStyle != value);
+ this._strokeStyle = value;
+ }
+
+ get lineWidth() {
+ return this._lineWidth;
+ }
+
+ set lineWidth(value) {
+ this.dirty |= (this._lineWidth != value);
+ this._lineWidth = value;
+ }
+
+ setStrokeStyle(strokeStyle, lineWidth) {
+ this.strokeStyle = strokeStyle;
+ this.lineWidth = lineWidth;
+ return this;
+ }
+
+ updateTexture() {
+ this.clear();
+ DrawContent.call(this);
+ super.updateTexture();
+ return this;
+ }
+}
+
+export default RoundRectangle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/utils/DrawRoundRectangleBackground.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/utils/DrawRoundRectangleBackground.js
new file mode 100644
index 000000000..e3f6b369b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/canvas/utils/DrawRoundRectangleBackground.js
@@ -0,0 +1,36 @@
+import DrawRoundRectangle from '../../../utils/canvas/DrawRoundRectangle.js';
+
+var DrawRoundRectangleBackground = function (
+ canvasObject,
+ color,
+ strokeColor, strokeLineWidth,
+ radius,
+ color2, isHorizontalGradient,
+ iteration
+) {
+
+ if ((color == null) && (strokeColor == null)) {
+ return;
+ }
+
+ var width = canvasObject.canvas.width,
+ height = canvasObject.canvas.height;
+
+ if (strokeColor == null) {
+ strokeLineWidth = 0;
+ }
+ var x = strokeLineWidth / 2;
+ width = Math.max(1, width - strokeLineWidth); // Min width is 1
+ height = Math.max(1, height - strokeLineWidth); // Min height is 1
+ DrawRoundRectangle(canvasObject.canvas, canvasObject.context,
+ x, x,
+ width, height,
+ radius,
+ color,
+ strokeColor, strokeLineWidth,
+ color2, isHorizontalGradient,
+ iteration
+ );
+}
+
+export default DrawRoundRectangleBackground;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Carousel.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Carousel.js
new file mode 100644
index 000000000..19997d77b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Carousel.js
@@ -0,0 +1,26 @@
+import GridTable from '../gridtable/GridTable.js';
+import Methods from './methods/Methods.js';
+
+class Carousel extends GridTable {
+ constructor(scene, x, y, width, height, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ config.columns = 1; // Force columns to 1
+ if (!config.hasOwnProperty('clamplTableOXY')) {
+ config.clamplTableOXY = false;
+ }
+
+ super(scene, x, y, width, height, config);
+ this.type = 'rexCarousel';
+
+ }
+}
+
+// mixin
+Object.assign(
+ Carousel.prototype,
+ Methods
+);
+
+export default Carousel;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Creator.js
new file mode 100644
index 000000000..2aa1a68c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Creator.js
@@ -0,0 +1,23 @@
+import Carousel from './Carousel.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetValue(config, 'width', 256);
+ var height = GetValue(config, 'height', 256);
+ var gameObject = new Carousel(this.scene, 0, 0, width, height, config);
+
+ // set properties wo modify children
+ gameObject.syncChildrenEnable = false;
+ BuildGameObject(this.scene, gameObject, config);
+ // sync properties of children
+ gameObject.syncChildrenEnable = true;
+ gameObject.syncPosition().syncVisible().syncAlpha();
+
+ return gameObject;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Factory.js
new file mode 100644
index 000000000..992213228
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/Factory.js
@@ -0,0 +1,7 @@
+import Carousel from './Carousel.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new Carousel(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/Methods.js
new file mode 100644
index 000000000..f19d37e68
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/Methods.js
@@ -0,0 +1,7 @@
+import ShowCells from './ShowCells.js';
+
+var Methods = {
+ showCells: ShowCells
+}
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/ShowCells.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/ShowCells.js
new file mode 100644
index 000000000..4854b8a37
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/carousel/methods/ShowCells.js
@@ -0,0 +1,57 @@
+var ShowCells = function () {
+ if (this.cellsCount === 0) {
+ return;
+ }
+ var table = this.table;
+
+ var startRowIdx = table.heightToRowIndex(-this.tableOY);
+ if (startRowIdx <= 0) {
+ startRowIdx = 0; //Turn -0 to 0
+ }
+ var rowIdx = startRowIdx;
+
+ var startColIdx = table.widthToColIndex(-this.tableOX);
+ if (startColIdx <= 0) {
+ startColIdx = 0; //Turn -0 to 0
+ }
+ var colIdx = startColIdx;
+
+ var cellIdx = table.colRowToCellIndex(colIdx, rowIdx);
+ var bottomBound = this.bottomBound;
+ var rightBound = this.rightBound;
+ var lastIdx = table.cellsCount - 1;
+ var lastColIdx = table.colCount - 1;
+
+ var startCellTLX = this.getCellTLX(colIdx),
+ cellTLX = startCellTLX;
+ var cellTLY = this.getCellTLY(rowIdx);
+ while ((cellTLY < bottomBound) && (cellIdx <= lastIdx)) {
+ if (this.table.isValidCellIdx(cellIdx)) {
+ var cell = table.getCell(cellIdx, true);
+ this.visibleCells.set(cell);
+ if (!this.preVisibleCells.contains(cell)) {
+ this.showCell(cell);
+ }
+ if (this.scrollMode === 0) {
+ cell.setXY(cellTLX, cellTLY);
+ } else {
+ cell.setXY(cellTLY, cellTLX);
+ }
+ }
+
+ if ((cellTLX < rightBound) && (colIdx < lastColIdx)) {
+ cellTLX += table.getColWidth(colIdx);
+ colIdx += 1;
+ } else {
+ cellTLX = startCellTLX;
+ cellTLY += table.getRowHeight(rowIdx);
+
+ colIdx = startColIdx;
+ rowIdx += 1;
+ }
+
+ cellIdx = table.colRowToCellIndex(colIdx, rowIdx);
+ }
+}
+
+export default ShowCells;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Active.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Active.js
new file mode 100644
index 000000000..cd0d5995e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Active.js
@@ -0,0 +1,47 @@
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ updateChildActive(child) {
+ var localState = GetLocalState(child);
+ var parent = localState.parent;
+ child.active = parent.active && localState.active;
+ return this;
+ },
+
+ syncActive() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildActive, this);
+ }
+ return this;
+ },
+
+ resetChildActiveState(child) {
+ var localState = GetLocalState(child);
+ localState.active = child.active;
+ return this;
+ },
+
+ setChildActive(child, active) {
+ child.active = active;
+ this.resetChildActiveState(child);
+ return this;
+ },
+
+ setChildLocalActive(child, active) {
+ if (active === undefined) {
+ active = true;
+ }
+ var localState = GetLocalState(child);
+ localState.active = active;
+ this.updateChildActive(child);
+ return this;
+ },
+
+ resetLocalActiveState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildActiveState(this);
+ }
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/AddChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/AddChild.js
new file mode 100644
index 000000000..a861463d9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/AddChild.js
@@ -0,0 +1,136 @@
+import Base from './Base.js';
+import GetLocalState from './utils/GetLocalState.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const BaseAdd = Base.prototype.add;
+
+var Add = function (gameObject, config) {
+ this.setParent(gameObject);
+
+ var state = GetLocalState(gameObject);
+ SetupSyncFlags(state, config);
+
+ this
+ .resetChildState(gameObject) // Reset local state of child
+ .updateChildVisible(gameObject) // Apply parent's visible to child
+ .updateChildActive(gameObject) // Apply parent's active to child
+ .updateChildScrollFactor(gameObject) // Apply parent's scroll factor to child
+ .updateChildMask(gameObject); // Apply parent's mask to child
+
+ BaseAdd.call(this, gameObject);
+
+ this.addToParentContainer(gameObject);
+ this.addToRenderLayer(gameObject);
+
+ return this;
+}
+
+var AddLocal = function (gameObject, config) {
+ this.setParent(gameObject);
+
+ // Set local state from child directly
+ var state = GetLocalState(gameObject);
+ SetupSyncFlags(state, config);
+ // Position
+ state.x = gameObject.x;
+ state.y = gameObject.y;
+ state.rotation = gameObject.rotation;
+ state.scaleX = gameObject.scaleX;
+ state.scaleY = gameObject.scaleY;
+ // Alpha
+ state.alpha = gameObject.alpha;
+ // Visible
+ state.visible = gameObject.visible;
+ // Active
+ state.active = gameObject.active;
+
+ this
+ .updateChildPosition(gameObject)
+ .updateChildAlpha(gameObject)
+ .updateChildVisible(gameObject) // Apply parent's visible to child
+ .updateChildActive(gameObject) // Apply parent's active to child
+ .updateChildScrollFactor(gameObject) // Apply parent's scroll factor to child
+ .updateChildMask(gameObject); // Apply parent's mask to child
+
+ BaseAdd.call(this, gameObject);
+
+ this.addToRenderLayer(gameObject);
+
+ return this;
+}
+
+var SetupSyncFlags = function (state, config) {
+ if (config === undefined) {
+ config = true;
+ }
+
+ if (typeof (config) === 'boolean') {
+ state.syncPosition = config;
+ state.syncRotation = config;
+ state.syncScale = config;
+ state.syncAlpha = config;
+ state.syncScrollFactor = config;
+ } else {
+ state.syncPosition = GetValue(config, 'syncPosition', true);
+ state.syncRotation = GetValue(config, 'syncRotation', true);
+ state.syncScale = GetValue(config, 'syncScale', true);
+ state.syncAlpha = GetValue(config, 'syncAlpha', true);
+ state.syncScrollFactor = GetValue(config, 'syncScrollFactor', true);
+ }
+
+}
+
+export default {
+ // Can override this method
+ add(gameObject) {
+ if (Array.isArray(gameObject)) {
+ this.addMultiple(gameObject);
+ } else {
+ Add.call(this, gameObject);
+ }
+ return this;
+ },
+
+ // Don't override this method
+ pin(gameObject, config) {
+ if (Array.isArray(gameObject)) {
+ this.addMultiple(gameObject, config);
+ } else {
+ Add.call(this, gameObject, config);
+ }
+ return this;
+ },
+
+ addMultiple(gameObjects) {
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ Add.call(this, gameObjects[i]);
+ }
+ return this;
+ },
+
+ addLocal(gameObject) {
+ if (Array.isArray(gameObject)) {
+ this.addMultiple(gameObject);
+ } else {
+ AddLocal.call(this, gameObject);
+ }
+ return this;
+ },
+
+ // Don't override this method
+ pinLocal(gameObject, config) {
+ if (Array.isArray(gameObject)) {
+ this.addMultiple(gameObject, config);
+ } else {
+ AddLocal.call(this, gameObject, config);
+ }
+ return this;
+ },
+
+ addLocalMultiple(gameObjects) {
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ AddLocal.call(this, gameObjects[i]);
+ }
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Alpha.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Alpha.js
new file mode 100644
index 000000000..5c620415f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Alpha.js
@@ -0,0 +1,48 @@
+import GetLocalState from './utils/GetLocalState.js';
+import GetScale from './utils/GetScale.js';
+
+export default {
+ updateChildAlpha(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ if (state.syncAlpha) {
+ child.alpha = parent.alpha * state.alpha;
+ }
+ return this;
+ },
+
+ syncAlpha() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildAlpha, this);
+ }
+ return this;
+ },
+
+ resetChildAlphaState(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ state.alpha = GetScale(child.alpha, parent.alpha);
+ return this;
+ },
+
+ setChildAlpha(child, alpha) {
+ child.alpha = alpha;
+ this.resetChildAlphaState(child);
+ return this;
+ },
+
+ setChildLocalAlpha(child, alpha) {
+ var state = GetLocalState(child);
+ state.alpha = alpha;
+ this.updateChildAlpha(child);
+ return this;
+ },
+
+ resetLocalAlphaState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildAlphaState(this);
+ }
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.d.ts
new file mode 100644
index 000000000..503f77c3c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.d.ts
@@ -0,0 +1,43 @@
+export default Base;
+
+declare class Base extends Phaser.GameObjects.Zone {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ width?: number, height?: number,
+ );
+
+ contains(
+ gameObject: Phaser.GameObjects.GameObject
+ ): boolean;
+
+ add(
+ child: Phaser.GameObjects.GameObject | Phaser.GameObjects.GameObject[]
+ ): this;
+
+ remove(
+ gameObject: Phaser.GameObjects.GameObject,
+ destroyChild?: boolean
+ ): this;
+
+ clear(
+ destroyChild?: boolean
+ ): this;
+
+
+ // Components
+ clearAlpha(): this;
+ setAlpha(topLeft?: number, topRight?: number, bottomLeft?: number, bottomRight?: number): this;
+ alpha: number;
+ alphaTopLeft: number;
+ alphaTopRight: number;
+ alphaBottomLeft: number;
+ alphaBottomRight: number;
+
+ toggleFlipX(): this;
+ toggleFlipY(): this;
+ setFlipX(value: boolean): this;
+ setFlipY(value: boolean): this;
+ setFlip(x: boolean, y: boolean): this;
+ resetFlip(): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.js
new file mode 100644
index 000000000..53ee74f41
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Base.js
@@ -0,0 +1,104 @@
+const Zone = Phaser.GameObjects.Zone;
+const AddItem = Phaser.Utils.Array.Add;
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+class Base extends Zone {
+ constructor(scene, x, y, width, height) {
+ if (x === undefined) {
+ x = 0;
+ }
+ if (y === undefined) {
+ y = 0;
+ }
+ if (width === undefined) {
+ width = 1;
+ }
+ if (height === undefined) {
+ height = 1;
+ }
+ super(scene, x, y, width, height);
+ this.children = [];
+ }
+
+ destroy(fromScene) {
+ // This Game Object has already been destroyed
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ if (fromScene) {
+ // Stop scene
+ var child;
+ for (var i = this.children.length - 1; i >= 0; i--) {
+ child = this.children[i];
+ if (!child.parentContainer && // Not in container
+ !child.displayList // Not in scene, neither in layer
+ ) {
+ // Destroy child which is not in scene, container, or layer manually
+ child.destroy(fromScene);
+ }
+ }
+ }
+
+ // Destroy/remove children
+ this.clear(!fromScene);
+ super.destroy(fromScene);
+ }
+
+ contains(gameObject) {
+ return (this.children.indexOf(gameObject) !== -1);
+ }
+
+ add(gameObjects) {
+ var parent = this;
+ AddItem(this.children, gameObjects, 0,
+ // Callback of item added
+ function (gameObject) {
+ gameObject.once('destroy', parent.onChildDestroy, parent);
+ }, this);
+ return this;
+ }
+
+ remove(gameObjects, destroyChild) {
+ var parent = this;
+ RemoveItem(this.children, gameObjects,
+ // Callback of item removed
+ function (gameObject) {
+ gameObject.off('destroy', parent.onChildDestroy, parent);
+ if (destroyChild) {
+ gameObject.destroy();
+ }
+ }
+ );
+ return this;
+ }
+
+ onChildDestroy(child, fromScene) {
+ // Only remove reference
+ this.remove(child, false);
+ }
+
+ clear(destroyChild) {
+ var parent = this;
+ var gameObject;
+ for (var i = 0, cnt = this.children.length; i < cnt; i++) {
+ gameObject = this.children[i];
+ gameObject.off('destroy', parent.onChildDestroy, parent);
+ if (destroyChild) {
+ gameObject.destroy();
+ }
+ }
+ this.children.length = 0;
+ return this;
+ }
+}
+
+const Components = Phaser.GameObjects.Components;
+Phaser.Class.mixin(Base,
+ [
+ Components.Alpha,
+ Components.Flip
+ ]
+);
+
+export default Base;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChangeOrigin.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChangeOrigin.js
new file mode 100644
index 000000000..dfd0a1b43
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChangeOrigin.js
@@ -0,0 +1,15 @@
+import ChangeOriginBase from '../../../utils/origin/ChangeOrigin.js';
+
+var ChangeOrigin = function (originX, originY) {
+ this.syncChildrenEnable = false;
+ ChangeOriginBase(this, originX, originY);
+ this.syncChildrenEnable = true;
+
+ var children = this.getAllChildren();
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ this.resetChildPositionState(children[i]);
+ }
+ return this;
+}
+
+export default ChangeOrigin;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChildState.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChildState.js
new file mode 100644
index 000000000..fce11dc15
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ChildState.js
@@ -0,0 +1,34 @@
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ getLocalState(gameObject) {
+ return GetLocalState(gameObject);
+ },
+
+ resetChildState(gameObject) {
+ this
+ .resetChildPositionState(gameObject)
+ .resetChildVisibleState(gameObject)
+ .resetChildAlphaState(gameObject)
+ .resetChildActiveState(gameObject);
+ return this;
+ },
+
+ resetChildrenState(gameObjects) {
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ this.resetChildState(gameObjects[i]);
+ }
+ return this;
+ },
+
+ syncProperties() {
+ this
+ .syncPosition()
+ .syncVisible()
+ .syncAlpha()
+ .syncActive()
+ .syncScrollFactor()
+ .syncMask();
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Children.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Children.js
new file mode 100644
index 000000000..cd2917f38
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Children.js
@@ -0,0 +1,135 @@
+import { GetParent } from './GetParent.js';
+import { DepthFirstSearch, BreadthFirstSearch } from './utils/Traversal.js';
+
+const ArrayUtils = Phaser.Utils.Array;
+
+export default {
+ getChildren(out) {
+ if (!out) {
+ out = this.children; // Return internal children array
+ } else {
+ for (var i = 0, cnt = this.children.length; i < cnt; i++) {
+ out.push(this.children[i]);
+ }
+ // Copy children
+ }
+ return out;
+ },
+
+ getAllChildren(out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ var root = this;
+ BreadthFirstSearch(root, function (child) {
+ // Don't add root
+ if (child === root) {
+ return;
+ }
+ out.push(child);
+ });
+
+ return out;
+ },
+
+ getAllVisibleChildren(out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ var root = this;
+ BreadthFirstSearch(root, function (child) {
+ // Don't add root
+ if (child === root) {
+ return;
+ }
+ // Don't add invisible child
+ if (!child.visible) {
+ return true;
+ }
+ out.push(child);
+ });
+
+ return out;
+ },
+
+ bfs(callback, root) {
+ if (root === undefined) {
+ root = this;
+ }
+ BreadthFirstSearch(root, callback);
+ return this;
+ },
+
+ dfs(callback, root) {
+ if (root === undefined) {
+ root = this;
+ }
+ DepthFirstSearch(root, callback);
+ return this;
+ },
+
+ contains(gameObject) { // Override Base.contains method
+ var parent = GetParent(gameObject);
+ if (!parent) {
+ return false;
+ } else if (parent === this) {
+ return true;
+ } else {
+ return this.contains(parent);
+ }
+ },
+
+ getByName(name, recursive) {
+ if (!recursive) {
+ return ArrayUtils.GetFirst(this.children, 'name', name); // object, or null if not found
+
+ } else { // recursive
+ // Breadth-first search
+ var queue = [this];
+ var parent, child;
+ while (queue.length) {
+ parent = queue.shift();
+
+ for (var i = 0, cnt = parent.children.length; i < cnt; i++) {
+ child = parent.children[i];
+ if (child.name === name) {
+ return child;
+ } else if (child.isRexContainerLite) {
+ queue.push(child);
+ }
+ }
+ }
+ return null;
+
+ }
+
+ },
+
+ getRandom(startIndex, length) {
+ return ArrayUtils.GetRandom(this.children, startIndex, length);
+ },
+
+ getFirst(property, value, startIndex, endIndex) {
+ return ArrayUtils.GetFirstElement(this.children, property, value, startIndex, endIndex);
+ },
+
+ getAll(property, value, startIndex, endIndex) {
+ return ArrayUtils.GetAll(this.children, property, value, startIndex, endIndex);
+ },
+
+ count(property, value, startIndex, endIndex) {
+ return ArrayUtils.CountAllMatching(this.children, property, value, startIndex, endIndex);
+ },
+
+ swap(child1, child2) {
+ ArrayUtils.Swap(this.children, child1, child2);
+ return this;
+ },
+
+ setAll(property, value, startIndex, endIndex) {
+ ArrayUtils.SetAll(this.children, property, value, startIndex, endIndex);
+ return this;
+ },
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.d.ts
new file mode 100644
index 000000000..a6083e399
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.d.ts
@@ -0,0 +1,345 @@
+// import * as Phaser from 'phaser';
+import Base from './Base';
+
+export default ContainerLite;
+
+declare namespace ContainerLite {
+ interface ILocalState {
+ parent: ContainerLite,
+ self: Phaser.GameObjects.GameObject,
+ layer: Phaser.GameObjects.Layer | null,
+
+ x: number, y: number,
+ rotation: number, angle: number,
+ scaleX: number, scaleY: number, displayWidth: number, displayHeight: number,
+ alpha: number,
+ visible: boolean,
+ active: boolean,
+ }
+
+ interface IAddChildConfig {
+ syncPosition?: boolean,
+ syncRotation?: boolean,
+ syncScale?: boolean,
+ syncAlpha?: boolean,
+ syncScrollFactor?: boolean,
+
+ }
+
+ interface IDrawBoundsConfig {
+ drawContainer?: boolean,
+ children?: Phaser.GameObjects.GameObject,
+ color?: number,
+ lineWidth?: number,
+ padding?: number,
+ }
+}
+
+declare class ContainerLite extends Base {
+ isRexContainerLite: true;
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ width?: number, height?: number,
+ children?: Phaser.GameObjects.GameObject[]
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ children?: Phaser.GameObjects.GameObject[]
+ );
+
+ add(
+ gameObject: Phaser.GameObjects.GameObject | Phaser.GameObjects.GameObject[]
+ ): this;
+
+ pin(
+ gameObject: Phaser.GameObjects.GameObject | Phaser.GameObjects.GameObject[],
+ config?: ContainerLite.IAddChildConfig | boolean
+ ): this;
+
+ unpin(
+ gameObject: Phaser.GameObjects.GameObject,
+ destroyChild?: boolean
+ ): this;
+
+ addMultiple(
+ children: Phaser.GameObjects.GameObject[]
+ ): this;
+
+ addLocal(
+ child: Phaser.GameObjects.GameObject | Phaser.GameObjects.GameObject[]
+ ): this;
+
+ pinLocal(
+ child: Phaser.GameObjects.GameObject | Phaser.GameObjects.GameObject[],
+ config?: ContainerLite.IAddChildConfig | boolean
+ ): this;
+
+ addLocalMultiple(
+ children: Phaser.GameObjects.GameObject[]
+ ): this;
+
+ setChildPosition(
+ child: Phaser.GameObjects.GameObject,
+ x: number,
+ y: number
+ ): this;
+
+ setChildLocalPosition(
+ child: Phaser.GameObjects.GameObject,
+ x: number,
+ y: number
+ ): this;
+
+ setChildRotation(
+ child: Phaser.GameObjects.GameObject,
+ rotation: number
+ ): this;
+
+ setChildAngle(
+ child: Phaser.GameObjects.GameObject,
+ angle: number
+ ): this;
+
+ setChildLocalRotation(
+ child: Phaser.GameObjects.GameObject,
+ rotation: number
+ ): this;
+
+ setChildLocalAngle(
+ child: Phaser.GameObjects.GameObject,
+ angle: number
+ ): this;
+
+ setChildScale(
+ child: Phaser.GameObjects.GameObject,
+ scaleX: number,
+ scaleY: number
+ ): this;
+
+ setChildLocalScale(
+ child: Phaser.GameObjects.GameObject,
+ scaleX: number,
+ scaleY: number
+ ): this;
+
+ setChildDisplaySize(
+ child: Phaser.GameObjects.GameObject,
+ width: number,
+ height: number
+ ): this;
+
+ setChildVisible(
+ child: Phaser.GameObjects.GameObject,
+ visible: boolean
+ ): this;
+
+ setChildAlpha(
+ child: Phaser.GameObjects.GameObject,
+ alpha: number
+ ): this;
+
+ setChildLocalAlpha(
+ child: Phaser.GameObjects.GameObject,
+ alpha: number
+ ): this;
+
+ resetChildState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildPositionState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildRotationState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildScaleState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildAlphaState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildVisibleState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ resetChildActiveState(
+ child: Phaser.GameObjects.GameObject
+ ): this;
+
+ setMask(
+ mask: Phaser.Display.Masks.BitmapMask | Phaser.Display.Masks.GeometryMask
+ ): this;
+
+ clearMask(
+ destroyMask?: boolean
+ ): this;
+
+ tween(
+ config: Phaser.Types.Tweens.TweenBuilderConfig | object
+ ): Phaser.Tweens.Tween;
+
+ tweenChild(
+ config: Phaser.Types.Tweens.TweenBuilderConfig | object
+ ): Phaser.Tweens.Tween;
+
+ tweenSelf(
+ config: Phaser.Types.Tweens.TweenBuilderConfig | object
+ ): Phaser.Tweens.Tween;
+
+ createTweenChildConfig(
+ config: Phaser.Types.Tweens.TweenBuilderConfig | object
+ ): Phaser.Types.Tweens.TweenBuilderConfig;
+
+ getChildren(
+ out?: Phaser.GameObjects.GameObject[]
+ ): Phaser.GameObjects.GameObject[];
+
+ getAllChildren(
+ out?: Phaser.GameObjects.GameObject[]
+ ): Phaser.GameObjects.GameObject[];
+
+ getAllVisibleChildren(
+ out?: Phaser.GameObjects.GameObject[]
+ ): Phaser.GameObjects.GameObject[];
+
+ bfs(
+ callback: (child: Phaser.GameObjects.GameObject) => boolean
+ ): this;
+
+ dfs(
+ callback: (child: Phaser.GameObjects.GameObject) => boolean
+ ): this;
+
+ getByName(
+ name: string,
+ recursive?: boolean
+ ): Phaser.GameObjects.GameObject;
+
+ getRandom(): Phaser.GameObjects.GameObject;
+
+ getFirst(
+ property: string,
+ value?: unknown,
+ startIndex?: number,
+ endIndex?: number
+ ): Phaser.GameObjects.GameObject;
+
+ getAll(
+ property: string,
+ value?: unknown,
+ startIndex?: number,
+ endIndex?: number
+ ): Phaser.GameObjects.GameObject;
+
+ count(
+ property: string,
+ value?: unknown,
+ startIndex?: number,
+ endIndex?: number
+ ): this;
+
+ swap(
+ child1: Phaser.GameObjects.GameObject,
+ child2: Phaser.GameObjects.GameObject
+ ): number;
+
+ setAll(
+ property: string,
+ value?: unknown,
+ startIndex?: number,
+ endIndex?: number
+ ): this;
+
+ setDepth(
+ value: number,
+ containerOnly?: boolean
+ ): this;
+
+ swapDepth(
+ containerB: ContainerLite
+ ): this;
+
+ incDepth(
+ inc: number
+ ): this;
+
+ bringToTop(): this;
+
+ moveDepthBelow(
+ gameObject: Phaser.GameObjects.GameObject
+ ): this;
+
+ moveDepthAbove(
+ gameObject: Phaser.GameObjects.GameObject
+ ): this;
+
+ getParent(
+ name?: string
+ ): ContainerLite;
+
+ getParent(
+ gameObject?: Phaser.GameObjects.GameObject,
+ name?: string
+ ): ContainerLite;
+
+ getTopmostParent(
+ gameObject?: Phaser.GameObjects.GameObject
+ ): ContainerLite;
+
+ getLocalState(
+ child: Phaser.GameObjects.GameObject
+ ): ContainerLite.ILocalState;
+
+ addToLayer(
+ layer: Phaser.GameObjects.Layer
+ ): this;
+
+ addToContainer(
+ container: Phaser.GameObjects.Container
+ ): this;
+
+ removeFromContainer(): this;
+
+ enableLayer(): this;
+
+ getLayer(): Phaser.GameObjects.Layer;
+
+ snapshot(
+ config?: {
+ renderTexture?: Phaser.GameObjects.RenderTexture,
+ padding?: number,
+ }
+ ): Phaser.GameObjects.RenderTexture;
+
+ snapshot(
+ config?: {
+ padding?: number,
+ saveTexture: string,
+ }
+ ): this;
+
+ changeOrigin(
+ originX: number,
+ originY: number
+ ): this;
+
+ drawBounds(
+ graphics: Phaser.GameObjects.Graphics,
+ color?: number
+ ): this;
+
+ drawBounds(
+ graphics: Phaser.GameObjects.Graphics,
+ config?: ContainerLite.IDrawBoundsConfig,
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.js
new file mode 100644
index 000000000..67da68b86
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ContainerLite.js
@@ -0,0 +1,270 @@
+import Base from './Base.js';
+import Methods from './Methods.js';
+import { GetParent } from './GetParent.js';
+
+class ContainerLite extends Base {
+ constructor(scene, x, y, width, height, children) {
+ if (Array.isArray(width)) {
+ children = width;
+ width = undefined;
+ height = undefined;
+ }
+ super(scene, x, y, width, height);
+ this.type = 'rexContainerLite';
+ this.isRexContainerLite = true;
+ this.syncChildrenEnable = true;
+
+ this._active = true;
+ this._mask = null;
+ this._scrollFactorX = 1;
+ this._scrollFactorY = 1;
+ this.privateRenderLayer = undefined;
+
+ if (children) {
+ this.add(children);
+ }
+ }
+
+ destroy(fromScene) {
+ // This Game Object has already been destroyed
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ this.syncChildrenEnable = false; // Don't sync properties changing anymore
+ super.destroy(fromScene);
+
+ if (this.privateRenderLayer) {
+ this.privateRenderLayer.list.length = 0; // Remove all children without trigger callback
+ this.privateRenderLayer.destroy();
+ }
+ }
+
+ resize(width, height) {
+ this.setSize(width, height);
+ return this;
+ }
+
+ get x() {
+ return this._x;
+ }
+
+ set x(value) {
+ if (this._x === value) {
+ return;
+ }
+ this._x = value;
+
+ this.syncPosition();
+ }
+
+ get y() {
+ return this._y;
+ }
+
+ set y(value) {
+ if (this._y === value) {
+ return;
+ }
+ this._y = value;
+
+ this.syncPosition();
+ }
+
+ // Override
+ get rotation() {
+ return super.rotation;
+ }
+
+ set rotation(value) {
+ if (this.rotation === value) {
+ return;
+ }
+ super.rotation = value;
+
+ this.syncPosition();
+ }
+
+ // Override
+ get scaleX() {
+ return super.scaleX;
+ }
+
+ set scaleX(value) {
+ if (this.scaleX === value) {
+ return;
+ }
+ super.scaleX = value;
+
+ this.syncPosition();
+ }
+
+ // Override
+ get scaleY() {
+ return super.scaleY;
+ }
+
+ set scaleY(value) {
+ if (this.scaleY === value) {
+ return;
+ }
+ super.scaleY = value;
+
+ this.syncPosition();
+ }
+
+ // Override
+ get scale() {
+ return super.scale;
+ }
+
+ set scale(value) {
+ if (this.scale === value) {
+ return;
+ }
+ super.scale = value;
+
+ this.syncPosition();
+ }
+
+ // Override
+ get visible() {
+ return super.visible;
+ }
+
+ set visible(value) {
+ if (super.visible === value) {
+ return;
+ }
+ super.visible = value;
+
+ this.syncVisible();
+ }
+
+ // Override
+ get alpha() {
+ return super.alpha;
+ }
+
+ set alpha(value) {
+ if (super.alpha === value) {
+ return;
+ }
+ super.alpha = value;
+
+ this.syncAlpha();
+ }
+
+ // Override
+ get active() {
+ return this._active;
+ }
+
+ set active(value) {
+ if (this._active === value) {
+ return;
+ }
+ this._active = value;
+
+ this.syncActive();
+ }
+
+ // Override
+ get mask() {
+ return this._mask;
+ }
+ set mask(mask) {
+ if (this._mask === mask) {
+ return;
+ }
+ this._mask = mask;
+
+ this.syncMask();
+ }
+
+ // Override
+ get scrollFactorX() {
+ return this._scrollFactorX;
+ }
+
+ set scrollFactorX(value) {
+ if (this._scrollFactorX === value) {
+ return;
+ }
+
+ this._scrollFactorX = value;
+ this.syncScrollFactor();
+ }
+ get scrollFactorY() {
+ return this._scrollFactorY;
+ }
+
+ set scrollFactorY(value) {
+ if (this._scrollFactorY === value) {
+ return;
+ }
+
+ this._scrollFactorY = value;
+ this.syncScrollFactor();
+ }
+
+ // Compatiable with container plugin
+ get list() {
+ return this.children;
+ }
+
+ static GetParent(child) {
+ return GetParent(child);
+ }
+
+ // For p3-container
+ get parentContainer() {
+ return this._parentContainer;
+ }
+
+ set parentContainer(value) {
+ // Initialize
+ if (!this._parentContainer && !value) {
+ this._parentContainer = value;
+ return;
+ }
+
+ // Set this._parentContainer only,
+ // if under AddToContainer, or RemoveFromContainer methods
+ if (this.setParentContainerFlag) {
+ this._parentContainer = value;
+ return;
+ }
+ // else if (!this.setParentContainerFlag)
+
+ // Add itself and all children to container,
+ // Or remove itseld and all children from container
+ if (this._parentContainer && !value) {
+ // Remove from container
+ this.removeFromContainer();
+ this._parentContainer = value;
+ } else if (value) {
+ // Add to container
+ this._parentContainer = value;
+ this.addToContainer(value);
+ } else {
+ this._parentContainer = value;
+ }
+ }
+
+ get setParentContainerFlag() {
+ if (this._setParentContainerFlag) {
+ return true;
+ }
+ var parent = GetParent(this);
+ return (parent) ? parent.setParentContainerFlag : false;
+ }
+
+}
+
+Object.assign(
+ ContainerLite.prototype,
+ Methods
+);
+
+export default ContainerLite;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Creator.js
new file mode 100644
index 000000000..5c098a686
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Creator.js
@@ -0,0 +1,25 @@
+import ContainerLite from './ContainerLite.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', 1);
+ var height = GetAdvancedValue(config, 'height', width);
+ var children = GetValue(config, 'children', undefined);
+ var gameObject = new ContainerLite(this.scene, 0, 0, width, height);
+
+ // set properties wo modify children
+ gameObject.syncChildrenEnable = false;
+ BuildGameObject(this.scene, gameObject, config);
+ // sync properties of children
+ gameObject.syncChildrenEnable = true;
+
+ gameObject.add(children);
+ return gameObject;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Depth.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Depth.js
new file mode 100644
index 000000000..c945e6d16
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Depth.js
@@ -0,0 +1,85 @@
+import SortGameObjectsByDepth from '../../../utils/system/SortGameObjectsByDepth.js';
+
+export default {
+ setDepth(value, containerOnly) {
+ this.depth = value;
+ if (!containerOnly && this.children) {
+ var children = this.getAllChildren();
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ children[i].depth = value;
+ }
+ }
+ return this;
+ },
+
+ swapDepth(containerB) {
+ var depthA = this.depth;
+ var depthB = containerB.depth;
+ this.setDepth(depthB);
+ containerB.setDepth(depthA);
+ return this;
+ },
+
+ incDepth(inc) {
+ this.depth += inc;
+ if (this.children) {
+ var children = this.getAllChildren();
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ children[i].depth += inc;
+ }
+ }
+ return this;
+ },
+
+ bringToTop() {
+ var displayList = this.displayList;
+ var children = this.getAllChildren([this]);
+ SortGameObjectsByDepth(children, false);
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (displayList.exists(child)) {
+ displayList.bringToTop(child);
+ }
+ }
+ return this;
+ },
+
+ moveDepthBelow(gameObject) {
+ var displayList = this.displayList;
+ if (gameObject.displayList !== displayList) {
+ // Do nothing if not at the same display list
+ return this;
+ }
+
+ var children = this.getAllChildren([this]);
+ SortGameObjectsByDepth(children, false);
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (displayList.exists(child)) {
+ displayList.moveBelow(gameObject, child);
+ break;
+ }
+ }
+ return this;
+ },
+
+ moveDepthAbove(gameObject) {
+ var displayList = this.displayList;
+ if (gameObject.displayList !== displayList) {
+ // Do nothing if not at the same display list
+ return this;
+ }
+
+ var children = this.getAllChildren([this]);
+ SortGameObjectsByDepth(children, true);
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (displayList.exists(child)) {
+ displayList.moveAbove(gameObject, child);
+ break;
+ }
+ }
+ return this;
+ },
+
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/DrawBounds.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/DrawBounds.js
new file mode 100644
index 000000000..89592ddf1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/DrawBounds.js
@@ -0,0 +1,24 @@
+import Draw from '../../../utils/bounds/DrawBounds.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var DrawBounds = function (graphics, config) {
+ var drawContainer = GetValue(config, 'drawContainer', true);
+
+ var gameObjects = GetValue(config, 'children');
+ if (gameObjects === undefined) {
+ gameObjects = this.getAllVisibleChildren([this]);
+ }
+
+ if (!drawContainer) {
+ gameObjects = gameObjects.filter(function (gameObject) {
+ return !gameObject.isRexContainerLite;
+ })
+ }
+
+ Draw(gameObjects, graphics, config);
+
+ return this;
+}
+
+export default DrawBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Factory.js
new file mode 100644
index 000000000..b3bbf910a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Factory.js
@@ -0,0 +1,7 @@
+import ContainerLite from './ContainerLite.js';
+
+export default function (x, y, width, height, children) {
+ var gameObject = new ContainerLite(this.scene, x, y, width, height, children);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/GetParent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/GetParent.js
new file mode 100644
index 000000000..c58650e95
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/GetParent.js
@@ -0,0 +1,28 @@
+var GetParent = function (gameObject, name) {
+ var parent;
+ if (name === undefined) {
+ if (gameObject.hasOwnProperty('rexContainer')) {
+ parent = gameObject.rexContainer.parent;
+ }
+ } else {
+ parent = GetParent(gameObject);
+ while (parent) {
+ if (parent.name === name) {
+ break;
+ }
+ parent = GetParent(parent);
+ }
+ }
+ return parent;
+}
+
+var GetTopmostParent = function (gameObject) {
+ var parent = GetParent(gameObject);
+ while (parent) {
+ gameObject = parent;
+ parent = GetParent(parent);
+ }
+ return gameObject;
+}
+
+export { GetParent, GetTopmostParent };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Layer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Layer.js
new file mode 100644
index 000000000..2b7f46335
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Layer.js
@@ -0,0 +1,89 @@
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ enableLayer() {
+ if (this.privateRenderLayer) {
+ return this;
+ }
+
+ var layer = this.scene.add.layer();
+ // layer.name = (this.name) ? `${this.name}.privateLayer` : 'privateLayer';
+
+ this.moveDepthBelow(layer);
+
+ this.addToLayer(layer);
+
+ this.privateRenderLayer = layer;
+
+ return this;
+ },
+
+ getLayer() {
+ if (!this.privateRenderLayer) {
+ this.enableLayer();
+ }
+
+ return this.privateRenderLayer;
+ },
+
+ getRenderLayer() {
+ // This containerLite has a layer
+ if (this.privateRenderLayer) {
+ return this.privateRenderLayer;
+ }
+
+ // One of parent container has a layer
+ var parent = this.getParent();
+ while (parent) {
+ var layer = parent.privateRenderLayer;
+ if (layer) {
+ return layer;
+ }
+ parent = parent.getParent();
+ }
+
+ return null;
+ },
+
+ // Internal method for adding child
+ addToRenderLayer(gameObject) {
+ // Don't add to layer if gameObject is not in any displayList
+ if (!gameObject.displayList) {
+ return this;
+ }
+
+ // Move gameObject from scene to layer
+ var layer = this.getRenderLayer();
+ if (!layer) {
+ return this;
+ }
+
+ if (gameObject.isRexContainerLite) {
+ // Add containerLite and its children
+ gameObject.addToLayer(layer);
+ } else {
+ // Add gameObject directly
+ layer.add(gameObject);
+ }
+
+ var state = GetLocalState(gameObject);
+ state.layer = layer;
+
+ return this;
+ },
+
+ // Internal method for removing child
+ removeFromRenderLayer(gameObject) {
+ // Move gameObject from layer to scene
+ var state = GetLocalState(gameObject);
+ var layer = state.layer;
+ if (!layer) {
+ return this;
+ }
+
+ layer.remove(gameObject);
+ state.layer = null;
+
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Mask.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Mask.js
new file mode 100644
index 000000000..b4abefe6c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Mask.js
@@ -0,0 +1,44 @@
+export default {
+ updateChildMask(child) {
+ // Don't propagate null mask to clear children's mask
+ if (this.mask == null) {
+ return this;
+ }
+
+ var maskGameObject = (this.mask.hasOwnProperty('geometryMask')) ? this.mask.geometryMask : this.mask.bitmapMask;
+ if (maskGameObject !== child) {
+ child.mask = this.mask;
+ }
+ return this;
+ },
+
+ syncMask() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildMask, this);
+ }
+ return this;
+ },
+
+ setMask(mask) {
+ this.mask = mask;
+ return this;
+ },
+
+ clearMask(destroyMask) {
+ if (destroyMask === undefined) {
+ destroyMask = false;
+ }
+
+ // Clear current mask
+ this._mask = null;
+ // Clear children mask
+ this.children.forEach(function (child) {
+ child.clearMask(false);
+ });
+
+ if (destroyMask && this.mask) {
+ this.mask.destroy();
+ }
+ return this;
+ },
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Methods.js
new file mode 100644
index 000000000..764aef551
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Methods.js
@@ -0,0 +1,52 @@
+import Parent from './Parent.js';
+import AddChild from './AddChild.js';
+import RemoveChild from './RemoveChild.js';
+import ChildState from './ChildState.js';
+import Transform from './Transform.js';
+import Position from './Position.js';
+import Rotation from './Rotation.js';
+import Scale from './Scale.js';
+import Visible from './Visible.js';
+import Alpha from './Alpha.js';
+import Active from './Active.js';
+import ScrollFactor from './ScrollFactor.js';
+import Mask from './Mask.js';
+import Depth from './Depth.js';
+import Children from './Children.js';
+import Tween from './Tween.js';
+import P3Container from './P3Container.js';
+import Layer from './Layer.js';
+import RenderTexture from './RenderTexture.js';
+
+import DrawBounds from './DrawBounds.js';
+import ChangeOrigin from './ChangeOrigin.js';
+
+var methods = {
+ changeOrigin: ChangeOrigin,
+ drawBounds: DrawBounds,
+};
+
+Object.assign(
+ methods,
+ Parent,
+ AddChild,
+ RemoveChild,
+ ChildState,
+ Transform,
+ Position,
+ Rotation,
+ Scale,
+ Visible,
+ Alpha,
+ Active,
+ ScrollFactor,
+ Mask,
+ Depth,
+ Children,
+ Tween,
+ P3Container,
+ Layer,
+ RenderTexture,
+);
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/P3Container.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/P3Container.js
new file mode 100644
index 000000000..99276872b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/P3Container.js
@@ -0,0 +1,86 @@
+import SortGameObjectsByDepth from '../../../utils/system/SortGameObjectsByDepth.js';
+
+export default {
+ addToContainer(p3Container) {
+ this._setParentContainerFlag = true;
+
+ var gameObjects = this.getAllChildren([this]);
+ SortGameObjectsByDepth(gameObjects);
+ p3Container.add(gameObjects);
+
+ this._setParentContainerFlag = false;
+ return this;
+ },
+
+ addToLayer(layer) {
+ this.addToContainer(layer);
+ return this;
+ },
+
+ removeFromContainer() {
+ if (!this.parentContainer) {
+ return this;
+ }
+
+ // Will add gameObjects to scene
+ var gameObjects = this.getAllChildren([this])
+ .filter(function (gameObject) {
+ return !!gameObject.scene;
+ });
+
+ if (gameObjects.length === 0) {
+ return this;
+ }
+
+ this._setParentContainerFlag = true;
+
+ if (gameObjects.length > 1) {
+ SortGameObjectsByDepth(gameObjects);
+ gameObjects.reverse();
+ }
+
+ this.parentContainer.remove(gameObjects);
+
+ this._setParentContainerFlag = false;
+ return this;
+ },
+
+ getParentContainer() {
+ if (this.parentContainer) {
+ return this.parentContainer;
+ }
+
+ // One of parent container has a layer
+ var parent = this.getParent();
+ while (parent) {
+ var p3Container = parent.parentContainer;
+ if (p3Container) {
+ return p3Container;
+ }
+ parent = parent.getParent();
+ }
+
+ return null;
+ },
+
+ addToParentContainer(gameObject) {
+ // Don't add to layer if gameObject is not in any displayList
+ if (!gameObject.displayList) {
+ return this;
+ }
+ var p3Container = this.getParentContainer();
+ if (!p3Container) {
+ return this;
+ }
+
+ if (gameObject.isRexContainerLite) {
+ // Add containerLite and its children
+ gameObject.addToContainer(p3Container);
+ } else {
+ // Add gameObject directly
+ p3Container.add(gameObject);
+ }
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Parent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Parent.js
new file mode 100644
index 000000000..dfecb677d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Parent.js
@@ -0,0 +1,37 @@
+import { GetParent, GetTopmostParent } from './GetParent.js';
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ setParent(gameObject, parent) {
+ if (parent === undefined) {
+ parent = this;
+ }
+ var localState = GetLocalState(gameObject);
+ if (parent) { // Add to parent
+ localState.parent = parent;
+ localState.self = gameObject;
+ } else { // Remove from parent
+ localState.parent = null;
+ localState.self = null;
+ }
+ return this;
+ },
+
+ getParent(gameObject, name) {
+ if (typeof (gameObject) === 'string') {
+ name = gameObject;
+ gameObject = undefined;
+ }
+ if (gameObject === undefined) {
+ gameObject = this;
+ }
+ return GetParent(gameObject, name);
+ },
+
+ getTopmostParent(gameObject) {
+ if (gameObject === undefined) {
+ gameObject = this;
+ }
+ return GetTopmostParent(gameObject);
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Position.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Position.js
new file mode 100644
index 000000000..917d2cb29
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Position.js
@@ -0,0 +1,77 @@
+import GetLocalState from './utils/GetLocalState.js';
+import GetScale from './utils/GetScale.js';
+
+export default {
+ updateChildPosition(child) {
+ if (child.isRexContainerLite) {
+ child.syncChildrenEnable = false;
+ }
+ var state = GetLocalState(child);
+ var parent = state.parent;
+
+ if (state.syncPosition) {
+ child.x = state.x;
+ child.y = state.y;
+ parent.localToWorld(child);
+ }
+
+ if (state.syncRotation) {
+ child.rotation = state.rotation + parent.rotation;
+ }
+
+ if (state.syncScale) {
+ child.scaleX = state.scaleX * parent.scaleX;
+ child.scaleY = state.scaleY * parent.scaleY;
+ }
+
+ if (child.isRexContainerLite) {
+ child.syncChildrenEnable = true;
+ child.syncPosition();
+ }
+ return this;
+ },
+
+ syncPosition() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildPosition, this);
+ }
+ return this;
+ },
+
+ resetChildPositionState(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ state.x = child.x;
+ state.y = child.y;
+ parent.worldToLocal(state);
+
+ state.scaleX = GetScale(child.scaleX, parent.scaleX);
+ state.scaleY = GetScale(child.scaleY, parent.scaleY);
+
+ state.rotation = child.rotation - parent.rotation;
+ return this;
+ },
+
+ setChildPosition(child, x, y) {
+ child.x = x;
+ child.y = y;
+ this.resetChildPositionState(child);
+ return this;
+ },
+
+ setChildLocalPosition(child, x, y) {
+ var state = GetLocalState(child);
+ state.x = x;
+ state.y = y;
+ this.updateChildPosition(child);
+ return this;
+ },
+
+ resetLocalPositionState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildPositionState(this);
+ }
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RemoveChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RemoveChild.js
new file mode 100644
index 000000000..b209a9d7c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RemoveChild.js
@@ -0,0 +1,51 @@
+import Base from './Base.js';
+import { GetParent } from './GetParent.js';
+
+const BaseRemove = Base.prototype.remove;
+const BaseClear = Base.prototype.clear;
+
+export default {
+ // Can override this method
+ remove(gameObject, destroyChild) {
+ if (GetParent(gameObject) !== this) {
+ return this;
+ }
+ this.setParent(gameObject, null);
+
+ if (!destroyChild) {
+ this.removeFromRenderLayer(gameObject);
+ }
+
+ BaseRemove.call(this, gameObject, destroyChild);
+ return this;
+ },
+
+ // Don't override this method
+ unpin(gameObject, destroyChild) {
+ if (GetParent(gameObject) !== this) {
+ return this;
+ }
+ this.setParent(gameObject, null);
+
+ if (!destroyChild) {
+ this.removeFromRenderLayer(gameObject);
+ }
+
+ BaseRemove.call(this, gameObject, destroyChild);
+ return this;
+ },
+
+ clear(destroyChild) {
+ var children = this.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ this.setParent(child, null);
+
+ if (!destroyChild) {
+ this.removeFromRenderLayer(child);
+ }
+ }
+ BaseClear.call(this, destroyChild);
+ return this;
+ },
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RenderTexture.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RenderTexture.js
new file mode 100644
index 000000000..d8a711e39
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/RenderTexture.js
@@ -0,0 +1,36 @@
+import Snapshot from '../../../utils/rendertexture/Snapshot.js';
+
+export default {
+ snapshot(config) {
+ // Save scale
+ var scaleXSave = this.scaleX;
+ var scaleYSave = this.scaleY;
+ var scale1 = (scaleXSave === 1) && (scaleYSave === 1);
+ if (!scale1) {
+ this.setScale(1);
+ }
+
+ // Snapshot with scale = 1
+ if (config === undefined) {
+ config = {};
+ }
+ config.gameObjects = this.getAllVisibleChildren();
+ config.x = this.x;
+ config.y = this.y;
+ config.originX = this.originX;
+ config.originY = this.originY;
+ var rt = Snapshot(config);
+ var isValidRT = !!rt.scene;
+
+ // Restore scale
+ if (!scale1) {
+ this.setScale(scaleXSave, scaleYSave);
+
+ if (isValidRT) {
+ rt.setScale(scaleXSave, scaleYSave);
+ }
+ }
+
+ return (isValidRT) ? rt : this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Rotation.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Rotation.js
new file mode 100644
index 000000000..29eaf2809
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Rotation.js
@@ -0,0 +1,62 @@
+import GetLocalState from './utils/GetLocalState.js';
+
+const DegToRad = Phaser.Math.DegToRad;
+
+export default {
+ updateChildRotation(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ if (state.syncRotation) {
+ child.rotation = parent.rotation + state.rotation;
+ }
+ return this;
+ },
+
+ syncRotation() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildRotation, this);
+ }
+ return this;
+ },
+
+ resetChildRotationState(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ state.rotation = child.rotation - parent.rotation;
+ return this;
+ },
+
+ setChildRotation(child, rotation) {
+ child.rotation = rotation;
+ this.resetChildRotationState(child);
+ return this;
+ },
+
+ setChildAngle(child, angle) {
+ child.angle = angle;
+ this.resetChildRotationState(child);
+ return this;
+ },
+
+ setChildLocalRotation(child, rotation) {
+ var state = GetLocalState(child);
+ state.rotation = rotation;
+ this.updateChildRotation(child);
+ return this;
+ },
+
+ setChildLocalAngle(child, angle) {
+ var state = GetLocalState(child);
+ state.rotation = DegToRad(angle);
+ this.updateChildRotation(child);
+ return this;
+ },
+
+ resetLocalRotationState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildRotationState(this);
+ }
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Scale.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Scale.js
new file mode 100644
index 000000000..83e472062
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Scale.js
@@ -0,0 +1,64 @@
+import GetLocalState from './utils/GetLocalState.js';
+import GetScale from './utils/GetScale.js';
+
+export default {
+ updateChildScale(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ if (state.syncScale) {
+ child.scaleX = parent.scaleX * state.scaleX;
+ child.scaleY = parent.scaleY * state.scaleY;
+ }
+ return this;
+ },
+
+ syncScale() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildScale, this);
+ }
+ return this;
+ },
+
+ resetChildScaleState(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+ state.scaleX = GetScale(child.scaleX, parent.scaleX);
+ state.scaleY = GetScale(child.scaleY, parent.scaleY);
+ return this;
+ },
+
+ setChildScale(child, scaleX, scaleY) {
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+ child.scaleX = scaleX;
+ child.scaleY = scaleY;
+ this.resetChildScaleState(child);
+ return this;
+ },
+
+ setChildLocalScale(child, scaleX, scaleY) {
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+ var state = GetLocalState(child);
+ state.scaleX = scaleX;
+ state.scaleY = scaleY;
+ this.updateChildScale(child);
+ return this;
+ },
+
+ setChildDisplaySize(child, width, height) {
+ child.setDisplaySize(width, height);
+ this.resetChildScaleState(child);
+ return this;
+ },
+
+ resetLocalScaleState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildScaleState(this);
+ }
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ScrollFactor.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ScrollFactor.js
new file mode 100644
index 000000000..6cf57330f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/ScrollFactor.js
@@ -0,0 +1,22 @@
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ updateChildScrollFactor(child) {
+ var state = GetLocalState(child);
+ var parent = state.parent;
+
+ if (state.syncScrollFactor) {
+ child.setScrollFactor(parent.scrollFactorX, parent.scrollFactorY);
+ }
+
+ return this;
+ },
+
+ syncScrollFactor() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildScrollFactor, this);
+ }
+ return this;
+ },
+
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Transform.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Transform.js
new file mode 100644
index 000000000..467e5f502
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Transform.js
@@ -0,0 +1,27 @@
+const RotateAround = Phaser.Math.RotateAround;
+
+export default {
+ worldToLocal(point) {
+ // Transform
+ point.x -= this.x;
+ point.y -= this.y;
+ // Rotate
+ RotateAround(point, 0, 0, -this.rotation);
+ // Scale
+ point.x /= this.scaleX;
+ point.y /= this.scaleY;
+ return point;
+ },
+
+ localToWorld(point) {
+ // Scale
+ point.x *= this.scaleX;
+ point.y *= this.scaleY;
+ // Rotate
+ RotateAround(point, 0, 0, this.rotation);
+ // Transform
+ point.x += this.x;
+ point.y += this.y;
+ return point;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Tween.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Tween.js
new file mode 100644
index 000000000..e1055972a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Tween.js
@@ -0,0 +1,118 @@
+var GetLocalStates = function (gameObjects) {
+ var localStates = []
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ var gameObject = gameObjects[i];
+ if (!gameObject.hasOwnProperty('rexContainer')) {
+ continue;
+ }
+ localStates.push(gameObject.rexContainer);
+ }
+ return localStates;
+}
+
+var GetScene = function (gameObjects) {
+ for (var i = 0, cnt = gameObjects.length; i < cnt; i++) {
+ var scene = gameObjects[i].scene;
+ if (scene) {
+ return scene;
+ }
+ }
+ return null;
+}
+
+var UpdateChild = function (tween, key, target) {
+ if (!target.parent) {
+ // target object was removed, so remove this tween too
+ tween.remove();
+ return;
+ }
+
+ var parent = target.parent;
+ var child = target.self;
+ switch (key) {
+ case 'x':
+ case 'y':
+ parent.updateChildPosition(child);
+ break;
+
+ case 'angle':
+ case 'rotation':
+ parent.updateChildRotation(child);
+ break;
+
+ case 'scaleX':
+ case 'scaleY':
+ case 'displayWidth':
+ case 'displayHeight':
+ parent.updateChildScale(child);
+ break;
+
+ case 'alpha':
+ parent.updateChildAlpha(child);
+ break;
+
+ default:
+ parent.updateChildPosition(child);
+ parent.updateChildRotation(child);
+ parent.updateChildScale(child);
+ parent.updateChildAlpha(child);
+ break;
+ }
+};
+
+export default {
+ tweenChild(tweenConfig) {
+ var targets = tweenConfig.targets;
+ if (!Array.isArray(targets)) {
+ targets = [targets];
+ }
+
+ var scene = this.scene || GetScene(targets);
+ if (!scene) {
+ return;
+ }
+
+ // Map child game objects to local states
+ tweenConfig.targets = GetLocalStates(targets);
+ var tween = scene.tweens.add(tweenConfig);
+
+ // Update child game object in 'update' event
+ tween.on('update', UpdateChild);
+
+ return tween;
+ },
+
+ tweenSelf(tweenConfig) {
+ tweenConfig.targets = [this];
+ return this.tweenChild(tweenConfig);
+ },
+
+ createTweenChildConfig(tweenConfig) {
+ var targets = tweenConfig.targets;
+ if (targets) {
+ if (!Array.isArray(targets)) {
+ targets = [targets];
+ }
+ // Map child game objects to local states
+ tweenConfig.targets = GetLocalStates(targets);
+ }
+
+ var onUpdate = tweenConfig.onUpdate;
+ tweenConfig.onUpdate = function (tween, target) {
+ if (onUpdate) {
+ onUpdate(tween, target);
+ }
+ UpdateChild(tween, undefined, target);
+ }
+
+ return tweenConfig;
+ },
+
+ tween(tweenConfig) {
+ var scene = this.scene;
+ if (!tweenConfig.targets) {
+ tweenConfig.targets = this;
+ }
+ return scene.tweens.add(tweenConfig);
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Visible.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Visible.js
new file mode 100644
index 000000000..3be4e85d3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/Visible.js
@@ -0,0 +1,77 @@
+/*
+
+Visible in localState:
+
+ - visible: original visible of child
+ - maskVisible: invisible by parent mask, see MaskChildren.js
+ - undefined (not in masking) : Equal to mask visible
+ - true (mask visible) : Inside, or across parent's visible area
+ - false (maske invisible) : Out of parent's visible area
+
+Visible result of child = (parent visible) && (child visible) && (mask visible)
+*/
+
+import GetLocalState from './utils/GetLocalState.js';
+
+export default {
+ updateChildVisible(child) {
+ var localState = GetLocalState(child);
+ var parent = localState.parent;
+ var maskVisible = (localState.hasOwnProperty('maskVisible')) ? localState.maskVisible : true;
+ child.visible = parent.visible && localState.visible && maskVisible;
+ return this;
+ },
+
+ syncVisible() {
+ if (this.syncChildrenEnable) {
+ this.children.forEach(this.updateChildVisible, this);
+ }
+ return this;
+ },
+
+ resetChildVisibleState(child) {
+ var localState = GetLocalState(child);
+ // Delete maskVisible property
+ if (localState.hasOwnProperty('maskVisible')) {
+ delete localState.maskVisible;
+ }
+ localState.visible = child.visible;
+ return this;
+ },
+
+ setChildVisible(child, visible) {
+ // Visible of child will be affect by parent's visible, and mask visible
+ this.setChildLocalVisible(child, visible);
+ return this;
+ },
+
+ // Internal method
+ setChildLocalVisible(child, visible) {
+ if (visible === undefined) {
+ visible = true;
+ }
+ var localState = GetLocalState(child);
+ localState.visible = visible;
+ this.updateChildVisible(child);
+ return this;
+ },
+
+ // Internal method
+ setChildMaskVisible(child, visible) {
+ if (visible === undefined) {
+ visible = true;
+ }
+ var localState = GetLocalState(child);
+ localState.maskVisible = visible;
+ this.updateChildVisible(child);
+ return this;
+ },
+
+ resetLocalVisibleState() {
+ var parent = GetLocalState(this).parent;
+ if (parent) {
+ parent.resetChildVisibleState(this);
+ }
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/AddChildMask.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/AddChildMask.js
new file mode 100644
index 000000000..87545d333
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/AddChildMask.js
@@ -0,0 +1,17 @@
+import DefaultMaskGraphics from '../../../../utils/mask/defaultmaskgraphics/DefaultMaskGraphics.js';
+
+var AddChildMask = function (maskTarget, sizeTarget, shape, padding) {
+ var maskGameObject = new DefaultMaskGraphics(sizeTarget, shape, padding); // A Graphics game object
+ if (maskTarget && !maskTarget.isRexSizer) { // Sizer game object can't apply mask
+ var mask = maskGameObject.createGeometryMask();
+ maskTarget.setMask(mask);
+ this.once('destroy', function () {
+ maskTarget.setMask();
+ mask.destroy();
+ })
+ }
+ this.pin(maskGameObject);
+ return maskGameObject;
+}
+
+export default AddChildMask;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/ChildrenMaskMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/ChildrenMaskMethods.js
new file mode 100644
index 000000000..6d3546cb8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/ChildrenMaskMethods.js
@@ -0,0 +1,112 @@
+import MaskChildren from './MaskChildren.js';
+import AddChildMask from './AddChildMask.js';
+import MaskToGameObject from '../../../../utils/mask/MaskToGameObject.js'
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+const MASKUPDATEMODE = {
+ update: 0,
+ everyTick: 1
+};
+
+export default {
+ setupChildrenMask(config) {
+ if (config === false) {
+ // No children mask
+ return this;
+ }
+
+ this.setMaskUpdateMode(GetValue(config, 'updateMode', 0));
+ this.enableChildrenMask(GetValue(config, 'padding', 0));
+ this.setMaskLayer(GetValue(config, 'layer', undefined));
+ this.startMaskUpdate();
+
+ return this;
+ },
+
+ destroyChildrenMask() {
+ if (!this.childrenMask) {
+ return this;
+ }
+
+ this.stopMaskUpdate();
+ this.childrenMask.destroy();
+ this.childrenMask = undefined;
+
+ return this;
+ },
+
+ setMaskUpdateMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = MASKUPDATEMODE[mode];
+ }
+ this.maskUpdateMode = mode;
+ return this;
+ },
+
+ startMaskUpdate() {
+ this.scene.game.events.on('poststep', this.maskChildren, this);
+ },
+
+ stopMaskUpdate() {
+ this.scene.game.events.off('poststep', this.maskChildren, this);
+ },
+
+ enableChildrenMask(maskPadding) {
+ var maskGameObject = AddChildMask.call(this, null, this, 0, maskPadding);
+ this.childrenMask = maskGameObject.createGeometryMask();
+ // this.childrenMask is a mask object, not a (Graphics) game object
+ return this;
+ },
+
+ setMaskChildrenFlag(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.maskChildrenFlag = value;
+ return this;
+ },
+
+ setMaskLayer(layer) {
+ // To reduce amount of masked game object
+ this.maskLayer = layer;
+ return this;
+ },
+
+ maskChildren() {
+ if (
+ (!this.childrenMask) || // No childrenMask
+ (!this.maskChildrenFlag) || // No maskChildrenFlag set
+ (this.alpha === 0) || (!this.visible) // Parent is not visible
+ ) {
+ return this;
+ }
+
+ if (this.privateRenderLayer) {
+ this.privateRenderLayer.setMask(this.childrenMask);
+ } else if (this.maskLayer) {
+ // 1. Add parent and children into layer
+ this.addToLayer(this.maskLayer);
+ // 2. Mask this layer
+ this.maskLayer.setMask(this.childrenMask);
+ } else {
+ MaskChildren(this, this.childrenMask);
+ }
+
+ if (this.maskUpdateMode === 0) {
+ this.maskChildrenFlag = false;
+ }
+ return this;
+ },
+
+ layoutChildrenMask() {
+ if (!this.childrenMask) {
+ return this;
+ }
+
+ var maskGameObject = MaskToGameObject(this.childrenMask);
+ maskGameObject.setPosition().resize();
+ this.resetChildPositionState(maskGameObject);
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/MaskChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/MaskChildren.js
new file mode 100644
index 000000000..677f54137
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/mask/MaskChildren.js
@@ -0,0 +1,113 @@
+import MaskToGameObject from '../../../../utils/mask/MaskToGameObject.js';
+
+const Intersects = Phaser.Geom.Intersects.RectangleToRectangle;
+const Overlaps = Phaser.Geom.Rectangle.Overlaps;
+
+var MaskChildren = function (parent, mask, children) {
+ if (!mask) {
+ return;
+ }
+
+ if (children === undefined) {
+ children = parent.getAllChildren();
+ }
+
+ var parentBounds = parent.getBounds();
+ var maskGameObject = MaskToGameObject(mask);
+
+ var child, childBounds, visiblePointsNumber;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ child = children[i];
+ if (child.hasOwnProperty('isRexContainerLite')) {
+ continue;
+ }
+ if (child === maskGameObject) {
+ continue;
+ }
+ if (!IsVisible(child)) { // Child is invisible before masking
+ continue;
+ }
+
+ if (child.getBounds) {
+ childBounds = child.getBounds(childBounds);
+ visiblePointsNumber = ContainsPoints(parentBounds, childBounds);
+ switch (visiblePointsNumber) {
+ case 4: // 4 points are all inside visible window, set visible
+ ShowAll(parent, child, mask);
+ break;
+ case 0: // No point is inside visible window
+ // Parent intersects with child, or parent is inside child, set visible, and apply mask
+ if (Intersects(parentBounds, childBounds) || Overlaps(parentBounds, childBounds)) {
+ ShowSome(parent, child, mask);
+ } else { // Set invisible
+ ShowNone(parent, child, mask);
+ }
+ break;
+ default: // Part of points are inside visible window, set visible, and apply mask
+ ShowSome(parent, child, mask);
+ break;
+ }
+ } else {
+ ShowSome(parent, child, mask);
+ }
+ }
+}
+
+var IsVisible = function (gameObject) {
+ if (!gameObject.displayList) {
+ return false;
+ }
+
+ while (1) {
+ var localState = gameObject.rexContainer;
+ if (!localState) { // Top game object
+ return gameObject.visible;
+ } else if (localState.visible) {
+ var parent = localState.parent;
+ if (parent) { // Test parent's visible
+ gameObject = parent;
+ continue;
+ } else { // Top visible game object
+ return true;
+ }
+ } else { // Current game object is invisible
+ return false;
+ }
+ }
+}
+
+var ContainsPoints = function (rectA, rectB) {
+ var result = 0;
+ var top = rectB.top,
+ bottom = rectB.bottom,
+ left = rectB.left,
+ right = rectB.right;
+ result += rectA.contains(left, top) ? 1 : 0;
+ result += rectA.contains(left, bottom) ? 1 : 0;
+ result += rectA.contains(right, top) ? 1 : 0;
+ result += rectA.contains(right, bottom) ? 1 : 0;
+ return result;
+};
+
+var ShowAll = function (parent, child, mask) {
+ parent.setChildMaskVisible(child, true);
+ if (child.clearMask) {
+ child.clearMask();
+ }
+}
+
+var ShowSome = function (parent, child, mask) {
+ parent.setChildMaskVisible(child, true);
+ if (child.setMask) {
+ child.setMask(mask);
+ }
+}
+
+var ShowNone = function (parent, child, mask) {
+ parent.setChildMaskVisible(child, false);
+ if (child.clearMask) {
+ child.clearMask();
+ }
+}
+
+export default MaskChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Enter.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Enter.js
new file mode 100644
index 000000000..0b067d0b2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Enter.js
@@ -0,0 +1,43 @@
+import Exit from './Exit.js';
+import Snapshot from '../../../../utils/rendertexture/Snapshot.js';
+
+var Enter = function (parentContainer, rtOwner) {
+ if (!parentContainer) {
+ return false;
+ }
+
+ Exit(parentContainer, rtOwner);
+
+ // Get and paste all visible children, which dose not include this render texture
+ var useParentBounds = rtOwner.useParentBounds;
+ Snapshot({
+ gameObjects: parentContainer.getAllVisibleChildren(),
+ renderTexture: rtOwner.rt,
+ x: rtOwner.x,
+ y: rtOwner.y,
+ width: ((useParentBounds) ? parentContainer.displayWidth : undefined),
+ height: ((useParentBounds) ? parentContainer.displayHeighth : undefined),
+ originX: ((useParentBounds) ? parentContainer.originX : undefined),
+ originY: ((useParentBounds) ? parentContainer.originY : undefined),
+ });
+
+ // Set rtOwner to be visible
+ parentContainer.setChildVisible(rtOwner, true);
+
+ // Set visible sibling to be invisible
+ var visibleSibling = rtOwner.visibleSibling;
+ var children = parentContainer.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if ((child.visible) && (child !== rtOwner)) {
+ parentContainer.setChildVisible(child, false);
+ visibleSibling.push(child);
+ }
+ }
+
+ rtOwner.isRunning = true;
+
+ return true;
+}
+
+export default Enter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Exit.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Exit.js
new file mode 100644
index 000000000..1bde7e46b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Exit.js
@@ -0,0 +1,21 @@
+var Exit = function (parentContainer, rtOwner) {
+ if (!parentContainer) {
+ return false;
+ }
+
+ var visibleSibling = rtOwner.visibleSibling;
+ // Set all visible children back
+ for (var i = 0, cnt = visibleSibling.length; i < cnt; i++) {
+ parentContainer.setChildVisible(visibleSibling[i], true);
+ }
+ visibleSibling.length = 0;
+
+ // Set rtOwner to be invisible
+ parentContainer.setChildVisible(rtOwner, false);
+
+ rtOwner.isRunning = false;
+
+ return true;
+}
+
+export default Exit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Init.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Init.js
new file mode 100644
index 000000000..6da49484a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/Init.js
@@ -0,0 +1,14 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var Init = function (parentContainer, rtOwner, config) {
+ rtOwner.visibleSibling = [];
+ rtOwner.isRunning = false;
+ rtOwner.useParentBounds = GetValue(config, 'useParentBounds', false);
+
+ rtOwner
+ .setPosition(parentContainer.x, parentContainer.y)
+ .setVisible(false)
+ parentContainer.pin(rtOwner);
+}
+
+export default Init;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/MeshRenderTextureBase.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/MeshRenderTextureBase.js
new file mode 100644
index 000000000..c3007d8e4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/rendertexture/MeshRenderTextureBase.js
@@ -0,0 +1,40 @@
+import Init from './Init.js';
+import Enter from './Enter.js';
+import Exit from './Exit.js';
+
+var MeshRenderTextureBase = function (RenderTextureOwnerClass) {
+ return class Base extends RenderTextureOwnerClass {
+ constructor(parentContainer, config) {
+ var scene = parentContainer.scene;
+ super(scene, 0, 0, 1, 1, config);
+ scene.add.existing(this);
+
+ Init(parentContainer, this, config);
+ }
+
+ destroy(fromScene) {
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ this.exit();
+ super.destroy(fromScene);
+ }
+
+ enter() {
+ var result = Enter(this.rexContainer.parent, this);
+ if (result) {
+ this.syncSize();
+ }
+
+ return this;
+ }
+
+ exit() {
+ Exit(this.rexContainer.parent, this);
+ return this;
+ }
+ }
+}
+
+export default MeshRenderTextureBase;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetLocalState.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetLocalState.js
new file mode 100644
index 000000000..7311f4bc2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetLocalState.js
@@ -0,0 +1,46 @@
+const DegToRad = Phaser.Math.DegToRad;
+const RadToDeg = Phaser.Math.RadToDeg;
+
+var GetLocalState = function (gameObject) {
+ if (!gameObject.hasOwnProperty('rexContainer')) {
+ var rexContainer = {
+ parent: null, self: null, layer: null,
+ x: 0, y: 0, syncPosition: true,
+ rotation: 0, syncRotation: true,
+ scaleX: 0, scaleY: 0, syncScale: true,
+ alpha: 0, syncAlpha: true,
+ visible: true,
+ active: true,
+ };
+
+ Object.defineProperty(rexContainer, 'angle', {
+ get: function () {
+ return RadToDeg(this.rotation);
+ },
+ set: function (value) {
+ this.rotation = DegToRad(value);
+ }
+ });
+ Object.defineProperty(rexContainer, 'displayWidth', {
+ get: function () {
+ return gameObject.width * this.scaleX;
+ },
+ set: function (width) {
+ this.scaleX = width / gameObject.width;
+ }
+ });
+ Object.defineProperty(rexContainer, 'displayHeight', {
+ get: function () {
+ return gameObject.height * this.scaleY;
+ },
+ set: function (height) {
+ this.scaleY = height / gameObject.height;
+ }
+ });
+
+ gameObject.rexContainer = rexContainer;
+ }
+ return gameObject.rexContainer;
+}
+
+export default GetLocalState;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetScale.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetScale.js
new file mode 100644
index 000000000..e14e6b376
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/GetScale.js
@@ -0,0 +1,9 @@
+var GetScale = function (a, b) {
+ if (a === b) {
+ return 1;
+ } else {
+ return a / b;
+ }
+}
+
+export default GetScale;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/Traversal.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/Traversal.js
new file mode 100644
index 000000000..fc1c8b446
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/containerlite/utils/Traversal.js
@@ -0,0 +1,26 @@
+var DepthFirstSearch = function (root, callback) {
+ var skip = callback(root);
+ if ((!skip) && root.isRexContainerLite) {
+ var children = root.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ DepthFirstSearch(children[i], callback);
+ }
+ }
+}
+
+var BreadthFirstSearch = function (root, callback) {
+ var queue = [root];
+ while (queue.length > 0) {
+ var current = queue.shift();
+ var skip = callback(current);
+
+ if ((!skip) && current.isRexContainerLite) {
+ queue.push(...current.children);
+ }
+ }
+}
+
+export {
+ DepthFirstSearch,
+ BreadthFirstSearch
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Creator.js
new file mode 100644
index 000000000..68a711cce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Creator.js
@@ -0,0 +1,23 @@
+import GridTable from './GridTable.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetValue(config, 'width', 256);
+ var height = GetValue(config, 'height', 256);
+ var gameObject = new GridTable(this.scene, 0, 0, width, height, config);
+
+ // set properties wo modify children
+ gameObject.syncChildrenEnable = false;
+ BuildGameObject(this.scene, gameObject, config);
+ // sync properties of children
+ gameObject.syncChildrenEnable = true;
+ gameObject.syncPosition().syncVisible().syncAlpha();
+
+ return gameObject;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Factory.js
new file mode 100644
index 000000000..3fcd8d80e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/Factory.js
@@ -0,0 +1,7 @@
+import GridTable from './GridTable.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new GridTable(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.d.ts
new file mode 100644
index 000000000..998db3cc3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.d.ts
@@ -0,0 +1,148 @@
+import ContainerLite from '../../container/containerlite/ContainerLite';
+
+export default GridTable;
+
+declare namespace GridTable {
+
+ type ScrollModeType = 0 | 1 | 'v' | 'vertical' | 'h' | 'horizontal';
+
+ interface CellData {
+ scene: Phaser.Scene,
+ width: number,
+ height: number,
+ deltaWidth: number,
+ deltaHeight: number,
+ item: unknown,
+ index: number,
+
+ setHeight(value: number): void,
+ setDeltaHeight(value: number): void,
+ setWidth(value: number): void,
+ setDeltaWidth(value: number): void,
+
+ setContainer(cellContainer?: Phaser.GameObjects.GameObject | null): void,
+ getContainer(): Phaser.GameObjects.GameObject | null,
+ popContainer(): Phaser.GameObjects.GameObject | null,
+ destroyContainer(): this,
+ }
+
+ type CellVisibleCallbackType = (
+ cell: CellData,
+ cellContainer: Phaser.GameObjects.GameObject | null,
+ table: GridTable
+ ) => void;
+
+ type CellInvisibleCallbackType = (
+ cell: CellData
+ ) => void;
+
+ type MaskUpdateModeType = 0 | 1 | 'update' | 'everyTick';
+ type MaskConfig = {
+ padding?: number,
+ updateMode?: MaskUpdateModeType
+ } |
+ boolean;
+
+
+ interface IConfig {
+ cellsCount?: number,
+ columns?: number,
+ cellHeight?: number,
+ cellWidth?: number,
+
+ cellVisibleCallback: CellVisibleCallbackType,
+ cellVisibleCallbackScope?: Object,
+ reuseCellContainer?: boolean,
+
+ cellInvisibleCallback: CellInvisibleCallbackType,
+ cellInvisibleCallbackScope: undefined,
+
+ clamplTableOXY?: boolean,
+ scrollMode?: ScrollModeType,
+ mask?: MaskConfig,
+ enableLayer?: boolean,
+ }
+
+ namespace Events {
+ type CellvisibleCallbackType = (
+ cell: CellData,
+ cellContainer: Phaser.GameObjects.GameObject | null,
+ table: GridTable
+ ) => void;
+
+ type CellInvisibleCallbackType = (cell: CellData) => void;
+
+ type CellHeightchange = (
+ cell: CellData,
+ cellContainer: Phaser.GameObjects.GameObject | null,
+ table: GridTable
+ ) => void;
+
+ type CellWidthchange = (
+ cell: CellData,
+ cellContainer: Phaser.GameObjects.GameObject | null,
+ table: GridTable
+ ) => void;
+ }
+}
+
+declare class GridTable extends ContainerLite {
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config: GridTable.IConfig
+ );
+
+ resize(width: number, height: number): this;
+
+ setTableOY(oy: number): this;
+ addTableOY(dy: number): this;
+ setTableOX(ox: number): this;
+ addTableOX(dx: number): this;
+ setTableOXY(ox: number, oy: number): this;
+ addTableOXY(dx: number, dy: number): this;
+ tableOY: number;
+ tableOX: number;
+
+ setTableOYByPercentage(t: number): this;
+ t: number;
+ getTableOYPercentage(): number;
+ scrollToBottom(): this;
+
+ scrollToRow(rowIndex: number): this;
+ scrollToNextRow(rowCount?: number): this;
+ startRowIndex: number;
+
+ updateTable(refresh?: boolean): this;
+ updateVisibleCell(cellIdx: number): this;
+
+ setGridSize(colCount: number, rowCount: number): this;
+ setCellsCount(count: number): this;
+ readonly cellsCount: number;
+ readonly columnCount: number;
+
+ readonly tableHeight: number;
+ readonly tableWidth: number;
+
+ readonly topTableOY: number;
+ readonly bottomTableOY: number;
+ readonly leftTableOX: number;
+ readonly rightTableOX: number;
+
+ getCell(cellIndex: number): GridTable.CellData;
+
+ pointToCellIndex(x: number, y: number): number;
+
+ setCellHeight(cellIndex: number, cellHeight: number): this;
+ setCellWidth(cellIndex: number, cellWidth: number): this;
+
+ iterateVisibleCell(
+ callback: (cell: GridTable.CellData) => void
+ ): this;
+
+ eachVisibleCell(
+ callback: (cell: GridTable.CellData) => void
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.js
new file mode 100644
index 000000000..0a65b9d2c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/GridTable.js
@@ -0,0 +1,369 @@
+import ContainerLite from '../../container/containerlite/ContainerLite.js';
+import Table from './table/Table.js';
+import Methods from './methods/Methods.js';
+
+const Group = Phaser.GameObjects.Group;
+const Set = Phaser.Structs.Set;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class GridTable extends ContainerLite {
+ constructor(scene, x, y, width, height, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ super(scene, x, y, width, height);
+ this.type = 'rexGridTable';
+ this._tableOX = 0;
+ this._tableOY = 0;
+ this.visibleCells = new Set();
+ this.preVisibleCells = new Set();
+ this.execeedTopState = false;
+ this.execeedBottomState = false;
+ this.execeedLeftState = false;
+ this.execeedRightState = false;
+
+ var reuseCellContainer = GetValue(config, 'reuseCellContainer', false);
+ if (reuseCellContainer) {
+ this.cellContainersPool = new Group(scene); // Don't add Group into update list, I will destroy it manually
+ }
+
+ var callback = GetValue(config, 'cellVisibleCallback', null);
+ if (callback !== null) {
+ var scope = GetValue(config, 'cellVisibleCallbackScope', undefined);
+ this.on('cellvisible', callback, scope);
+ }
+ callback = GetValue(config, 'cellInvisibleCallback', null);
+ if (callback !== null) {
+ var scope = GetValue(config, 'cellInvisibleCallbackScope', undefined);
+ this.on('cellinvisible', callback, scope);
+ }
+
+ if (GetValue(config, 'enableLayer', false)) {
+ this.enableLayer();
+ }
+
+ this.setupChildrenMask(GetValue(config, 'mask', undefined));
+
+ this.setScrollMode(GetValue(config, 'scrollMode', 0));
+ this.setClampMode(GetValue(config, 'clamplTableOXY', true));
+
+ // Pre-process cell size
+ var cellWidth, cellHeight, columns;
+ var scrollY = (this.scrollMode === 0);
+ if (scrollY) { // scroll y
+ cellWidth = config.cellWidth;
+ cellHeight = config.cellHeight;
+ columns = config.columns;
+ } else { // scroll x
+ cellWidth = config.cellHeight;
+ cellHeight = config.cellWidth;
+ columns = GetValue(config, 'rows', config.columns);
+ }
+ if (!columns) {
+ columns = 1; // Default columns
+ }
+ this.expandCellSize = (cellWidth === undefined);
+ if (this.expandCellSize) {
+ var width = (scrollY) ? this.width : this.height;
+ cellWidth = width / columns;
+ }
+
+ config.cellWidth = cellWidth;
+ config.cellHeight = cellHeight;
+ config.columns = columns;
+
+ this.table = new Table(this, config);
+
+ this.updateTable();
+ }
+
+ destroy(fromScene) { // preDestroy method does not have fromScene parameter
+ // This Game Object has already been destroyed
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ this.destroyChildrenMask();
+
+ this.table.destroy(fromScene);
+ this.table = undefined;
+ if (this.cellContainersPool) {
+ this.cellContainersPool.destroy(true);
+ this.cellContainersPool = undefined;
+ }
+
+ super.destroy(fromScene);
+ }
+
+ setScrollMode(mode) {
+ if (typeof (mode) === 'string') {
+ mode = SCROLLMODE[mode.toLowerCase()];
+ }
+ this.scrollMode = mode;
+ return this;
+ }
+
+ setClampMode(mode) {
+ if (mode === undefined) {
+ mode = true;
+ }
+ this.clampTableOXY = mode;
+ return this;
+ }
+
+ get tableOY() {
+ return this._tableOY;
+ }
+
+ get tableOX() {
+ return this._tableOX;
+ }
+
+ set tableOY(oy) {
+ this.setTableOY(oy).updateTable();
+ }
+
+ set tableOX(ox) {
+ this.setTableOX(ox).updateTable();
+ }
+
+ setTableOXY(ox, oy) {
+ this.setTableOY(oy).setTableOX(ox);
+ return this;
+ }
+
+ addTableOY(dy) {
+ this.setTableOY(this.tableOY + dy);
+ return this;
+ }
+
+ addTableOX(dx) {
+ this.setTableOX(this.tableOX + dx);
+ return this;
+ }
+
+ addTableOXY(dx, dy) {
+ this.addTableOY(dy).addTableOX(dx);
+ return this;
+ }
+
+ setTableOYByPercentage(percentage) {
+ this.setTableOY(-this.tableVisibleHeight * percentage);
+ return this;
+ }
+
+ getTableOYPercentage() {
+ var tableVisibleHeight = this.tableVisibleHeight;
+ if (tableVisibleHeight === 0) {
+ return 0;
+ }
+ return (this.tableOY / -tableVisibleHeight);
+ }
+
+ set t(value) {
+ this.setTableOYByPercentage(value).updateTable();
+ }
+
+ get t() {
+ return this.getTableOYPercentage();
+ }
+
+ scrollToBottom() {
+ this.t = 1;
+ // t will be 0 if table does not exceed visible area
+ if (this.t === 0) {
+ return this;
+ }
+
+ // Table height might be expanded while cells are visible
+ do {
+ this.t = 1;
+ } while (this.t !== 1)
+
+ return this;
+ }
+
+ scrollToRow(rowIndex) {
+ // To get all height of cells
+ this.scrollToBottom();
+
+ var height = this.table.rowIndexToHeight(0, rowIndex - 1)
+ this.setTableOY(-height).updateTable();
+ return this;
+ }
+
+ scrollToNextRow(rowCount) {
+ if (rowCount === undefined) {
+ rowCount = 1;
+ }
+ this.scrollToRow(this.startRowIndex + rowCount);
+ return this;
+ }
+
+ getCell(cellIdx) {
+ return this.table.getCell(cellIdx, true);
+ }
+
+ getCellContainer(cellIdx) {
+ var cell = this.table.getCell(cellIdx, false);
+ var container;
+ if (cell) {
+ container = cell.getContainer();
+ }
+ return container;
+ }
+
+ get cellsCount() {
+ return this.table.cellsCount;
+ }
+
+ get columnCount() {
+ return this.table.colCount;
+ }
+
+ setCellHeight(cellIdx, height) {
+ var cell;
+ if (typeof (cellIdx) === 'number') {
+ cell = this.table.getCell(cellIdx, true);
+ } else {
+ cell = cellIdx;
+ }
+ cell.height = height; // Only worked when scrollMode is 0
+ return this;
+ }
+
+ setCellWidth(cellIdx, width) {
+ var cell;
+ if (typeof (cellIdx) === 'number') {
+ cell = this.table.getCell(cellIdx, true);
+ } else {
+ cell = cellIdx;
+ }
+ cell.width = width; // Only worked when scrollMode is 1
+ return this;
+ }
+
+ get instHeight() {
+ return (this.scrollMode === 0) ? this.height : this.width;
+ }
+
+ get instWidth() {
+ return (this.scrollMode === 0) ? this.width : this.height;
+ }
+
+ get tableHeight() {
+ return this.table.totalRowsHeight;
+ }
+
+ get tableWidth() {
+ return this.table.totalColumnWidth;
+ }
+
+ get topTableOY() {
+ return 0;
+ }
+
+ get bottomTableOY() {
+ return -this.tableVisibleHeight;
+ }
+
+ get leftTableOX() {
+ return 0;
+ }
+
+ get rightTableOX() {
+ return -this.tableVisibleWidth;
+ }
+
+ get tableVisibleHeight() {
+ var h = this.tableHeight - this.instHeight;
+ if (h < 0) {
+ h = 0;
+ }
+ return h;
+ }
+
+ get tableVisibleWidth() {
+ var w;
+ var tableWidth = this.tableWidth;
+ var instWidth = this.instWidth;
+ if (tableWidth > instWidth) {
+ w = tableWidth - instWidth;
+ } else {
+ w = 0;
+ }
+ return w;
+ };
+
+ get bottomLeftY() {
+ return -(this.displayHeight * this.originY) + this.displayHeight;
+ }
+
+ get topRightX() {
+ return -(this.displayWidth * this.originX) + this.displayWidth;
+ }
+
+ get topLeftX() {
+ return -(this.displayWidth * this.originX);
+ }
+
+ get topLeftY() {
+ return -(this.displayHeight * this.originY)
+ }
+
+ get bottomBound() {
+ if (this.scrollMode === 0) {
+ return this.bottomLeftY;
+ } else {
+ return this.topRightX;
+ }
+ }
+
+ get rightBound() {
+ if (this.scrollMode === 0) {
+ return this.topRightX;
+ } else {
+ return this.bottomLeftY;
+ }
+ }
+
+ resize(width, height) {
+ if ((this.width === width) && (this.height === height)) {
+ return this;
+ }
+
+ super.resize(width, height);
+
+ if (this.expandCellSize) {
+ this.table.setDefaultCellWidth(this.instWidth / this.table.colCount);
+ }
+ this.updateTable(true);
+
+ // Layout children-mask
+ this.layoutChildrenMask();
+ // Re-mask children
+ this.maskChildren();
+
+ return this;
+ }
+};
+
+// mixin
+Object.assign(
+ GridTable.prototype,
+ Methods
+);
+
+const SCROLLMODE = {
+ v: 0,
+ vertical: 0,
+ h: 1,
+ horizontal: 1
+};
+
+const MASKUPDATEMODE = {
+ update: 0,
+ everyTick: 1
+};
+
+export default GridTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/EachCell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/EachCell.js
new file mode 100644
index 000000000..78be8fc83
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/EachCell.js
@@ -0,0 +1,23 @@
+// For when you know this Set will be modified during the iteration
+var EachVisibleCell = function (callback, scope) {
+ this.visibleCells.each(callback, scope);
+ return this;
+}
+
+// For when you absolutely know this Set won't be modified during the iteration
+var IterateVisibleCell = function (callback, scope) {
+ this.visibleCells.iterate(callback, scope);
+ return this;
+}
+
+var EachCell = function (callback, scope) {
+ this.table.cells.slice().forEach(callback, scope);
+ return this;
+}
+
+var IterateCell = function (callback, scope) {
+ this.table.cells.forEach(callback, scope);
+ return this;
+}
+
+export { EachVisibleCell, IterateVisibleCell, EachCell, IterateCell };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/InsertNewCells.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/InsertNewCells.js
new file mode 100644
index 000000000..30173ad7f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/InsertNewCells.js
@@ -0,0 +1,18 @@
+const Clamp = Phaser.Math.Clamp;
+
+var InsertNewCells = function (cellIdx, count) {
+ if (typeof (cellIdx) === 'object') {
+ cellIdx = cellIdx.index;
+ }
+ if (count === undefined) {
+ count = 1;
+ }
+ if (count <= 0) {
+ return this;
+ }
+ cellIdx = Clamp(cellIdx, 0, this.cellsCount);
+ this.table.insertNewCells(cellIdx, count);
+ return this;
+}
+
+export default InsertNewCells;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/IsCellVisible.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/IsCellVisible.js
new file mode 100644
index 000000000..2d26aa2d9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/IsCellVisible.js
@@ -0,0 +1,6 @@
+var IsCellVisible = function (cellIdx) {
+ var cell = this.table.getCell(cellIdx, false);
+ return cell && this.visibleCells.contains(cell);
+}
+
+export default IsCellVisible;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/Methods.js
new file mode 100644
index 000000000..f4f98638d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/Methods.js
@@ -0,0 +1,57 @@
+import SetTableOY from './SetTableOY.js';
+import SetTableOX from './SetTableOX.js';
+import ChildrenMaskMethods from '../../../container/containerlite/mask/ChildrenMaskMethods.js';
+
+import ShowCells from './updatetable/ShowCells.js';
+import ShowCell from './updatetable/ShowCell.js';
+import GetCellTLX from './updatetable/GetCellTLX.js';
+import GetCellTLY from './updatetable/GetCellTLY.js';
+import HideCells from './updatetable/HideCells.js';
+import HideCell from './updatetable/HideCell.js';
+import UpdateTable from './updatetable/UpdateTable.js';
+
+import IsCellVisible from './IsCellVisible.js';
+import { PointToCellIndex, PointToCellContainer } from './PointToCell.js';
+import { EachVisibleCell, IterateVisibleCell, EachCell, IterateCell } from './EachCell.js';
+
+import SetCellsCount from './SetCellsCount.js';
+import InsertNewCells from './InsertNewCells.js';
+import RemoveCells from './RemoveCells.js';
+import SetColumnCount from './SetColumnCount.js';
+import SetGridSize from './SetGridSize.js';
+import UpdateVisibleCell from './UpdateVisibleCell';
+
+var methods = {
+ setTableOY: SetTableOY,
+ setTableOX: SetTableOX,
+
+ showCells: ShowCells,
+ showCell: ShowCell,
+ getCellTLX: GetCellTLX,
+ getCellTLY: GetCellTLY,
+ hideCells: HideCells,
+ hideCell: HideCell,
+ updateTable: UpdateTable,
+
+ isCellVisible: IsCellVisible,
+ pointToCellIndex: PointToCellIndex,
+ pointToCellContainer: PointToCellContainer,
+ eachVisibleCell: EachVisibleCell,
+ iterateVisibleCell: IterateVisibleCell,
+ eachCell: EachCell,
+ iterateCell: IterateCell,
+
+ setCellsCount: SetCellsCount,
+ insertNewCells: InsertNewCells,
+ removeCells: RemoveCells,
+ setColumnCount: SetColumnCount,
+ setGridSize: SetGridSize,
+ updateVisibleCell: UpdateVisibleCell
+}
+
+Object.assign(
+ methods,
+ ChildrenMaskMethods
+);
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/PointToCell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/PointToCell.js
new file mode 100644
index 000000000..9de289b5f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/PointToCell.js
@@ -0,0 +1,28 @@
+var PointToCellIndex = function (x, y) {
+ y -= (this.y + this.topLeftY);
+ x -= (this.x + this.topLeftX);
+ var offsetTableOY = this.tableOY - ((this.scrollMode === 0) ? y : x);
+ var offsetTableOX = this.tableOX - ((this.scrollMode === 0) ? x : y);
+
+ var table = this.table;
+ var rowIdx = table.heightToRowIndex(-offsetTableOY, 0);
+ var colIdx = table.widthToColIndex(-offsetTableOX);
+ var cellIdx = table.colRowToCellIndex(colIdx, rowIdx);
+ if (cellIdx === null) {
+ return null;
+ }
+ if (!this.isCellVisible(cellIdx)) {
+ return null;
+ }
+ return cellIdx;
+}
+
+var PointToCellContainer = function (x, y) {
+ var cellIdx = PointToCellIndex.call(this, x, y);
+ if (cellIdx === null) {
+ return undefined;
+ }
+ return this.getCellContainer(cellIdx);
+}
+
+export { PointToCellIndex, PointToCellContainer };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/RemoveCells.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/RemoveCells.js
new file mode 100644
index 000000000..1df03e95c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/RemoveCells.js
@@ -0,0 +1,38 @@
+import HideCell from './updatetable/HideCell.js';
+
+var RemoveCells = function (cellIdx, count) {
+ if (typeof (cellIdx) === 'object') {
+ cellIdx = cellIdx.index;
+ }
+ if (count === undefined) {
+ count = 1;
+ }
+ if (cellIdx < 0) {
+ count += cellIdx;
+ cellIdx = 0;
+ }
+ if (count <= 0) {
+ return this;
+ }
+ // out-of-range
+ if (cellIdx > this.cellsCount) {
+ return this;
+ }
+
+ var cell;
+ for (var i = cellIdx, endIdx = cellIdx + count; i < endIdx; i++) {
+ cell = this.getCell(i, false);
+ if (cell) {
+ if (this.visibleCells.contains(cell)) {
+ HideCell.call(this, cell);
+ this.visibleCells.delete(cell);
+ }
+ this.preVisibleCells.delete(cell);
+ }
+ }
+
+ this.table.removeCells(cellIdx, count);
+ return this;
+}
+
+export default RemoveCells;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetCellsCount.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetCellsCount.js
new file mode 100644
index 000000000..7bf46c207
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetCellsCount.js
@@ -0,0 +1,15 @@
+var SetCellsCount = function (count) {
+ var cellsCount = this.cellsCount;
+ if (cellsCount === count) {
+ return this;
+ }
+
+ if (cellsCount > count) {
+ this.removeCells(count, cellsCount - count);
+ } else { // cellsCount < count
+ this.insertNewCells(cellsCount, count - cellsCount);
+ }
+ return this;
+}
+
+export default SetCellsCount;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetColumnCount.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetColumnCount.js
new file mode 100644
index 000000000..a7e100f9a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetColumnCount.js
@@ -0,0 +1,9 @@
+var SetColumnCount = function (count) {
+ if (this.table.colCount === count) {
+ return this;
+ }
+ this.table.setColumnCount(count);
+ return this;
+}
+
+export default SetColumnCount;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetGridSize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetGridSize.js
new file mode 100644
index 000000000..d3dcc979d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetGridSize.js
@@ -0,0 +1,7 @@
+var SetGridSize = function (colCount, rowCount) {
+ this.setCellsCount(colCount * rowCount);
+ this.table.setColumnCount(colCount);
+ return this;
+}
+
+export default SetGridSize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOX.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOX.js
new file mode 100644
index 000000000..cdedc5002
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOX.js
@@ -0,0 +1,43 @@
+var SetTableOX = function (ox) {
+ var table = this.table;
+ var leftTableOX = this.leftTableOX;
+ var rightTableOX = this.rightTableOX;
+ var tableOXExeceedLeft = (ox > this.leftTableOX);
+ var tableOXExeceedRight = (ox < this.rightTableOX);
+ if (this.clampTableOXY) {
+ var colCount = table.colCount;
+ var visibleColCount = table.widthToColIndex(this.instWidth, true);
+
+ // less then 1 page
+ if (colCount < visibleColCount) {
+ ox = 0;
+ } else if (tableOXExeceedLeft) {
+ ox = leftTableOX
+ } else {
+ // var tableVisibleWidth = this.tableVisibleWidth;
+ if (tableOXExeceedRight)
+ ox = rightTableOX;
+ }
+ }
+
+ if (this._tableOX !== ox) {
+ this._tableOX = ox;
+ }
+
+ if (tableOXExeceedLeft) {
+ if (!this.execeedLeftState) {
+ this.emit('execeedleft', this, ox, leftTableOX);
+ }
+ }
+ this.execeedLeftState = tableOXExeceedLeft;
+
+ if (tableOXExeceedRight) {
+ if (!this.execeedRightState) {
+ this.emit('execeedright', this, ox, rightTableOX);
+ }
+ }
+ this.execeedRightState = tableOXExeceedRight;
+ return this;
+}
+
+export default SetTableOX;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOY.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOY.js
new file mode 100644
index 000000000..372889792
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/SetTableOY.js
@@ -0,0 +1,42 @@
+var SetTableOY = function (oy) {
+ var table = this.table;
+ var topTableOY = this.topTableOY;
+ var bottomTableOY = this.bottomTableOY;
+ var tableOYExceedTop = (oy > this.topTableOY);
+ var tableOYExeceedBottom = (oy < this.bottomTableOY);
+ if (this.clampTableOXY) {
+ var rowCount = table.rowCount;
+ var visibleRowCount = table.heightToRowIndex(this.instHeight, 1);
+
+ // less then 1 page
+ if (rowCount < visibleRowCount) {
+ oy = 0;
+ } else if (tableOYExceedTop) {
+ oy = topTableOY
+ } else if (tableOYExeceedBottom) {
+ oy = bottomTableOY;
+ }
+ }
+
+ if (this._tableOY !== oy) {
+ this._tableOY = oy;
+ }
+
+
+ if (tableOYExceedTop) {
+ if (!this.execeedTopState) {
+ this.emit('execeedtop', this, oy, topTableOY);
+ }
+ }
+ this.execeedTopState = tableOYExceedTop;
+
+ if (tableOYExeceedBottom) {
+ if (!this.execeedBottomState) {
+ this.emit('execeedbottom', this, oy, bottomTableOY);
+ }
+ }
+ this.execeedBottomState = tableOYExeceedBottom;
+ return this;
+}
+
+export default SetTableOY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/UpdateVisibleCell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/UpdateVisibleCell.js
new file mode 100644
index 000000000..45bafb0fc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/UpdateVisibleCell.js
@@ -0,0 +1,14 @@
+import ShowCell from './updatetable/ShowCell';
+
+var UpdateVisibleCell = function (cellIdx) {
+ var cell = this.table.getCell(cellIdx, false);
+ if (!cell || !cell.container) {
+ return this;
+ }
+
+ ShowCell.call(this, cell);
+
+ return this;
+}
+
+export default UpdateVisibleCell;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLX.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLX.js
new file mode 100644
index 000000000..c9102b1ee
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLX.js
@@ -0,0 +1,7 @@
+var GetCellTLX = function (colIdx) {
+ var ox = (this.scrollMode === 0) ? this.topLeftX : this.topLeftY;
+ var x = this.tableOX + this.table.colIndexToWidth(0, colIdx - 1) + ox;
+ return x;
+}
+
+export default GetCellTLX;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLY.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLY.js
new file mode 100644
index 000000000..30723b5c0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/GetCellTLY.js
@@ -0,0 +1,7 @@
+var GetCellTLY = function (rowIdx) {
+ var oy = (this.scrollMode === 0) ? this.topLeftY : this.topLeftX;
+ var y = this.tableOY + this.table.rowIndexToHeight(0, rowIdx - 1) + oy;
+ return y;
+}
+
+export default GetCellTLY;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCell.js
new file mode 100644
index 000000000..70be4a48d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCell.js
@@ -0,0 +1,15 @@
+var HideCell = function (cell) {
+ // Option: pop container of cell by cell.popContainer() under this event
+ this.emit('cellinvisible', cell);
+
+ if (this.cellContainersPool) {
+ var cellContainer = cell.popContainer(); // null if already been removed
+ if (cellContainer) {
+ this.cellContainersPool.killAndHide(cellContainer);
+ }
+ }
+
+ cell.destroyContainer(); // Destroy container of cell
+}
+
+export default HideCell;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCells.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCells.js
new file mode 100644
index 000000000..d8a4f6f27
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/HideCells.js
@@ -0,0 +1,11 @@
+var HideCells = function () {
+ var preList = this.preVisibleCells;
+ var curList = this.visibleCells;
+ preList.iterate(function (cell) {
+ if (!curList.contains(cell)) {
+ this.hideCell(cell);
+ }
+ }, this);
+}
+
+export default HideCells;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCell.js
new file mode 100644
index 000000000..610bac2ef
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCell.js
@@ -0,0 +1,35 @@
+var ShowCell = function (cell) {
+ // Attach container to cell by cell.setContainer(container) under this event
+ var reusedCellContainer = null;
+ var cellContainer = cell.getContainer();
+ if (cellContainer) {
+ reusedCellContainer = cellContainer;
+ cell.popContainer();
+ } else if (this.cellContainersPool) {
+ reusedCellContainer = this.cellContainersPool.getFirstDead();
+ if (reusedCellContainer !== null) { // Reuse this game object
+ reusedCellContainer.setActive(true).setVisible(true);
+ }
+ }
+
+ this.emit('cellvisible', cell, reusedCellContainer, this);
+
+ if (this.cellContainersPool) {
+ var cellContainer = cell.getContainer();
+ if (cellContainer) {
+ if (reusedCellContainer === null) {
+ this.cellContainersPool.add(cellContainer); // New cell container, add to pool
+ } else if (reusedCellContainer !== cellContainer) {
+ // Why reusedCellContainer is not equal to cellContainer?
+ this.cellContainersPool.add(cellContainer); // New cell container, add to pool
+ this.cellContainersPool.killAndHide(reusedCellContainer); // Unused cell container, put back to pool
+ }
+ } else { // No cell container added
+ if (reusedCellContainer !== null) {
+ this.cellContainersPool.killAndHide(reusedCellContainer); // Unused cell container, put back to pool
+ }
+ }
+ }
+}
+
+export default ShowCell;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCells.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCells.js
new file mode 100644
index 000000000..8cb7b1ab5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/ShowCells.js
@@ -0,0 +1,64 @@
+import AlignIn from '../../../../../utils/actions/AlignIn.js';
+
+var ShowCells = function () {
+ if (this.cellsCount === 0) {
+ return;
+ }
+ var table = this.table;
+
+ this.startRowIndex = Math.max(table.heightToRowIndex(-this.tableOY, 2), 0);
+ var rowIndex = this.startRowIndex;
+
+ var startColumnIndex = Math.max(table.widthToColIndex(-this.tableOX), 0);
+ var columnIndex = startColumnIndex;
+
+ var cellIdx = table.colRowToCellIndex(columnIndex, rowIndex);
+ var bottomBound = this.bottomBound;
+ var rightBound = this.rightBound;
+ var lastIdx = table.cellsCount - 1;
+ var lastColIdx = table.colCount - 1;
+
+ var startCellTLX = this.getCellTLX(columnIndex),
+ cellTLX = startCellTLX;
+ var cellTLY = this.getCellTLY(rowIndex);
+ while ((cellTLY < bottomBound) && (cellIdx <= lastIdx)) {
+ if (this.table.isValidCellIdx(cellIdx)) {
+ var cell = table.getCell(cellIdx, true);
+ this.visibleCells.set(cell);
+ if (!this.preVisibleCells.contains(cell)) {
+ this.showCell(cell);
+ }
+
+ var x, y;
+ if (this.scrollMode === 0) {
+ x = cellTLX;
+ y = cellTLY;
+ } else {
+ x = cellTLY;
+ y = cellTLX;
+ }
+ if (cell.cellContainerAlign == null) {
+ cell.setXY(x, y);
+ } else {
+ var cellContainer = cell.getContainer();
+ AlignIn(cellContainer, x, y, cell.width, cell.height, cell.cellContainerAlign);
+ cell.setXY(cellContainer.x, cellContainer.y);
+ }
+ }
+
+ if ((cellTLX < rightBound) && (columnIndex < lastColIdx)) {
+ cellTLX += table.getColWidth(columnIndex);
+ columnIndex += 1;
+ } else {
+ cellTLX = startCellTLX;
+ cellTLY += table.getRowHeight(rowIndex);
+
+ columnIndex = startColumnIndex;
+ rowIndex += 1;
+ }
+
+ cellIdx = table.colRowToCellIndex(columnIndex, rowIndex);
+ }
+}
+
+export default ShowCells;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/UpdateTable.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/UpdateTable.js
new file mode 100644
index 000000000..77b05a6ba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/methods/updatetable/UpdateTable.js
@@ -0,0 +1,24 @@
+var UpdateTable = function (refresh) {
+ if (refresh === undefined) {
+ refresh = false;
+ }
+ if (refresh) {
+ ClearVisibleCellIndexes.call(this);
+ this.hideCells();
+ }
+ ClearVisibleCellIndexes.call(this);
+ this.showCells();
+ this.hideCells();
+
+ this.setMaskChildrenFlag();
+ return this;
+}
+
+var ClearVisibleCellIndexes = function () {
+ var tmp = this.preVisibleCells;
+ this.preVisibleCells = this.visibleCells;
+ this.visibleCells = tmp;
+ this.visibleCells.clear();
+}
+
+export default UpdateTable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Cell.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Cell.js
new file mode 100644
index 000000000..1a87fa006
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Cell.js
@@ -0,0 +1,204 @@
+import DataMethods from '../../../../utils/data/DataMethods.js';
+import AlignConst from '../../../../utils/actions/AlignConst.js';
+
+class Cell {
+ constructor(parent, config) {
+ this.container = null;
+ this._deltaHeight = 0;
+ this.setParent(parent);
+ // this.resetFromJSON(config);
+ }
+
+ setParent(parent) {
+ this.parent = parent; // parent: table
+ this.parentContainer = parent.getParentContainer();
+ }
+
+ // resetFromJSON(o) {
+ // return this;
+ // }
+
+ destroy(fromScene) {
+ if (fromScene === undefined) {
+ fromScene = false;
+ }
+
+ if (!fromScene) {
+ this.destroyContainer();
+ }
+
+ this.deltaHeight = 0;
+ this.data = undefined;
+ this.container = null;
+ this.parent = undefined;
+ this.parentContainer = undefined;
+ }
+
+ get table() {
+ return this.parent;
+ }
+
+ get scrollMode() {
+ return this.parentContainer.scrollMode;
+ }
+
+ get colIndx() {
+ return this.parent.cellIndxeToColIndex(this.index);
+ }
+
+ get rowIndx() {
+ return this.parent.cellIndxeToRowIndex(this.index);
+ }
+
+ getContainer() {
+ return this.container;
+ }
+
+ setContainer(container) {
+ if (!container) {
+ this.destroyContainer();
+ return this;
+ }
+
+ if (this.container) {
+ this.container.destroy();
+ }
+ this.container = container;
+ this.parentContainer.add(container);
+ return this;
+ }
+
+ destroyContainer() {
+ if (this.container) {
+ this.container.destroy();
+ this.container = null;
+ }
+ return this;
+ }
+
+ popContainer() {
+ if (this.container) {
+ var container = this.container;
+ this.container = null;
+ this.parentContainer.remove(container);
+ return container;
+ } else {
+ return null;
+ }
+ }
+
+ setXY(x, y) {
+ if (this.container) {
+ this.parentContainer.setChildLocalPosition(this.container, x, y);
+ }
+ return this;
+ }
+
+ setCellContainerAlign(align) {
+ if (typeof (align) === 'string') {
+ align = AlignConst[align];
+ }
+ this.cellContainerAlign = align;
+ return this;
+ }
+
+ get deltaHeight() {
+ return this._deltaHeight;
+ }
+
+ set deltaHeight(deltaHeight) {
+ if (deltaHeight == null) {
+ deltaHeight = 0;
+ }
+ var table = this.parent;
+ if ((this._deltaHeight === 0) && (deltaHeight !== 0)) {
+ table.nonZeroDeltaHeightCount++;
+ } else if ((this._deltaHeight !== 0) && (deltaHeight === 0)) {
+ table.nonZeroDeltaHeightCount--;
+ }
+
+ var isTableHeightChanged = (this._deltaHeight !== deltaHeight);
+
+ this._deltaHeight = deltaHeight;
+
+ if (isTableHeightChanged) {
+ table.resetTotalRowsHeight();
+ var eventName = (this.scrollMode === 0) ? 'cellheightchange' : 'cellwidthchange';
+ this.parentContainer.emit(eventName, this, this.container, this.parentContainer);
+ }
+ }
+
+ get deltaWidth() {
+ return this.deltaHeight;
+ }
+
+ set deltaWidth(deltaWidth) {
+ this.deltaHeight = deltaWidth;
+ }
+
+ setDeltaHeight(deltaHeight) {
+ this.deltaHeight = deltaHeight;
+ return this;
+ }
+
+ setDeltaWidth(deltaWidth) {
+ this.deltaHeight = deltaWidth;
+ return this;
+ }
+
+ get height() {
+ if (this.scrollMode === 0) {
+ return this.deltaHeight + this.parent.defaultCellHeight;
+ } else {
+ return this.parent.defaultCellWidth;
+ }
+ }
+
+ set height(height) {
+ // Only worked when scrollMode is 0
+ if (this.scrollMode === 1) {
+ return;
+ }
+ this.setDeltaHeight(height - this.parent.defaultCellHeight);
+ }
+
+ setHeight(height) {
+ // Only worked when scrollMode is 0
+ this.height = height;
+ return this;
+ }
+
+ get width() {
+ if (this.scrollMode === 0) {
+ return this.parent.defaultCellWidth;
+ } else {
+ return this.deltaHeight + this.parent.defaultCellHeight;
+ }
+ }
+
+ set width(width) {
+ // Only worked when scrollMode is 1
+ if (this.scrollMode === 0) {
+ return;
+ }
+ this.setDeltaHeight(width - this.parent.defaultCellHeight);
+ }
+
+ setWidth(width) {
+ this.width = width;
+ return this;
+ }
+
+ get scene() {
+ return this.parentContainer.scene;
+ }
+};
+
+
+Object.assign(
+ Cell.prototype,
+ DataMethods
+);
+
+
+export default Cell;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Table.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Table.js
new file mode 100644
index 000000000..7f2760192
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/gridtable/table/Table.js
@@ -0,0 +1,372 @@
+import Cell from './Cell.js';
+import Pool from '../../../../pool.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const SpliceOne = Phaser.Utils.Array.SpliceOne;
+
+class Table {
+ constructor(parent, config) {
+ this.parent = parent; // parent: GridTable game object (Container)
+ this.cells = [];
+ this.cellPool = new Pool();
+ this.resetFromJSON(config);
+ }
+
+ resetFromJSON(o) {
+ if (o === undefined) {
+ o = {};
+ }
+ this.colCount = undefined;
+ this.nonZeroDeltaHeightCount = 0;
+ this.resetTotalRowsHeight();
+
+ var cellHeight = o.cellHeight;
+ if (cellHeight === undefined) {
+ cellHeight = 30;
+ }
+ var cellWidth = o.cellWidth;
+ if (cellWidth === undefined) {
+ cellWidth = 30;
+ }
+
+ this.setDefaultCellHeight(cellHeight);
+ this.setDefaultCellWidth(cellWidth);
+ this.initCells(GetValue(o, 'cellsCount', 0));
+ this.setColumnCount(GetValue(o, 'columns', 1));
+ return this;
+ }
+
+ destroy(fromScene) {
+ // GridTable is destroyed, all cell containers will also be destroyed too
+ // Don't have to freeCell
+ this.cellPool.destroy();
+ this.cells = undefined;
+ this.parent = undefined;
+ }
+
+ get defaultCellHeightMode() {
+ return (this.nonZeroDeltaHeightCount === 0);
+ }
+
+ setDefaultCellHeight(height) {
+ this.defaultCellHeight = height;
+ return this;
+ }
+
+ setDefaultCellWidth(width) {
+ this.defaultCellWidth = width;
+ return this;
+ }
+
+ initCells(size) {
+ var cells = this.cells;
+ cells.length = size;
+ for (var i = 0; i < size; i++) {
+ cells[i] = null;
+ }
+ return this;
+ }
+
+ insertNewCells(cellIdx, count) {
+ var cells = this.cells;
+ if (cellIdx === cells.length) {
+ // append at end of array
+ var endIdx = cellIdx + count;
+ cells.legth = endIdx;
+ for (var i = cellIdx; i < endIdx; i++) {
+ cells[i] = null;
+ }
+ } else {
+ var newCells = [];
+ newCells.length = count;
+ for (var i = 0; i < count; i++) {
+ newCells[i] = null;
+ }
+ this.cells.splice(cellIdx, 0, ...newCells);
+ }
+
+ this.resetTotalRowsHeight();
+ return this;
+ }
+
+ removeCells(cellIdx, count) {
+ var endIdx = cellIdx + count;
+ for (var i = cellIdx; i < endIdx; i++) {
+ this.freeCell(i);
+ }
+
+ if (endIdx === this.cells.length) {
+ // remove until end of array
+ this.cells.length = cellIdx;
+ } else {
+ if (count === 1) {
+ SpliceOne(this.cells, cellIdx);
+ } else {
+ this.cells.splice(cellIdx, count);
+ }
+ this.buildCellIndex(cellIdx);
+ }
+
+ this.resetTotalRowsHeight();
+ return this;
+ }
+
+ setColumnCount(cnt) {
+ this.colCount = cnt;
+ this.resetTotalRowsHeight();
+ return this;
+ }
+
+ get rowCount() {
+ return Math.ceil(this.cells.length / this.colCount);
+ }
+
+ get cellsCount() {
+ return this.cells.length;
+ }
+
+ isValidCellIdx(idx) {
+ return ((idx >= 0) && (idx < this.cells.length));
+ }
+
+ heightToRowIndex(height, roundMode) {
+ if (roundMode === undefined) {
+ roundMode = 0;
+ }
+ /*
+ roundMode:
+ - 0 : floor
+ - 1 : ceil
+ - 2 : plus one if rowIdx is an integer, else floor
+ */
+
+ if (height === 0) {
+ return 0;
+ }
+
+ // defaultCellHeightMode
+ if (this.defaultCellHeightMode) {
+ var rowIdx = height / this.defaultCellHeight;
+ switch (roundMode) {
+ case 0:
+ rowIdx = Math.floor(rowIdx);
+ break;
+
+ case 1:
+ rowIdx = Math.ceil(rowIdx);
+ break;
+
+ default: // 2
+ if (Number.isInteger(rowIdx)) {
+ rowIdx += 1;
+ } else {
+ rowIdx = Math.floor(rowIdx);
+ }
+ break;
+ }
+
+ return rowIdx;
+ }
+
+ // count cell height one by one
+ var rowCount = this.rowCount;
+ var remainder = height,
+ isValidIdx;
+ var cell, rowHeight, rowIdx = 0;
+
+ while (1) {
+ rowHeight = this.getRowHeight(rowIdx);
+ remainder -= rowHeight;
+
+ isValidIdx = (rowIdx >= 0) && (rowIdx < rowCount);
+ if ((remainder > 0) && isValidIdx) {
+ rowIdx += 1;
+ } else if (remainder === 0) {
+ if (roundMode === 2) {
+ rowIdx += 1;
+ }
+ return rowIdx;
+ } else {
+ if (roundMode === 1) {
+ var preRowIdx = rowIdx;
+ rowIdx += 1;
+ isValidIdx = (rowIdx >= 0) && (rowIdx < rowCount);
+
+ if (!isValidIdx) {
+ rowIdx = preRowIdx;
+ }
+ }
+
+ return rowIdx;
+ }
+ }
+
+ }
+
+ widthToColIndex(width, isCeil) {
+ if (width === 0) {
+ return 0;
+ }
+
+ var colIdx = width / this.defaultCellWidth;
+ if (isCeil) {
+ colIdx = Math.ceil(colIdx);
+ } else {
+ colIdx = Math.floor(colIdx);
+ }
+
+ return colIdx;
+ }
+
+ colRowToCellIndex(colIdx, rowIdx) {
+ if (colIdx >= this.colCount) {
+ return null;
+ }
+ return (rowIdx * this.colCount) + colIdx;
+ }
+
+ rowIndexToHeight(start, end) {
+ // defaultCellHeightMode
+ if (this.defaultCellHeightMode) {
+ return (end - start + 1) * this.defaultCellHeight;
+ }
+
+ var h, sum = 0;
+ for (var i = start; i <= end; i++) {
+ h = this.getRowHeight(i);
+ sum += h;
+ }
+
+ return sum;
+ }
+
+ colIndexToWidth(start, end) {
+ return (end - start + 1) * this.defaultCellWidth;
+ };
+
+ getRowHeight(rowIdx) {
+ var cnt = this.colCount;
+ // single column
+ if (cnt <= 1) {
+ return this.getCellHeight(this.colRowToCellIndex(0, rowIdx));
+ }
+
+ // multiple columns, get the maximum height
+ var maxHeight = 0,
+ cellHeight;
+ for (var i = 0; i < cnt; i++) {
+ cellHeight = this.getCellHeight(this.colRowToCellIndex(i, rowIdx));
+ if (maxHeight < cellHeight)
+ maxHeight = cellHeight;
+ }
+ return maxHeight;
+ }
+
+ getColWidth(idx) {
+ return this.defaultCellWidth;
+ }
+
+ getCellHeight(cellIdx) {
+ if (!this.isValidCellIdx(cellIdx)) {
+ return 0;
+ }
+
+ var cellHeight;
+ if (this.defaultCellHeightMode)
+ cellHeight = this.defaultCellHeight;
+ else {
+ var cell = this.getCell(cellIdx, false);
+ var deltaHeight = (cell) ? cell.deltaHeight : 0;
+ cellHeight = this.defaultCellHeight + deltaHeight;
+ }
+
+ return cellHeight;
+ }
+
+ resetTotalRowsHeight() {
+ this._totalRowsHeight = null;
+ }
+
+ get totalRowsHeight() {
+ if (this._totalRowsHeight === null) {
+ this._totalRowsHeight = this.rowIndexToHeight(0, this.rowCount - 1);
+ }
+
+ return this._totalRowsHeight;
+ }
+
+ get totalColumnWidth() {
+ return this.colCount * this.defaultCellWidth;
+ }
+
+ cellIndxeToColIndex(cellIdx) {
+ return cellIdx % this.colCount;
+ }
+
+ cellIndxeToRowIndex(cellIdx) {
+ return Math.floor(cellIdx / this.colCount);
+ }
+
+ getCell(cellIdx, createNewCell) {
+ if (!this.isValidCellIdx(cellIdx)) {
+ return null;
+ }
+
+ if (createNewCell === undefined) {
+ createNewCell = true;
+ }
+ if ((this.cells[cellIdx] === null) && createNewCell) {
+ var cell = this.newCell(cellIdx);
+ this.cells[cellIdx] = cell;
+ }
+
+ return this.cells[cellIdx];
+ }
+
+ newCell(cellIdx) {
+ var cell = this.cellPool.pop();
+ if (cell === null) {
+ cell = new Cell(this);
+ } else {
+ cell.setParent(this);
+ }
+ cell.index = cellIdx;
+
+ return cell;
+ }
+
+ buildCellIndex(startIdx) {
+ if (startIdx === undefined) {
+ startIdx = 0;
+ }
+ var cells = this.cells,
+ cell;
+ for (var i = startIdx, len = cells.length; i < len; i++) {
+ cell = cells[i];
+ if (cell) {
+ cell.index = i;
+ }
+ }
+ return this;
+ }
+
+ getParentContainer() {
+ return this.parent;
+ }
+
+ freeCell(cell) {
+ if (typeof (cell) === 'number') {
+ cell = this.cells[cell];
+ }
+
+ if (!cell) {
+ return this;
+ }
+
+ cell.destroy();
+ this.cellPool.push(cell);
+ return this;
+ }
+}
+
+export default Table;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Creator.js
new file mode 100644
index 000000000..4a9afba27
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Creator.js
@@ -0,0 +1,17 @@
+import ImageBox from './ImageBox.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var key = GetAdvancedValue(config, 'key', null);
+ var frame = GetAdvancedValue(config, 'frame', null);
+
+ var gameObject = new ImageBox(this.scene, 0, 0, key, frame, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Factory.js
new file mode 100644
index 000000000..be77b61fe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/Factory.js
@@ -0,0 +1,7 @@
+import ImageBox from './ImageBox.js';
+
+export default function (x, y, texture, frame, config) {
+ var gameObject = new ImageBox(this.scene, x, y, texture, frame, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.d.ts
new file mode 100644
index 000000000..490eeb0d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.d.ts
@@ -0,0 +1,49 @@
+import ContainerLite from '../containerlite/ContainerLite';
+
+export default ImageBox;
+
+declare namespace ImageBox {
+
+ interface IConfig {
+ x?: number, y?: number,
+ texture?: string, frame?: string,
+
+ width?: number, height?: number,
+
+ image?: Phaser.GameObjects.GameObject,
+ }
+}
+
+declare class ImageBox extends ContainerLite {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ texture?: string, frame?: string,
+ config?: ImageBox.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ config?: ImageBox.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ config?: ImageBox.IConfig
+ );
+
+ image: Phaser.GameObjects.GameObject;
+
+ setTexture(texture?: string, frame?: string): this;
+ readonly texture: Phaser.Textures.Texture | Phaser.Textures.CanvasTexture;
+ readonly frame: Phaser.Textures.Frame;
+
+ setFlipX(value: boolean): this;
+ setFlipY(value: boolean): this;
+ toggleFlipX(): this;
+ toggleFlipY(): this;
+ setFlip(x: boolean, y: boolean): this;
+ flipX: boolean;
+ flipY: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.js
new file mode 100644
index 000000000..e42edce0a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/imagebox/ImageBox.js
@@ -0,0 +1,114 @@
+import Container from '../containerlite/ContainerLite.js';
+import FitToSize from '../../../utils/size/FitTo.js';
+import FlipMethods from '../utils/FlipMethods.js';
+import HasTexture from '../../../utils/texture/HasTexture.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class ImageBox extends Container {
+ constructor(scene, x, y, texture, frame, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ texture = GetValue(config, 'key', undefined);
+ frame = GetValue(config, 'frame', undefined);
+ } else if (IsPlainObject(frame)) {
+ config = frame;
+ frame = undefined;
+ }
+
+ var image = GetValue(config, 'image');
+ if (!image) {
+ image = scene.add.image(x, y, texture, frame);
+ if (texture === undefined) {
+ image.setVisible(false);
+ }
+ } else {
+ image.setPosition(x, y).setOrigin(0.5);
+ }
+
+ super(scene, x, y, 1, 1);
+ this.type = 'rexImageBox';
+
+ this.add(image);
+ this.image = image;
+
+ var width = GetValue(config, 'width', image.width);
+ var height = GetValue(config, 'height', image.height);
+ this.resize(width, height);
+
+ }
+
+ get texture() {
+ return this.image.texture;
+ }
+
+ get frame() {
+ return this.image.frame;
+ }
+
+ get flipX() {
+ return this._flipX;
+ }
+
+ set flipX(value) {
+ if (this._flipX === value) {
+ return;
+ }
+
+ this._flipX = value;
+ this.image.setFlipX(value);
+ }
+
+ get flipY() {
+ return this._flipY;
+ }
+
+ set flipY(value) {
+ if (this._flipY === value) {
+ return;
+ }
+ this._flipY = value;
+ this.image.setFlipY(value);
+ }
+
+ scaleImage() {
+ var image = this.image;
+
+ var result = FitToSize(image, { width: this.width, height: this.height }, true);
+ image.setDisplaySize(result.width, result.height);
+ this.resetChildScaleState(image);
+ return this;
+ }
+
+ resize(width, height) {
+ super.resize(width, height);
+
+ this.scaleImage();
+ return this;
+ }
+
+ setTexture(texture, frame) {
+ var image = this.image;
+ image.setTexture(texture, frame);
+
+ if (texture !== null) {
+ this.setChildVisible(image, true);
+ this.scaleImage();
+
+ } else {
+ this.setChildVisible(image, false);
+
+ }
+ return this;
+ }
+}
+
+Object.assign(
+ ImageBox.prototype,
+ FlipMethods,
+)
+
+export default ImageBox;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Creator.js
new file mode 100644
index 000000000..4d9e99e2d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Creator.js
@@ -0,0 +1,17 @@
+import TransitionImage from './TransitionImage.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var key = GetAdvancedValue(config, 'key', null);
+ var frame = GetAdvancedValue(config, 'frame', null);
+
+ var gameObject = new TransitionImage(this.scene, 0, 0, key, frame, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Factory.js
new file mode 100644
index 000000000..ad55c7c61
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/Factory.js
@@ -0,0 +1,7 @@
+import TransitionImage from './TransitionImage.js';
+
+export default function (x, y, texture, frame, config) {
+ var gameObject = new TransitionImage(this.scene, x, y, texture, frame, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.d.ts
new file mode 100644
index 000000000..fc461b369
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.d.ts
@@ -0,0 +1,104 @@
+import ContainerLite from '../containerlite/ContainerLite';
+
+export default TransitionImage;
+
+declare namespace TransitionImage {
+
+ type TransitionDirectionType = 0 | 1 | 'out' | 'in';
+
+ type TransitionCallbackType = (
+ parent: TransitionImage,
+ currentImage: Phaser.GameObjects.Image,
+ nextImage: Phaser.GameObjects.Image,
+ t: number
+ ) => void;
+
+ interface ITransitConfig {
+ texture?: string, frame?: string,
+
+ dir?: TransitionDirectionType,
+
+ onStart?: TransitionCallbackType,
+ onStartScope?: unknown,
+
+ onProgress?: TransitionCallbackType,
+ onProgressScope?: unknown,
+
+ onComplete?: TransitionCallbackType,
+ onCompleteScope?: unknown,
+
+ duration?: number,
+ ease?: string,
+ mask?: boolean,
+ }
+
+ interface IConfig extends ITransitConfig {
+ x?: number, y?: number,
+ }
+}
+
+declare class TransitionImage extends ContainerLite {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ texture?: string, frame?: string,
+ config?: TransitionImage.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ config?: TransitionImage.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ config?: TransitionImage.IConfig
+ );
+
+ texture: Phaser.Textures.Texture;
+ frame: Phaser.Textures.Frame;
+
+ setTransitionDirection(
+ dir: TransitionImage.TransitionDirectionType
+ ): this;
+
+ setTransitionStartCallback(
+ callback: TransitionImage.TransitionCallbackType,
+ scope?: object
+ ): this;
+
+ setTransitionProgressCallback(
+ callback: TransitionImage.TransitionCallbackType,
+ scope?: object
+ ): this;
+
+ setTransitionCompleteCallback(
+ callback: TransitionImage.TransitionCallbackType,
+ scope?: object
+ ): this;
+
+ setDuration(duration: number): this;
+
+ setDuration(ease: string): this;
+
+ setMaskEnable(enable?: boolean): this;
+
+ transit(texture: string, frame?: string): this;
+
+ transit(
+ config: TransitionImage.ITransitConfig
+ ): this;
+
+ pause(): this;
+ resume(): this;
+ stop(): this;
+
+ setFlipX(value: boolean): this;
+ setFlipY(value: boolean): this;
+ toggleFlipX(): this;
+ toggleFlipY(): this;
+ setFlip(x: boolean, y: boolean): this;
+ flipX: boolean;
+ flipY: boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.js
new file mode 100644
index 000000000..22cf80b9d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/TransitionImage.js
@@ -0,0 +1,261 @@
+import Container from '../containerlite/ContainerLite.js';
+import Methods from './methods/Methods.js';
+import {
+ OnStart as DefaultOnStart,
+ OnProgress as DefaultOnProgress,
+ OnComplete as DefaultOnComplete
+} from './methods/CrossFadeTransition.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Clamp = Phaser.Math.Clamp;
+
+class TransitionImage extends Container {
+ constructor(scene, x, y, texture, frame, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ texture = GetValue(config, 'key', undefined);
+ frame = GetValue(config, 'frame', undefined);
+ } else if (IsPlainObject(frame)) {
+ config = frame;
+ frame = undefined;
+ }
+
+ var backImage = GetValue(config, 'back', undefined);
+ var frontImage = GetValue(config, 'front', undefined);
+ if (!backImage) {
+ backImage = scene.add.image(x, y, texture, frame);
+ }
+ if (!frontImage) {
+ frontImage = scene.add.image(x, y, texture, frame);
+ }
+ var width = GetValue(config, 'width', frontImage.width);
+ var height = GetValue(config, 'height', frontImage.height);
+
+ super(scene, x, y, width, height);
+ this.type = 'rexTransitionImage';
+
+ backImage.setVisible(false);
+ this.addMultiple([backImage, frontImage])
+
+ this.backImage = backImage;
+ this.frontImage = frontImage;
+ this.maskGameObject = undefined;
+ this.cellImages = [];
+ this.imagesPool = [];
+
+ // Transition parameters
+ var onStart = GetValue(config, 'onStart', undefined);
+ var onProgress = GetValue(config, 'onProgress', undefined);
+ var onComplete = GetValue(config, 'onComplete', undefined);
+ var dir = GetValue(config, 'dir', 0);
+ if ((onStart === undefined) && (onProgress === undefined) && (onComplete === undefined)) {
+ onStart = DefaultOnStart;
+ onProgress = DefaultOnProgress;
+ onComplete = DefaultOnComplete;
+ dir = 0;
+ }
+
+ this
+ .setTransitionStartCallback(
+ onStart,
+ GetValue(config, 'onStartScope', undefined)
+ )
+ .setTransitionProgressCallback(
+ onProgress,
+ GetValue(config, 'onProgressScope', undefined)
+ )
+ .setTransitionCompleteCallback(
+ onComplete,
+ GetValue(config, 'onCompleteScope', undefined)
+ )
+ .setTransitionDirection(dir)
+ .setDuration(GetValue(config, 'duration', 1000))
+ .setEaseFunction(GetValue(config, 'ease', 'Linear'))
+
+ var maskGameObject = GetValue(config, 'mask', undefined);
+ if (maskGameObject) {
+ this.setMaskGameObject(maskGameObject);
+ }
+ this.setMaskEnable(false);
+
+ this.ignoreCompleteEvent = false;
+ }
+
+ destroy(fromScene) {
+ // This Game Object has already been destroyed
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ if (this.childrenMask) {
+ this.childrenMask.destroy();
+ this.childrenMask = undefined;
+ }
+ this.backImage = undefined;
+ this.frontImage = undefined;
+ this.maskGameObject = undefined;
+ this.cellImages.length = 0;
+ this.imagesPool.length = 0;
+
+ super.destroy(fromScene);
+
+ this.onStartCallback = undefined;
+ this.onStartCallbackScope = undefined;
+ this.onProgressCallback = undefined;
+ this.onProgressCallbackScope = undefined;
+ this.onCompleteCallback = undefined;
+ this.onCompleteCallbackScope = undefined;
+ this.easeValueTask = undefined;
+ }
+
+ get currentImage() {
+ return (this.dir === 0) ? this.frontImage : this.backImage;
+ }
+
+ get nextImage() {
+ return (this.dir === 0) ? this.backImage : this.frontImage;
+ }
+
+ get texture() {
+ return this.nextImage.texture;
+ }
+
+ get frame() {
+ return this.nextImage.frame;
+ }
+
+ get flipX() {
+ return this._flipX;
+ }
+
+ set flipX(value) {
+ if (this._flipX === value) {
+ return;
+ }
+
+ this._flipX = value;
+ this.backImage.setFlipX(value);
+ this.frontImage.setFlipX(value);
+ }
+
+ get flipY() {
+ return this._flipY;
+ }
+
+ set flipY(value) {
+ if (this._flipY === value) {
+ return;
+ }
+ this._flipY = value;
+ this.backImage.setFlipY(value);
+ this.frontImage.setFlipY(value);
+ }
+
+ get t() {
+ return this._t;
+ }
+
+ set t(value) {
+ value = Clamp(value, 0, 1);
+ if (this._t === value) {
+ return;
+ }
+ this._t = value;
+
+ var currentImage = this.currentImage;
+ var nextImage = this.nextImage;
+
+ // Start
+ if (value === 0) {
+ this
+ .setChildVisible(this.frontImage, true)
+ .setChildVisible(this.backImage, true)
+
+ RunCallback(
+ this.onStartCallback, this.onStartCallbackScope,
+ this, currentImage, nextImage, value
+ );
+ }
+
+ // Progress
+ RunCallback(
+ this.onProgressCallback, this.onProgressCallbackScope,
+ this, currentImage, nextImage, value
+ );
+
+ // Complete
+ if (value === 1) {
+ RunCallback(
+ this.onCompleteCallback, this.onCompleteCallbackScope,
+ this, currentImage, nextImage, value
+ );
+
+ var key = nextImage.texture.key,
+ frame = nextImage.frame.name;
+ this.frontImage.setTexture(key, frame);
+ this.backImage.setTexture(key, frame);
+
+ this
+ .setChildVisible(this.frontImage, true)
+ .setChildVisible(this.backImage, false)
+ .setMaskEnable(false)
+ .freeCellImages()
+ }
+
+ if ((value === 1) && (!this.ignoreCompleteEvent)) {
+ this.emit('complete');
+ }
+ }
+
+ setT(value) {
+ this.t = value;
+ return this;
+ }
+
+ get isRunning() {
+ return (this.easeValueTask) ? this.easeValueTask.isRunning : false;
+ }
+
+ setOrigin(originX, originY) {
+ super.setOrigin(originX, originY);
+
+ this.backImage.setOrigin(originX, originY);
+ this.frontImage.setOrigin(originX, originY);
+
+ if (this.maskGameObject) {
+ this.maskGameObject.setOrigin(originX, originY);
+ }
+
+ return this;
+ }
+
+ setTexture(texture, frame) {
+ // Without transition
+ this.frontImage.setTexture(texture, frame);
+ this.backImage.setTexture(texture, frame).setVisible(false);
+ return this;
+ }
+}
+
+var RunCallback = function (callback, scope, parent, currentImage, nextImage, t) {
+ if (!callback) {
+ return;
+ }
+
+ if (scope) {
+ callback.call(scope, parent, currentImage, nextImage, t);
+ } else {
+ callback(parent, currentImage, nextImage, t);
+ }
+}
+
+// mixin
+Object.assign(
+ TransitionImage.prototype,
+ Methods
+);
+
+export default TransitionImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/CrossFadeTransition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/CrossFadeTransition.js
new file mode 100644
index 000000000..260afe15b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/CrossFadeTransition.js
@@ -0,0 +1,16 @@
+var OnStart = function (parent, currentImage, nextImage, t) {
+}
+
+var OnProgress = function (parent, currentImage, nextImage, t) {
+ parent
+ .setChildLocalAlpha(currentImage, 1 - t)
+ .setChildLocalAlpha(nextImage, t)
+}
+
+var OnComplete = function (parent, currentImage, nextImage, t) {
+ parent.setChildLocalAlpha(currentImage, 1)
+}
+
+export {
+ OnStart, OnProgress, OnComplete
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/GridCutMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/GridCutMethods.js
new file mode 100644
index 000000000..2cd82aae7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/GridCutMethods.js
@@ -0,0 +1,64 @@
+import GridCutImage from '../../../../actions/GridCutImage.js'
+
+export default {
+ gridCutImage(gameObject, columns, rows, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ config.objectPool = this.imagesPool;
+ var cellImages = GridCutImage(gameObject, columns, rows, config),
+ cellImage;
+ for (var i = 0, cnt = cellImages.length; i < cnt; i++) {
+ cellImage = cellImages[i];
+ cellImage.setVisible(true);
+ this.add(cellImage);
+ }
+
+ this.cellImages = cellImages;
+ this.setChildLocalVisible(gameObject, false); // Set cut target to invisible
+ return cellImages;
+ },
+
+ gridCutCurrentImage(columns, rows, config) {
+ return this.gridCutImage(this.currentImage, columns, rows, config);
+ },
+
+ gridCutNextImage(columns, rows, config) {
+ return this.gridCutImage(this.nextImage, columns, rows, config);
+ },
+
+ getCellImages() {
+ return this.cellImages;
+ },
+
+ freeCellImages() {
+ if (this.cellImages.length === 0) {
+ return this;
+ }
+
+ var texture = this.cellImages[0].texture;
+ var cellImages = this.cellImages,
+ cellImage, frameName;
+ for (var i = 0, cnt = cellImages.length; i < cnt; i++) {
+ cellImage = cellImages[i];
+
+ // Reset property of cell image
+ this
+ .setChildLocalAlpha(cellImage, 1)
+ .setChildLocalScale(cellImage, 1)
+ .setChildLocalVisible(cellImage, false)
+
+ cellImage.clearMask();
+
+ // Remove frame object
+ frameName = cellImage.frame.name;
+ cellImage.setTexture();
+ texture.remove(frameName);
+ }
+
+ this.imagesPool.push(...cellImages);
+ cellImages.length = 0;
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/MaskMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/MaskMethods.js
new file mode 100644
index 000000000..1638181e7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/MaskMethods.js
@@ -0,0 +1,95 @@
+import DefaultMaskGraphics from '../../../../utils/mask/defaultmaskgraphics/DefaultMaskGraphics.js';
+
+export default {
+ setMaskGameObject(gameObject) {
+ if (!gameObject) {
+ this.removeMaskGameObject();
+ return this;
+ }
+
+ if (this.maskGameObject) {
+ if ((gameObject === true) && (this.maskGameObject instanceof DefaultMaskGraphics)) {
+ return this;
+ }
+ if (this.maskGameObject === gameObject) {
+ return this;
+ }
+
+ // Remove previous Mask Game Object
+ this.removeMaskGameObject();
+ }
+
+ // Add new Mask Game Object
+ if (gameObject === true) {
+ gameObject = new DefaultMaskGraphics(this);
+ }
+
+ this.maskGameObject = gameObject;
+ this.maskGameObject
+ .resize(this.width, this.height)
+ .setOrigin(this.originX, this.originY)
+ .setPosition(0, 0)
+ .setScale(1)
+ .setVisible(false)
+ this.addLocal(this.maskGameObject);
+
+ this.childrenMask = this.maskGameObject.createGeometryMask();
+ return this;
+ },
+
+ removeMaskGameObject() {
+ this.backImage.clearMask();
+ this.frontImage.clearMask();
+ this.childrenMask = undefined;
+ this.remove(this.maskGameObject, true);
+ this.maskGameObject = undefined;
+ return this;
+ },
+
+ setImageMaskEnable(gameObject, enable, invertAlpha) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ // Use DefaultMaskGraphics if not given
+ if (!this.childrenMask) {
+ this.setMaskGameObject(true);
+ }
+
+ if (enable) {
+ gameObject.setMask(this.childrenMask);
+ if (invertAlpha) {
+ this.childrenMask.setInvertAlpha();
+ }
+ } else {
+ gameObject.clearMask();
+ }
+
+ return this;
+ },
+
+ setCurrentImageMaskEnable(enable, invertAlpha) {
+ this.setImageMaskEnable(this.currentImage, enable, invertAlpha);
+ return this;
+ },
+
+ setNextImageMaskEnable(enable, invertAlpha) {
+ this.setImageMaskEnable(this.nextImage, enable, invertAlpha);
+ return this;
+ },
+
+ setCellImagesMaskEnable(enable, invertAlpha) {
+ var cellImages = this.getCellImages();
+ for (var i = 0, cnt = cellImages.length; i < cnt; i++) {
+ this.setImageMaskEnable(cellImages[i], enable, invertAlpha);
+ }
+ return this;
+ },
+
+ setMaskEnable(enable, invertAlpha) {
+ this.setImageMaskEnable(this.backImage, enable, invertAlpha);
+ this.setImageMaskEnable(this.frontImage, enable, invertAlpha);
+ this.setCellImagesMaskEnable(enable, invertAlpha);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/Methods.js
new file mode 100644
index 000000000..146c50394
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/Methods.js
@@ -0,0 +1,19 @@
+import SetTransitionCallbackMethods from './SetTransitionCallbackMethods.js';
+import TransitionMethods from './TransitionMethods.js';
+import MaskMethods from './MaskMethods.js';
+import GridCutMethods from './GridCutMethods.js';
+import FlipMethods from '../../utils/FlipMethods.js';
+
+var methods = {
+}
+
+Object.assign(
+ methods,
+ SetTransitionCallbackMethods,
+ TransitionMethods,
+ MaskMethods,
+ GridCutMethods,
+ FlipMethods
+)
+
+export default methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/SetTransitionCallbackMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/SetTransitionCallbackMethods.js
new file mode 100644
index 000000000..d13f3be9e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/SetTransitionCallbackMethods.js
@@ -0,0 +1,19 @@
+export default {
+ setTransitionStartCallback(callback, scope) {
+ this.onStartCallback = callback;
+ this.onStartCallbackScope = scope;
+ return this;
+ },
+
+ setTransitionProgressCallback(callback, scope) {
+ this.onProgressCallback = callback;
+ this.onProgressCallbackScope = scope;
+ return this;
+ },
+
+ setTransitionCompleteCallback(callback, scope) {
+ this.onCompleteCallback = callback;
+ this.onCompleteCallbackScope = scope;
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/TransitionMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/TransitionMethods.js
new file mode 100644
index 000000000..b4b1ee6b3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/transitionimage/methods/TransitionMethods.js
@@ -0,0 +1,117 @@
+import EaseValueTask from '../../../../utils/ease/EaseValueTask.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var DirMode = {
+ out: 0,
+ in: 1
+}
+
+export default {
+ setTransitionDirection(dir) {
+ if (typeof (dir) === 'string') {
+ dir = DirMode[dir];
+ }
+ this.dir = dir;
+ return this;
+ },
+
+ setDuration(duration) {
+ this.duration = duration;
+ return this;
+ },
+
+ setEaseFunction(ease) {
+ this.easeFunction = ease;
+ return this;
+ },
+
+ setNextTexture(texture, frame) {
+ this.nextImage.setTexture(texture, frame);
+ return this;
+ },
+
+ transit(texture, frame) {
+ if (this.isRunning) {
+ this.ignoreCompleteEvent = true;
+ this.stop();
+ this.ignoreCompleteEvent = false;
+ }
+
+ if (IsPlainObject(texture)) {
+ var config = texture;
+ texture = GetValue(config, 'key', undefined);
+ frame = GetValue(config, 'frame', undefined);
+
+ this
+ .setDuration(GetValue(config, 'duration', this.duration))
+ .setEaseFunction(GetValue(config, 'ease', this.easeFunction))
+ .setTransitionDirection(GetValue(config, 'dir', this.dir))
+
+ var maskGameObject = GetValue(config, 'mask', undefined);
+ if (maskGameObject) {
+ this.setMaskGameObject(maskGameObject);
+ }
+ this.setMaskEnable(maskGameObject === true);
+
+ var onStart = GetValue(config, 'onStart', undefined);
+ var onProgress = GetValue(config, 'onProgress', undefined);
+ var onComplete = GetValue(config, 'onComplete', undefined);
+ if ((onStart !== undefined) || (onProgress !== undefined) || (onComplete !== undefined)) {
+ this
+ .setTransitionStartCallback(
+ onStart,
+ GetValue(config, 'onStartScope', undefined)
+ )
+ .setTransitionProgressCallback(
+ onProgress,
+ GetValue(config, 'onProgressScope', undefined)
+ )
+ .setTransitionCompleteCallback(
+ onComplete,
+ GetValue(config, 'onCompleteScope', undefined)
+ )
+ }
+ }
+
+ this.setNextTexture(texture, frame);
+
+ this.start();
+ return this;
+ },
+
+ start() {
+ if (this.easeValueTask === undefined) {
+ this.easeValueTask = new EaseValueTask(this, { eventEmitter: null })
+ }
+ this.easeValueTask.restart({
+ key: 't', from: 0, to: 1,
+ duration: this.duration,
+ ease: this.easeFunction
+ });
+ return this;
+ },
+
+ pause() {
+ if (this.easeValueTask) {
+ this.easeValueTask.pause();
+ }
+ return this;
+ },
+
+ resume() {
+ if (this.easeValueTask) {
+ this.easeValueTask.resume();
+ }
+ return this;
+ },
+
+ stop() {
+ if (this.easeValueTask) {
+ this.easeValueTask.stop();
+ }
+ this.setT(1);
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/utils/FlipMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/utils/FlipMethods.js
new file mode 100644
index 000000000..56bc1e926
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/container/utils/FlipMethods.js
@@ -0,0 +1,28 @@
+export default {
+ setFlipX(value) {
+ this.flipX = value;
+ return this;
+ },
+ setFlipY(value) {
+ this.flipY = value;
+ return this;
+ },
+ toggleFlipX() {
+ this.flipX = !this.flipX;
+ return this;
+ },
+ toggleFlipY() {
+ this.flipY = !this.flipY;
+ return this;
+ },
+ setFlip(x, y) {
+ this.flipX = x;
+ this.flipY = y;
+ return this;
+ },
+ resetFlip() {
+ this.flipX = false;
+ this.flipY = false;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/ClickPromise.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/ClickPromise.js
new file mode 100644
index 000000000..c0c77e6e0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/ClickPromise.js
@@ -0,0 +1,19 @@
+import GetGame from '../../../utils/system/GetGame.js';
+import { WaitEvent } from '../../../utils/promise/WaitEvent.js'
+import Delay from '../../../utils/promise/Delay.js';
+
+var ClickPromise = function ({ game, fileInput, closeDelay }) {
+ return WaitEvent(GetGame(game).events, 'focus')
+ .then(function () {
+ return Delay(closeDelay);
+ })
+ .then(function () {
+ var result = {
+ files: fileInput.files
+ }
+
+ return Promise.resolve(result);
+ })
+}
+
+export default ClickPromise;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Creator.js
new file mode 100644
index 000000000..8f178dc44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Creator.js
@@ -0,0 +1,16 @@
+import FileChooser from './FileChooser.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new FileChooser(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Factory.js
new file mode 100644
index 000000000..4217de41d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/Factory.js
@@ -0,0 +1,7 @@
+import FileChooser from './FileChooser.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new FileChooser(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.d.ts
new file mode 100644
index 000000000..7773fa063
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.d.ts
@@ -0,0 +1,68 @@
+export default FileChooser;
+
+declare namespace FileChooser {
+
+ interface IConfig {
+ x?: number,
+ y?: number,
+ width?: number,
+ height?: number,
+
+ accept?: string,
+ multiple?: boolean
+ }
+
+ namespace Events {
+ type ValueChangeCallbackType = (fileChooser: FileChooser) => void;
+ }
+}
+
+declare class FileChooser extends Phaser.GameObjects.DOMElement {
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config?: FileChooser.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ config?: FileChooser.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ config?: FileChooser.IConfig
+ );
+
+ syncTo(gameObject: Phaser.GameObjects.GameObject): this;
+
+ readonly files: File[];
+
+ setAccept(accept: string): this;
+
+ setMultiple(multiple?: boolean): this;
+
+ loadFile(
+ file: File,
+ loaderType: string,
+ key: string,
+ cacheType?: string
+ ): this;
+
+ loadFile(
+ file: File,
+ loaderType: string,
+ key: string,
+ cacheType?: string,
+ onComplete?: (data: any) => void
+ ): this;
+
+ loadFilePromise(
+ file: File,
+ loaderType: string,
+ key: string,
+ cacheType?: string
+ ): Promise;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.js
new file mode 100644
index 000000000..6f6d031d6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filechooser/FileChooser.js
@@ -0,0 +1,118 @@
+import Resize from '../utils/Resize.js';
+import SyncTo from '../utils/SyncTo.js';
+import LoadFileMethods from '../utils/LoadFileMethods.js';
+import ClickPromose from './ClickPromise.js';
+
+const DOMElement = Phaser.GameObjects.DOMElement;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class FileChooser extends DOMElement {
+ constructor(scene, x, y, width, height, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ } else if (IsPlainObject(width)) {
+ config = width;
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ }
+
+ // Create a hidden file input
+ var inputElement = document.createElement('input');
+ inputElement.type = 'file';
+ var inputStyle = inputElement.style;
+ inputStyle.display = 'none';
+
+ // Create a label parent
+ var labelElement = document.createElement('label');
+ labelElement.appendChild(inputElement);
+
+ var style = GetValue(config, 'style', undefined);
+ super(scene, x, y, labelElement, style);
+ this.type = 'rexFileChooser';
+ this.resetFromJSON(config);
+ this.resize(width, height);
+
+ // Register events
+ var self = this;
+ inputElement.onchange = function () {
+ self.emit('change', self);
+ }
+
+ this.setCloseDelay(GetValue(config, 'closeDelay', 200));
+ inputElement.onclick = function () {
+ ClickPromose({
+ game: scene,
+ fileInput: inputElement,
+ closeDelay: self.closeDelay
+ })
+ .then(function () {
+ self.emit('select', self);
+ })
+ }
+ }
+
+ resetFromJSON(config) {
+ this.setAccept(GetValue(config, 'accept', ''));
+ this.setMultiple(GetValue(config, 'multiple', false));
+ return this;
+ }
+
+ setAccept(accept) {
+ if (accept === undefined) {
+ accept = '';
+ }
+ this.fileInput.setAttribute('accept', accept);
+ return this;
+ }
+
+ setMultiple(enabled) {
+ if (enabled === undefined) {
+ enabled = true;
+ }
+ if (enabled) {
+ this.fileInput.setAttribute('multiple', '');
+ } else {
+ this.fileInput.removeAttribute('multiple');
+ }
+ return this;
+ }
+
+ setCloseDelay(delay) {
+ if (delay === undefined) {
+ delay = 200;
+ }
+ this.closeDelay = delay;
+ return this;
+ }
+
+ get fileInput() {
+ return this.node.children[0];
+ }
+
+ open() { // Only work under any touch event
+ this.fileInput.click();
+ return this;
+ }
+
+ get files() {
+ return this.fileInput.files;
+ }
+}
+
+var methods = {
+ resize: Resize,
+ syncTo: SyncTo,
+}
+
+Object.assign(
+ FileChooser.prototype,
+ methods,
+ LoadFileMethods,
+);
+
+export default FileChooser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Creator.js
new file mode 100644
index 000000000..bf0402c44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Creator.js
@@ -0,0 +1,16 @@
+import FileDropZone from './FileDropZone.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new FileDropZone(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Factory.js
new file mode 100644
index 000000000..5251d17e7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/Factory.js
@@ -0,0 +1,7 @@
+import FileDropZone from './FileDropZone.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new FileDropZone(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.d.ts
new file mode 100644
index 000000000..8c6fed90f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.d.ts
@@ -0,0 +1,70 @@
+export default FileDropZone;
+
+declare namespace FileDropZone {
+
+ type FilterCallbackType = (
+ file: File,
+ files: File[]
+ ) => boolean;
+
+ type FiltersType = { [filterType: string]: FilterCallbackType }
+
+ interface IConfig {
+ x?: number,
+ y?: number,
+ width?: number,
+ height?: number,
+
+ filters?: FiltersType
+
+ }
+}
+
+declare class FileDropZone extends Phaser.GameObjects.DOMElement {
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config?: FileDropZone.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ config?: FileDropZone.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ config?: FileDropZone.IConfig
+ );
+
+ setDropEnable(enable?: boolean): this;
+ toggleDropEnable(): this;
+ dropEnable: boolean;
+
+ addFilter(
+ name: string,
+ callback: FileDropZone.FilterCallbackType
+ ): this;
+ addFilters(filters: FileDropZone.FiltersType): this;
+
+ syncTo(gameObject: Phaser.GameObjects.GameObject): this;
+
+ readonly files: File[];
+
+ loadFile(
+ file: File,
+ loaderType: string,
+ key: string,
+ cacheType?: string,
+ onComplete?: (data: any) => void
+ ): this;
+
+ loadFilePromise(
+ file: File,
+ loaderType: string,
+ key: string,
+ cacheType?: string
+ ): Promise;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.js
new file mode 100644
index 000000000..60e52352b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZone.js
@@ -0,0 +1,82 @@
+import Methods from './methods/Methods.js';
+import { DragDropEvents } from './FileDropZoneProperties.js';
+import RouteEvents from '../utils/RouteEvents.js';
+
+const DOMElement = Phaser.GameObjects.DOMElement;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class FileDropZone extends DOMElement {
+ constructor(scene, x, y, width, height, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ } else if (IsPlainObject(width)) {
+ config = width;
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ }
+
+ if (config === undefined) {
+ config = {};
+ }
+
+ var element = document.createElement('div');
+
+ var style = GetValue(config, 'style', undefined);
+ super(scene, x, y, element, style);
+ this.type = 'rexFileDropZone';
+ this.resize(width, height);
+
+ this._files = [];
+ this.setDropEnable(GetValue(config, 'dropEnable', true));
+
+ var filters = GetValue(config, 'filters');
+ if (filters) {
+ this.addFilters(filters);
+ }
+
+ // Apply events
+ RouteEvents(this, element, DragDropEvents, {
+ preventDefault: true,
+ preTest(gameObject) { return gameObject.dropEnable; }
+ });
+
+ this
+ .on('drop', function (gameObject, e) {
+ this._files = e.dataTransfer.files;
+ var files = this._files;
+ if (files && this.filters) {
+ for (var filterType in this.filters) {
+ var filterCallback = this.filters[filterType];
+
+ var filteredFiles = [];
+ for (var i = 0, cnt = files.length; i < cnt; i++) {
+ var file = files[i];
+ if (filterCallback(file, files)) {
+ filteredFiles.push(file);
+ }
+ }
+
+ if (filteredFiles.length > 0) {
+ this.emit(`drop.${filterType}`, filteredFiles);
+ }
+ }
+ }
+ }, this)
+ }
+
+ get files() {
+ return this._files;
+ }
+}
+
+Object.assign(
+ FileDropZone.prototype,
+ Methods,
+);
+
+export default FileDropZone;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZoneProperties.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZoneProperties.js
new file mode 100644
index 000000000..4a3279b40
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/FileDropZoneProperties.js
@@ -0,0 +1,10 @@
+const DragDropEvents = {
+ dragenter: 'dragenter',
+ dragleave: 'dragleave',
+ dragover: 'dragover',
+ drop: 'drop',
+};
+
+export {
+ DragDropEvents,
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/DropEnableMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/DropEnableMethods.js
new file mode 100644
index 000000000..255a7f167
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/DropEnableMethods.js
@@ -0,0 +1,15 @@
+export default {
+ setDropEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ this.dropEnable = enable;
+ return this;
+ },
+
+ toggleDropEnable() {
+ this.dropEnable = !this.dropEnable;
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/FilterMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/FilterMethods.js
new file mode 100644
index 000000000..66929ce9a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/FilterMethods.js
@@ -0,0 +1,19 @@
+export default {
+ addFilter(name, callback) {
+ if (!this.filters) {
+ this.filters = {};
+ }
+ this.filters[name] = callback;
+ return this;
+ },
+
+ addFilters(filters) {
+ if (!this.filters) {
+ this.filters = {};
+ }
+ for (var name in filters) {
+ this.filters[name] = filters[name];
+ }
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/Methods.js
new file mode 100644
index 000000000..e5399dee8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/filedropzone/methods/Methods.js
@@ -0,0 +1,19 @@
+import Resize from '../../utils/Resize.js';
+import SyncTo from '../../utils/SyncTo.js';
+import LoadFileMethods from '../../utils/LoadFileMethods.js';
+import DropEnableMethods from './DropEnableMethods.js';
+import FilterMethods from './FilterMethods.js';
+
+var Methods = {
+ resize: Resize,
+ syncTo: SyncTo,
+}
+
+Object.assign(
+ Methods,
+ DropEnableMethods,
+ FilterMethods,
+ LoadFileMethods,
+)
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Creator.js
new file mode 100644
index 000000000..9e06964db
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Creator.js
@@ -0,0 +1,16 @@
+import InputText from './InputText.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new InputText(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Factory.js
new file mode 100644
index 000000000..e7235508a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/Factory.js
@@ -0,0 +1,7 @@
+import InputText from './InputText.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new InputText(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.d.ts
new file mode 100644
index 000000000..995a73d83
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.d.ts
@@ -0,0 +1,113 @@
+export default InputText;
+
+declare namespace InputText {
+ interface IConfig {
+ x?: number,
+ y?: number,
+ width?: number,
+ height?: number,
+
+ type?: string,
+
+ // Element properties
+ id?: string,
+ text?: string,
+ maxLength?: number,
+ minLength?: number,
+ placeholder?: string,
+ tooltip?: string,
+ readOnly?: boolean,
+ spellCheck?: boolean,
+ autoComplete?: 'on' | 'off',
+
+ // Style properties
+ align?: string,
+ paddingLeft?: string,
+ paddingRight?: string,
+ paddingTop?: string,
+ paddingBottom?: string,
+ fontFamily?: string,
+ fontSize?: string,
+ color?: string,
+ border?: number,
+ backgroundColor?: string,
+ borderColor?: string,
+ outline?: string,
+
+ selectAll?: boolean,
+ }
+
+ namespace Events {
+ type TextChangeCallbackType = (inputText: InputText, e: Event) => void;
+ type FocusCallbackType = (inputText: InputText, e: Event) => void;
+ type BlurCallbackType = (inputText: InputText, e: Event) => void;
+ type ClickCallbackType = (inputText: InputText, e: Event) => void;
+ type DoubleClickCallbackType = (inputText: InputText, e: Event) => void;
+ type SelectCallbackType = (inputText: InputText, e: Event) => void;
+ type PointerDownCallbackType = (inputText: InputText, e: Event) => void;
+ type PointerMoveCallbackType = (inputText: InputText, e: Event) => void;
+ type PointerUpCallbackType = (inputText: InputText, e: Event) => void;
+ type KeyDownCallbackType = (inputText: InputText, e: Event) => void;
+ type KeyPressCallbackType = (inputText: InputText, e: Event) => void;
+ type KeyUpCallbackType = (inputText: InputText, e: Event) => void;
+ }
+}
+
+declare class InputText extends Phaser.GameObjects.DOMElement {
+ constructor(scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config?: InputText.IConfig
+ );
+
+ constructor(scene: Phaser.Scene,
+ x: number, y: number,
+ config?: InputText.IConfig
+ );
+
+ constructor(scene: Phaser.Scene,
+ config?: InputText.IConfig
+ );
+
+ setText(text: string): this;
+ text: string;
+
+ selectText(
+ selectionStart?: number,
+ selectionEnd?: number
+ ): this;
+ selectAll(): this;
+ readonly selectionStart: number;
+ readonly selectionEnd: number;
+ readonly selectedText: string;
+
+ setCursorPosition(value: number): this;
+ cursorPosition: number;
+
+ scrollToBottom(): this;
+
+ getStyle(key: string): string;
+
+ setStyle(key: string, value?: number | string): this;
+
+ setFocus(): this;
+ setBlur(): this;
+ readonly isFocused: boolean;
+
+ setFontColor(color: string): this;
+ fontColor: string;
+
+ setMaxLength(value: number): this;
+ maxLength: number;
+
+ setMinLength(value: number): this;
+ minLength: number;
+
+ setPlaceholder(value: string): this;
+ placeholder: string;
+
+ setTooltip(value: string): this;
+ tooltip: string;
+
+ resize(width: number, height: number): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.js
new file mode 100644
index 000000000..80aeb58eb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputText.js
@@ -0,0 +1,286 @@
+import Resize from '../utils/Resize.js';
+import {
+ ElementProperties,
+ StyleProperties,
+ ElementEvents
+} from './InputTextProperties.js';
+import SetPrpoerties from '../utils/SetProperties.js';
+import RouteEvents from '../utils/RouteEvents.js';
+import StopPropagationTouchEvents from '../utils/StopPropagationTouchEvents.js';
+
+const DOMElement = Phaser.GameObjects.DOMElement;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class InputText extends DOMElement {
+ constructor(scene, x, y, width, height, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ } else if (IsPlainObject(width)) {
+ config = width;
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ }
+
+ if (config === undefined) {
+ config = {};
+ }
+
+ var element;
+ var textType = GetValue(config, 'type', 'text');
+ if (textType === 'textarea') {
+ element = document.createElement('textarea');
+ element.style.resize = 'none';
+ } else {
+ element = document.createElement('input');
+ element.type = textType;
+ }
+
+ SetPrpoerties(ElementProperties, config, element);
+
+ var style = GetValue(config, 'style', undefined);
+ style = SetPrpoerties(StyleProperties, config, style);
+ // Apply other style properties
+ var elementStyle = element.style;
+ for (var key in config) {
+ if ((key in ElementProperties) || (key in StyleProperties)) {
+ continue;
+ } else if (key in elementStyle) {
+ style[key] = config[key];
+ }
+ }
+ style['box-sizing'] = 'border-box';
+ super(scene, x, y, element, style);
+ this.type = 'rexInputText';
+ this.resize(width, height);
+
+ // Apply events
+ RouteEvents(this, element, ElementEvents);
+
+ // Don't propagate touch/mouse events to parent(game canvas)
+ StopPropagationTouchEvents(element);
+
+ if (GetValue(config, 'selectAll', false)) {
+ this.selectAll();
+ }
+
+ this._isFocused = false;
+ this
+ .on('focus', function () {
+ this._isFocused = true;
+ }, this)
+ .on('blur', function () {
+ this._isFocused = false;
+ }, this)
+
+ }
+
+ get text() {
+ return this.node.value;
+ }
+
+ set text(value) {
+ this.node.value = value;
+ }
+
+ setText(value) { // Override
+ this.text = value;
+ return this;
+ }
+
+ get maxLength() {
+ return this.node.maxLength;
+ }
+
+ set maxLength(value) {
+ this.node.maxLength = value;
+ }
+
+ setMaxLength(value) {
+ this.maxLength = value;
+ return this;
+ }
+
+ get minLength() {
+ return this.node.minLength;
+ }
+
+ set minLength(value) {
+ this.node.minLength = value;
+ }
+
+ setMinLength(value) {
+ this.minLength = value;
+ return this;
+ }
+
+ get placeholder() {
+ return this.node.placeholder;
+ }
+
+ set placeholder(value) {
+ this.node.placeholder = value;
+ }
+
+ setPlaceholder(value) {
+ this.placeholder = value;
+ return this;
+ }
+
+ selectText(selectionStart, selectionEnd) {
+ if (selectionStart === undefined) {
+ this.node.select();
+ } else {
+ this.node.setSelectionRange(selectionStart, selectionEnd);
+ }
+ return this;
+ }
+
+ selectAll() {
+ this.selectText();
+ return this;
+ }
+
+ get selectionStart() {
+ return this.node.selectionStart;
+ }
+
+ get selectionEnd() {
+ return this.node.selectionEnd;
+ }
+
+ get selectedText() {
+ var node = this.node;
+ return node.value.substring(node.selectionStart, node.selectionEnd);
+ }
+
+ get cursorPosition() {
+ return this.node.selectionStart;
+ }
+
+ set cursorPosition(value) {
+ this.node.setSelectionRange(value, value);
+ }
+
+ setCursorPosition(value) {
+ if (value === undefined) {
+ value = this.text.length;
+ } else if (value < 0) {
+ value = this.text.length + value;
+ }
+
+ this.cursorPosition = value;
+ return this;
+ }
+
+ get tooltip() {
+ return this.node.title;
+ }
+
+ set tooltip(value) {
+ this.node.title = value;
+ }
+
+ setTooltip(value) {
+ this.tooltip = value;
+ return this;
+ }
+
+ setTextChangedCallback(callback) {
+ this.onTextChanged = callback;
+ return this;
+ }
+
+ get readOnly() {
+ return this.node.readOnly;
+ }
+
+ set readOnly(value) {
+ this.node.readOnly = value;
+ }
+
+ setReadOnly(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.readOnly = value;
+ return this;
+ }
+
+ get spellCheck() {
+ return this.node.spellcheck;
+ }
+
+ set spellCheck(value) {
+ this.node.spellcheck = value;
+ }
+
+ setSpellCheck(value) {
+ this.spellCheck = value;
+ return this;
+ }
+
+ get fontColor() {
+ return this.node.style.color;
+ }
+
+ set fontColor(value) {
+ this.node.style.color = value;
+ }
+
+ setFontColor(value) {
+ this.fontColor = value;
+ return this;
+ }
+
+ setStyle(key, value) {
+ this.node.style[key] = value;
+ return this;
+ }
+
+ getStyle(key) {
+ return this.node.style[key];
+ }
+
+ scrollToBottom() {
+ this.node.scrollTop = this.node.scrollHeight;
+ return this;
+ }
+
+ setEnabled(enabled) {
+ if (enabled === undefined) {
+ enabled = true;
+ }
+ this.node.disabled = !enabled;
+ return this;
+ }
+
+ setBlur() {
+ this.node.blur();
+ return this;
+ }
+
+ setFocus() {
+ this.node.focus();
+ return this;
+ }
+
+ get isFocused() {
+ return this._isFocused;
+ }
+}
+
+var methods = {
+ resize: Resize
+}
+
+Object.assign(
+ InputText.prototype,
+ methods
+);
+
+export default InputText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputTextProperties.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputTextProperties.js
new file mode 100644
index 000000000..03444fe50
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/inputtext/InputTextProperties.js
@@ -0,0 +1,62 @@
+const ElementProperties = {
+ id: ['id', undefined],
+ text: ['value', undefined],
+ maxLength: ['maxLength', undefined],
+ minLength: ['minLength', undefined],
+ placeholder: ['placeholder', undefined],
+ tooltip: ['title', undefined],
+ readOnly: ['readOnly', false],
+ spellCheck: ['spellcheck', false],
+ autoComplete: ['autocomplete', 'off'],
+};
+
+const StyleProperties = {
+ align: ['textAlign', undefined],
+ paddingLeft: ['padding-left', undefined],
+ paddingRight: ['padding-right', undefined],
+ paddingTop: ['padding-top', undefined],
+ paddingBottom: ['padding-bottom', undefined],
+ fontFamily: ['fontFamily', undefined],
+ fontSize: ['font-size', undefined],
+ color: ['color', '#ffffff'],
+ backgroundColor: ['backgroundColor', 'transparent'],
+ border: ['border', 0],
+ borderColor: ['borderColor', 'transparent'],
+ outline: ['outline', 'none'],
+ direction: ['direction', undefined]
+};
+
+const ElementEvents = {
+ input: 'textchange',
+
+ click: 'click',
+ dblclick: 'dblclick',
+
+ mousedown: 'pointerdown',
+ mousemove: 'pointermove',
+ mouseup: 'pointerup',
+
+ touchstart: 'pointerdown',
+ touchmove: 'pointermove',
+ touchend: 'pointerup',
+
+ keydown: 'keydown',
+ keyup: 'keyup',
+ keypress: 'keypress',
+
+ compositionstart: 'compositionStart',
+ compositionend: 'compositionEnd',
+ compositionupdate: 'compositionUpdate',
+
+ focus: 'focus',
+ blur: 'blur',
+
+ select: 'select',
+};
+
+
+export {
+ ElementProperties,
+ StyleProperties,
+ ElementEvents
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/LoadFileMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/LoadFileMethods.js
new file mode 100644
index 000000000..ad79537c8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/LoadFileMethods.js
@@ -0,0 +1,23 @@
+import FileObjectToCache from '../../../utils/loader/FileObjectToCache';
+
+var LoadFile = function (file, loaderType, key, cacheType, onComplete) {
+ var scene = this.scene;
+ FileObjectToCache(scene, file, loaderType, key, cacheType, onComplete);
+
+ return this;
+}
+
+var LoadFilePromise = function (file, loaderType, key, cacheType) {
+ var scene = this.scene;
+ return new Promise(function (resolve, reject) {
+ var onComplete = function (data) {
+ resolve(data)
+ }
+ FileObjectToCache(scene, file, loaderType, key, cacheType, onComplete);
+ });
+}
+
+export default {
+ loadFile: LoadFile,
+ loadFilePromise: LoadFilePromise,
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/Resize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/Resize.js
new file mode 100644
index 000000000..59648aa1c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/Resize.js
@@ -0,0 +1,18 @@
+var Resize = function (width, height) {
+ if (this.scene.sys.scale.autoRound) {
+ width = Math.floor(width);
+ height = Math.floor(height);
+ }
+
+ if ((this.width === width) && (this.height === height)) {
+ return this;
+ }
+
+ var style = this.node.style;
+ style.width = `${width}px`;
+ style.height = `${height}px`;
+ this.updateSize();
+ return this;
+}
+
+export default Resize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/RouteEvents.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/RouteEvents.js
new file mode 100644
index 000000000..7bb3b268b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/RouteEvents.js
@@ -0,0 +1,19 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var RouteEvents = function (gameObject, element, elementEvents, config) {
+ var preventDefault = GetValue(config, 'preventDefault', false);
+ var preTest = GetValue(config, 'preTest');
+ for (let elementEventName in elementEvents) { // Note: Don't use `var` here
+ element.addEventListener(elementEventName, function (e) {
+ if (!preTest || preTest(gameObject, elementEventName)) {
+ gameObject.emit(elementEvents[elementEventName], gameObject, e);
+ }
+
+ if (preventDefault) {
+ e.preventDefault();
+ }
+ });
+ }
+}
+
+export default RouteEvents;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SetProperties.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SetProperties.js
new file mode 100644
index 000000000..825066518
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SetProperties.js
@@ -0,0 +1,20 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var SetProperties = function (properties, config, out) {
+ if (out === undefined) {
+ out = {};
+ }
+
+ var property, value;
+ for (var key in properties) {
+ property = properties[key]; // [propName, defaultValue]
+ value = GetValue(config, key, property[1]);
+ if (value !== undefined) {
+ out[property[0]] = value;
+ }
+ }
+
+ return out;
+}
+
+export default SetProperties;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/StopPropagationTouchEvents.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/StopPropagationTouchEvents.js
new file mode 100644
index 000000000..553478466
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/StopPropagationTouchEvents.js
@@ -0,0 +1,14 @@
+var StopPropagationTouchEvents = function (element) {
+ // Don't propagate touch/mouse events to parent(game canvas)
+ element.addEventListener('touchstart', callback, false);
+ element.addEventListener('touchmove', callback, false);
+ element.addEventListener('touchend', callback, false);
+ element.addEventListener('mousedown', callback, false);
+ element.addEventListener('mouseup', callback, false);
+ element.addEventListener('mousemove', callback, false);
+}
+
+var callback = function (e) {
+ e.stopPropagation();
+}
+export default StopPropagationTouchEvents;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SyncTo.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SyncTo.js
new file mode 100644
index 000000000..316d79469
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/utils/SyncTo.js
@@ -0,0 +1,8 @@
+var SyncTo = function (gameObject) {
+ this.setOrigin(gameObject.originX, gameObject.originY);
+ this.setPosition(gameObject.x, gameObject.y);
+ this.resize(gameObject.displayWidth, gameObject.displayHeight);
+ return this;
+}
+
+export default SyncTo;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Creator.js
new file mode 100644
index 000000000..ee3609c21
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Creator.js
@@ -0,0 +1,16 @@
+import YoutubePlayer from './YoutubePlayer.js';
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new YoutubePlayer(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Factory.js
new file mode 100644
index 000000000..6589e886a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/Factory.js
@@ -0,0 +1,7 @@
+import YoutubePlayer from './YoutubePlayer.js';
+
+export default function (x, y, width, height, config) {
+ var gameObject = new YoutubePlayer(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/LoadAPI.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/LoadAPI.js
new file mode 100644
index 000000000..dcfe55b1d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/LoadAPI.js
@@ -0,0 +1,24 @@
+import LoadScript from '../../../utils/loader/LoadScript.js';
+
+var IsAPIReady = false;
+var LoadAPI = function (onLoaded) {
+ if (IsAPIReady) {
+ onLoaded();
+ } else {
+ if (!window.onYouTubeIframeAPIReady) {
+ window.onYouTubeIframeAPIReady = function () {
+ IsAPIReady = true;
+ for(var i=0, cnt = CallbackQueue.length; i void;
+ type PauseCallbackType = (player: YoutubePlayer) => void;
+ type EndedCallbackType = (player: YoutubePlayer) => void;
+ type BufferingCallbackType = (player: YoutubePlayer) => void;
+ type CuedCallbackType = (player: YoutubePlayer) => void;
+ type ErrorCallbackType = (player: YoutubePlayer) => void;
+ }
+}
+
+declare class YoutubePlayer extends Phaser.GameObjects.DOMElement {
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ width: number, height: number,
+ config?: YoutubePlayer.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ x: number, y: number,
+ config?: YoutubePlayer.IConfig
+ );
+
+ constructor(
+ scene: Phaser.Scene,
+ config?: YoutubePlayer.IConfig
+ );
+
+ load(
+ videoId: string,
+ autoPlay?: boolean
+ ): this;
+
+ play(): this;
+ pause(): this;
+
+ setPlaybackTime(time: number): this;
+ playbackTime: number;
+
+ setT(t: number): this;
+ t: number;
+
+ readonly duration: number;
+
+ setVolume(volume: number): this;
+ volume: number;
+
+ setMute(muted?: boolean): this;
+ muted: boolean;
+
+ setLoop(loop?: boolean): this;
+ loop: boolean;
+
+ resize(width: number, height: number): this;
+
+ readonly isPlaying: boolean;
+ readonly isPaused: boolean;
+ readonly hasEnded: boolean;
+ readonly videoState: number;
+ readonly videoStateString: string;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/YoutubePlayer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/YoutubePlayer.js
new file mode 100644
index 000000000..3b3b4bf67
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dom/youtubeplayer/YoutubePlayer.js
@@ -0,0 +1,257 @@
+import Resize from '../utils/Resize.js';
+import LoadAPI from './LoadAPI.js';
+
+const DOMElement = Phaser.GameObjects.DOMElement;
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+const Clamp = Phaser.Math.Clamp;
+const UUID = Phaser.Utils.String.UUID;
+
+class YoutubePlayer extends DOMElement {
+ constructor(scene, x, y, width, height, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ } else if (IsPlainObject(width)) {
+ config = width;
+ width = GetValue(config, 'width', 0);
+ height = GetValue(config, 'height', 0);
+ }
+
+ if (config === undefined) {
+ config = {};
+ }
+
+ super(scene, x, y);
+ this.type = 'rexYoutubePlayer';
+ this.youtubePlayer = undefined;
+ this.videoState = undefined;
+ this.videoId = GetValue(config, 'videoId', '');
+ this.loop = GetValue(config, 'loop', false);
+ this.paddingCallbacks = [];
+
+ // Create DIV element and add it
+ var elementId = `YT${UUID()}`;
+ var element = document.createElement('div');
+ element.id = elementId;
+ this.setElement(element);
+ this.resize(width, height);
+
+ // Create youtube player iframe when API ready
+ var playerVars = {
+ autoplay: GetValue(config, 'autoPlay', true) ? 1 : 0,
+ controls: GetValue(config, 'controls', true) ? 1 : 0,
+ disablekb: !GetValue(config, 'keyboardControl', true) ? 1 : 0,
+ modestbranding: GetValue(config, 'modestBranding', false) ? 1 : 0,
+ };
+ var onLoad = (function () {
+ var youtubePlayer = new YT.Player(
+ elementId,
+ {
+ 'videoId': this.videoId,
+ 'playerVars': playerVars,
+ 'events': {
+ 'onStateChange': (function (event) {
+ this.videoState = event.data;
+
+ this.emit('statechange', this);
+ this.emit(this.videoStateString, this);
+
+ if ((this.videoState === YT.PlayerState.ENDED) && this.loop) {
+ this.youtubePlayer.playVideo();
+ }
+ }).bind(this),
+ 'onReady': (function (event) {
+ this.youtubePlayer = youtubePlayer;
+ for (var i = 0, cnt = this.paddingCallbacks.length; i < cnt; i++) {
+ this.paddingCallbacks[i]();
+ }
+ this.paddingCallbacks = undefined;
+ this.emit('ready', this);
+ }).bind(this),
+ 'onError': (function (event) {
+ this.lastError = event.data;
+ this.emit('error', this, this.lastError);
+ }).bind(this),
+ }
+ }
+ );
+ this.setElement(document.getElementById(elementId)); // Also remove previous DIV element
+ }).bind(this);
+ LoadAPI(onLoad);
+ }
+
+ _runCallback(callback) {
+ if (this.youtubePlayer === undefined) {
+ this.paddingCallbacks.push(callback);
+ } else {
+ callback();
+ }
+ }
+
+ get videoStateString() {
+ if ((this.videoState === undefined) || (!YT)) {
+ return '';
+ } else {
+ switch (this.videoState) {
+ case -1: return "unstarted";
+ case YT.PlayerState.ENDED: return "ended";
+ case YT.PlayerState.PLAYING: return "playing";
+ case YT.PlayerState.PAUSED: return "pause";
+ case YT.PlayerState.BUFFERING: return "buffering";
+ case YT.PlayerState.CUED: return "cued";
+ }
+ }
+ }
+
+ load(videoId, autoPlay) {
+ if (autoPlay === undefined) {
+ autoPlay = true;
+ }
+
+ var callback = (function () {
+ this.youtubePlayer.loadVideoById(videoId);
+ if (autoPlay) {
+ this.youtubePlayer.playVideo();
+ } else {
+ this.youtubePlayer.pauseVideo();
+ }
+ }).bind(this);
+
+ this._runCallback(callback);
+ return this;
+ }
+
+ play() {
+ var callback = (function () {
+ this.youtubePlayer.playVideo();
+ }).bind(this);
+
+ this._runCallback(callback);
+ return this;
+ }
+
+ get isPlaying() {
+ return (this.videoState === 1); // YT.PlayerState.PLAYING
+ }
+
+ pause() {
+ var callback = (function () {
+ this.youtubePlayer.pauseVideo();
+ }).bind(this);
+
+ this._runCallback(callback);
+ return this;
+ }
+
+ get isPaused() {
+ return (this.videoState === 2); // YT.PlayerState.PAUSED
+ }
+
+ get playbackTime() {
+ return (this.youtubePlayer) ? this.youtubePlayer.getCurrentTime() : 0;
+ }
+
+ set playbackTime(value) {
+ var callback = (function () {
+ this.youtubePlayer.seekTo(value);
+ }).bind(this);
+
+ this._runCallback(callback);
+ }
+
+ setPlaybackTime(time) {
+ this.playbackTime = time;
+ return this;
+ }
+
+ get duration() {
+ return (this.youtubePlayer) ? this.youtubePlayer.getDuration() : 0;
+ }
+
+ get t() {
+ var duration = this.duration;
+ return (duration === 0) ? 0 : this.playbackTime / duration;
+ }
+
+ set t(value) {
+ var callback = (function () {
+ value = Clamp(value, 0, 1);
+ this.playbackTime = this.duration * Clamp(value, 0, 1);
+ }).bind(this);
+
+ this._runCallback(callback);
+ }
+
+ setT(value) {
+ this.t = value;
+ return this;
+ }
+
+ get hasEnded() {
+ return (this.videoState === 0); // YT.PlayerState.ENDED
+ }
+
+ get volume() {
+ return (this.youtubePlayer) ? (this.youtubePlayer.getVolume() / 100) : 0;
+ }
+
+ set volume(value) {
+ var callback = (function () {
+ this.youtubePlayer.setVolume(Clamp(value * 100, 0, 100));
+ }).bind(this);
+
+ this._runCallback(callback);
+ }
+
+ setVolume(value) {
+ this.volume = value;
+ return this;
+ }
+
+ get muted() {
+ return (this.youtubePlayer) ? this.youtubePlayer.isMuted() : false;
+ }
+
+ set muted(value) {
+ var callback = (function () {
+ if (value) {
+ this.youtubePlayer.mute();
+ } else {
+ this.youtubePlayer.unMute();
+ }
+ }).bind(this);
+
+ this._runCallback(callback);
+ }
+
+ setMute(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.muted = value;
+ return this;
+ }
+
+ setLoop(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.loop = value;
+ return this;
+ }
+}
+
+var methods = {
+ resize: Resize
+}
+
+Object.assign(
+ YoutubePlayer.prototype,
+ methods
+);
+
+export default YoutubePlayer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.d.ts
new file mode 100644
index 000000000..ef088e990
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.d.ts
@@ -0,0 +1,143 @@
+import DynamicText from '../dynamictext/DynamicText';
+import HiddenTextEdit from './textedit/HiddenTextEdit';
+
+export default CanvasInput;
+
+declare namespace CanvasInput {
+ type AddCharCallbackType = (
+ child: DynamicText.CharBob,
+ index: number,
+ canvasInput: CanvasInput
+ ) => void;
+
+ type MoveCursorCallbackType = (
+ currCursorIndex: number,
+ prevCursorIndex: number,
+ canvasInput: CanvasInput
+ ) => void;
+
+ type ParseTextCallbackType = (
+ text: string
+ ) => unknown;
+
+ interface IConfigBackground extends DynamicText.IConfigBackground {
+ 'focus.color'?: string | number | null,
+ 'focus.color2'?: string | number | null,
+ 'focus.horizontalGradient'?: boolean,
+
+ 'focus.stroke'?: string | number | null,
+ 'focus.strokeThickness'?: number,
+
+ 'focus.cornerRadius'?: number |
+ ({ x?: number, y?: number }) |
+ DynamicText.IRadiusConfig,
+ 'focus.cornerIteration'?: number
+ }
+
+ interface IConfigTextStyle extends DynamicText.IConfigTextStyle {
+ 'cursor.bold'?: boolean,
+ 'cursor.italic'?: boolean,
+ 'cursor.fontSize'?: string | number,
+ 'cursor.fontFamily'?: string,
+ 'cursor.color'?: string | number | null,
+ 'cursor.stroke'?: string | number | null,
+ 'cursor.strokeThickness'?: number,
+ 'cursor.shadowColor'?: string | number | null,
+ 'cursor.shadowOffsetX'?: number,
+ 'cursor.shadowOffsetY'?: number,
+ 'cursor.shadowBlur'?: number,
+ 'cursor.backgroundColor'?: string | number | null,
+ 'cursor.offsetX'?: number,
+ 'cursor.offsetY'?: number,
+ 'cursor.leftSpace'?: number,
+ 'cursor.rightSpace'?: number,
+ }
+
+ interface IConfig extends DynamicText.IConfig {
+ textArea?: boolean;
+
+ edit?: HiddenTextEdit.IConfig;
+
+ background?: IConfigBackground,
+
+ focusStyle?: DynamicText.IConfigBackground;
+
+ style?: IConfigTextStyle
+
+ cursorStyle?: DynamicText.IConfigTextStyle;
+
+ onOpen?: HiddenTextEdit.OnOpenCallbackType;
+ onFocus?: HiddenTextEdit.OnOpenCallbackType;
+
+ onClose?: HiddenTextEdit.OnCloseCallbackType;
+ onBlur?: HiddenTextEdit.OnCloseCallbackType;
+
+ onUpdate?: HiddenTextEdit.OnUpdateCallbackType;
+
+ onAddChar?: AddCharCallbackType;
+
+ onMoveCursor?: MoveCursorCallbackType;
+
+ parseTextCallback?: ParseTextCallbackType;
+
+ readOnly?: boolean,
+ maxLength?: number,
+ minLength?: number,
+ selectAll?: boolean,
+ }
+}
+
+declare class CanvasInput extends DynamicText {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ fixedWidth?: number, fixedHeight?: number,
+ config?: CanvasInput.IConfig
+ );
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ config?: CanvasInput.IConfig
+ );
+ constructor(
+ scene: Phaser.Scene,
+ config?: CanvasInput.IConfig
+ );
+
+ setText(text: string): this;
+ appendText(text: string): this;
+
+ displayText: string;
+ setDisplayText(value: string): this;
+
+ inputText: string;
+ setInputText(value: string): this;
+
+ setParseTextCallback(callback?: CanvasInput.ParseTextCallbackType): this;
+ getValue(): unknown;
+ setValue(value: unknown): this;
+ value: unknown;
+
+ setReadOnly(enable?: boolean): this;
+ readOnly: boolean;
+
+ open(onCloseCallback?: Function): this;
+ close(): this;
+ readonly isOpened: boolean;
+
+ setFocusStyle(
+ style: DynamicText.IConfigBackground
+ ): this;
+
+ setCursorStyle(
+ style: DynamicText.IConfigTextStyle
+ ): this;
+
+ setNumberInput(): this;
+
+ setMaxLength(value: number): this;
+ maxLength: number;
+
+ setMinLength(value: number): this;
+ minLength: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.js
new file mode 100644
index 000000000..a33f8ebfc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/CanvasInput.js
@@ -0,0 +1,273 @@
+import DynamicText from '../dynamictext/DynamicText.js';
+import CreateHiddenTextEdit from './textedit/CreateHiddenTextEdit.js';
+import InjectDefaultConfig from './methods/InjectDefaultConfig.js';
+import ExtractByPrefix from '../../../utils/object/ExtractByPrefix.js';
+import RegisterCursorStyle from './methods/RegisterCursorStyle.js';
+import RegisterFocusStyle from './methods/RegisterFocusStyle.js';
+import AddLastInsertCursor from './methods/AddLastInsertCursor.js';
+import SetText from './methods/SetText.js';
+import { IsChar } from '../dynamictext/bob/Types.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+
+class CanvasInput extends DynamicText {
+ constructor(scene, x, y, fixedWidth, fixedHeight, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ } else if (IsPlainObject(fixedWidth)) {
+ config = fixedWidth;
+ }
+
+ if (config === undefined) {
+ config = {};
+ }
+
+ InjectDefaultConfig(config);
+
+ // Set text later
+ var text = config.text;
+ if (text) {
+ delete config.text;
+ }
+
+ var focusStyle = ExtractByPrefix(config.background, 'focus');
+ var cursorStyle = ExtractByPrefix(config.style, 'cursor');
+
+ super(scene, x, y, fixedWidth, fixedHeight, config);
+ this.type = 'rexCanvasInput';
+
+ this._text = '';
+
+ this.textEdit = CreateHiddenTextEdit(this, config);
+
+ if (config.focusStyle) {
+ Object.assign(focusStyle, config.focusStyle);
+ }
+ RegisterFocusStyle.call(this, focusStyle);
+
+ if (config.cursorStyle) {
+ Object.assign(cursorStyle, config.cursorStyle);
+ }
+ RegisterCursorStyle.call(this, cursorStyle);
+
+ var addCharCallback = config.onAddChar;
+ if (addCharCallback) {
+ this.on('addchar', addCharCallback);
+ }
+
+ var cursorOutCallback = config.onCursorOut;
+ if (cursorOutCallback) {
+ this.on('cursorout', cursorOutCallback);
+ }
+ var cursorInCallback = config.onCursorIn;
+ if (cursorInCallback) {
+ this.on('cursorin', cursorInCallback);
+ }
+ var moveCursorCallback = config.onMoveCursor;
+ if (moveCursorCallback) {
+ this.on('movecursor', moveCursorCallback);
+ }
+
+ this.setParseTextCallback(config.parseTextCallback);
+
+ this.lastInsertCursor = AddLastInsertCursor(this);
+ if (text) {
+ this.setText(text);
+ } else {
+ // Still need run word wrap for lastInsertCursor child
+ this.runWordWrap();
+ }
+ }
+
+ addChild(child, index) {
+ super.addChild(child, index);
+
+ if (Array.isArray(child)) {
+ var children = child;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (IsChar(child)) {
+ this.emit('addchar', child, index + i, this);
+ }
+ }
+ } else {
+ if (IsChar(child)) {
+ this.emit('addchar', child, index, this);
+ }
+ }
+
+ return this;
+ }
+
+ get text() {
+ return this._text;
+ }
+
+ set text(value) {
+ if (value == null) {
+ value = '';
+ } else {
+ value = value.toString();
+ }
+ if (this._text === value) {
+ return;
+ }
+
+ SetText(this, value);
+
+ this._text = value;
+ }
+
+ setText(text) {
+ this.text = text;
+ return this;
+ }
+
+ appendText(text) {
+ this.setText(this.text + text);
+ return this;
+ }
+
+ setSize(width, height) {
+ if ((this.width === width) && (this.height === height)) {
+ return this;
+ }
+
+ super.setSize(width, height);
+
+ // Run wrap again since fixedWidth and fixedHeight are changed
+ this.runWrap();
+
+ return this;
+ }
+
+ get displayText() {
+ return this.text;
+ }
+
+ set displayText(value) {
+ this.text = value;
+ }
+
+ setDisplayText(value) {
+ this.displayText = value;
+ return this;
+ }
+
+ get inputText() {
+ return this.textEdit.text;
+ }
+
+ set inputText(value) {
+ this.textEdit.text = value;
+ }
+
+ setInputText(value) {
+ this.inputText = value;
+ return this;
+ }
+
+ setParseTextCallback(callback) {
+ if (!callback) {
+ callback = DefaultParseTextCallback;
+ }
+ this.parseTextCallback = callback;
+ return this;
+ }
+
+ get value() {
+ return this.parseTextCallback(this.text);
+ }
+
+ set value(value) {
+ this.setText(value);
+ }
+
+ getValue() {
+ return this.value;
+ }
+
+ setValue(value) {
+ this.value = value;
+ return this;
+ }
+
+ get readOnly() {
+ return this.textEdit.readOnly;
+ }
+
+ set readOnly(value) {
+ this.textEdit.readOnly = value;
+ }
+
+ setReadOnly(value) {
+ this.textEdit.setReadOnly(value);
+ return this;
+ }
+
+ open(onCloseCallback) {
+ if (onCloseCallback) {
+ this.textEdit.once('close', onCloseCallback)
+ }
+ this.textEdit.open();
+ return this;
+ }
+
+ close() {
+ this.textEdit.close();
+ return this;
+ }
+
+ get isOpened() {
+ return this.textEdit.isOpened;
+ }
+
+ setFocusStyle(style) {
+ this.focusStyle = style;
+ return this;
+ }
+
+ setCursorStyle(style) {
+ this.cursorStyle = style;
+ return this;
+ }
+
+ setNumberInput() {
+ this.textEdit.setNumberInput();
+ this.parseTextCallback = Number;
+ return this;
+ }
+
+ get maxLength() {
+ return this.textEdit.maxLength;
+ }
+
+ set maxLength(value) {
+ this.textEdit.maxLength = value
+ }
+
+ setMaxLength(value) {
+ this.maxLength = value;
+ return this;
+ }
+
+ get minLength() {
+ return this.textEdit.minLength;
+ }
+
+ set minLength(value) {
+ this.textEdit.minLength = value;
+ }
+
+ setMinLength(value) {
+ this.minLength = value;
+ return this;
+ }
+
+}
+
+var DefaultParseTextCallback = function (text) {
+ return text;
+}
+
+export default CanvasInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Creator.js
new file mode 100644
index 000000000..f9cdac8ac
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Creator.js
@@ -0,0 +1,16 @@
+import CanvasInput from './CanvasInput.js'
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new CanvasInput(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Factory.js
new file mode 100644
index 000000000..558593a33
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/Factory.js
@@ -0,0 +1,7 @@
+import CanvasInput from './CanvasInput.js'
+
+export default function (x, y, width, height, config) {
+ var gameObject = new CanvasInput(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/AddLastInsertCursor.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/AddLastInsertCursor.js
new file mode 100644
index 000000000..c977bcfcf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/AddLastInsertCursor.js
@@ -0,0 +1,13 @@
+import AddChild from '../../dynamictext/methods/AddChild.js';
+
+var AddLastInsertCursor = function (textObject) {
+ var child = textObject.createCharChild('|'); // Use '|' to update render size
+ child.text = ''; // Render empty string ''
+
+ // Invoke DynamicText's addChild method directly
+ AddChild.call(textObject, child);
+
+ return child;
+}
+
+export default AddLastInsertCursor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/InjectDefaultConfig.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/InjectDefaultConfig.js
new file mode 100644
index 000000000..479082dad
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/InjectDefaultConfig.js
@@ -0,0 +1,36 @@
+import HasValue from '../../../../utils/object/HasValue.js';
+import SetValue from '../../../../utils/object/SetValue.js';
+
+var InjectDefaultConfig = function (config) {
+ var isSingleLineMode = !config.textArea;
+
+ if (!HasValue(config, 'wrap.vAlign')) {
+ var defaultValue = (isSingleLineMode) ? 'center' : 'top';
+ SetValue(config, 'wrap.vAlign', defaultValue);
+ }
+
+ if (!HasValue(config, 'wrap.charWrap')) {
+ SetValue(config, 'wrap.charWrap', true);
+ }
+
+ if (!HasValue(config, 'wrap.maxLines')) {
+ var defaultValue = (isSingleLineMode) ? 1 : undefined;
+ SetValue(config, 'wrap.maxLines', defaultValue);
+ }
+
+ if (!HasValue(config, 'wrap.useDefaultTextHeight')) {
+ SetValue(config, 'wrap.useDefaultTextHeight', true);
+ }
+
+ if (!config.edit) {
+ config.edit = {};
+ }
+ if (!HasValue(config.edit, 'inputType')) {
+ var defaultValue = (isSingleLineMode) ? 'text' : 'textarea';
+ SetValue(config.edit, 'inputType', defaultValue);
+ }
+
+ return config;
+}
+
+export default InjectDefaultConfig;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterCursorStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterCursorStyle.js
new file mode 100644
index 000000000..4f44105b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterCursorStyle.js
@@ -0,0 +1,32 @@
+import IsEmpty from '../../../../utils/object/IsEmpty.js';
+import GetPartialData from '../../../../utils/object/GetPartialData.js';
+import IsKeyValueEqual from '../../../../utils/object/IsKeyValueEqual.js';
+
+var RegisterCursorStyle = function (cursorStyle) {
+ if (IsEmpty(cursorStyle)) {
+ return;
+ }
+
+ this
+ .setCursorStyle(cursorStyle)
+ .on('cursorin', function (child) {
+ var cursorStyle = this.cursorStyle;
+ var styleSave = GetPartialData(child.style, cursorStyle);
+ if (IsKeyValueEqual(cursorStyle, styleSave)) {
+ return;
+ }
+
+ child.styleSave = styleSave;
+ child.modifyStyle(cursorStyle);
+ }, this)
+ .on('cursorout', function (child) {
+ if (!child.styleSave) {
+ return;
+ }
+
+ child.modifyStyle(child.styleSave);
+ child.styleSave = undefined;
+ }, this)
+}
+
+export default RegisterCursorStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterFocusStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterFocusStyle.js
new file mode 100644
index 000000000..b72f8375a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/RegisterFocusStyle.js
@@ -0,0 +1,35 @@
+import IsEmpty from '../../../../utils/object/IsEmpty.js';
+import GetPartialData from '../../../../utils/object/GetPartialData.js';
+import IsKeyValueEqual from '../../../../utils/object/IsKeyValueEqual.js';
+
+var RegisterFocusStyle = function (focusStyle) {
+ if (IsEmpty(focusStyle)) {
+ return;
+ }
+
+ this
+ .setFocusStyle(focusStyle)
+ .on('open', function () {
+ var child = this.background;
+ var focusStyle = this.focusStyle;
+ var styleSave = GetPartialData(child, focusStyle);
+ if (IsKeyValueEqual(focusStyle, styleSave)) {
+ return;
+ }
+
+ child.styleSave = styleSave;
+ child.modifyStyle(focusStyle);
+ }, this)
+ .on('close', function () {
+ var child = this.background;
+
+ if (!child.styleSave) {
+ return;
+ }
+
+ child.modifyStyle(child.styleSave);
+ child.styleSave = undefined;
+ }, this)
+}
+
+export default RegisterFocusStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/SetText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/SetText.js
new file mode 100644
index 000000000..8c7baaa6e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/methods/SetText.js
@@ -0,0 +1,41 @@
+import { diffChars } from '../../../../utils/jsdiff/index.js'
+
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+var SetText = function (textObject, newText) {
+ var text = textObject.text;
+ if (newText === text) {
+ return;
+ }
+
+ // textObject.setText(newText);
+
+ // Remove lastInsertCursor directly
+ RemoveItem(textObject.children, textObject.lastInsertCursor);
+
+ if (newText === '') {
+ textObject.removeChildren();
+ } else {
+ var results = diffChars(text, newText);
+ var charIndex = 0;
+ for (var i = 0, cnt = results.length; i < cnt; i++) {
+ var result = results[i];
+ if (result.removed) {
+ // Remove character at charIndex
+ textObject.removeText(charIndex, result.count);
+ } else if (result.added) {
+ textObject.insertText(charIndex, result.value);
+ charIndex += result.count;
+ } else {
+ charIndex += result.count;
+ }
+ }
+ }
+
+ // Push back lastInsertCursor directly
+ textObject.children.push(textObject.lastInsertCursor);
+
+ textObject.runWordWrap();
+}
+
+export default SetText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearCursor.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearCursor.js
new file mode 100644
index 000000000..2510312fd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearCursor.js
@@ -0,0 +1,16 @@
+var ClearCursor = function (hiddenTextEdit) {
+ var prevCursorPosition = hiddenTextEdit.prevCursorPosition;
+ if (prevCursorPosition === null) {
+ return;
+ }
+
+ var textObject = hiddenTextEdit.parent;
+
+ var child = textObject.getCharChild(prevCursorPosition);
+ if (child) {
+ textObject.emit('cursorout', child, prevCursorPosition, textObject);
+ }
+
+ hiddenTextEdit.prevCursorPosition = null;
+}
+export default ClearCursor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearSelectRange.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearSelectRange.js
new file mode 100644
index 000000000..653445dc5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/ClearSelectRange.js
@@ -0,0 +1,21 @@
+var ClearSelectRange = function (hiddenTextEdit) {
+ var prevSelectionStart = hiddenTextEdit.prevSelectionStart;
+ if (prevSelectionStart === null) {
+ return;
+ }
+
+ var prevSelectionEnd = hiddenTextEdit.prevSelectionEnd;
+
+ var textObject = hiddenTextEdit.parent;
+ for (var i = prevSelectionStart; i < prevSelectionEnd; i++) {
+ var child = textObject.getCharChild(i);
+ if (child) {
+ textObject.emit('cursorout', child, i, textObject);
+ }
+ }
+
+ hiddenTextEdit.prevSelectionStart = null;
+ hiddenTextEdit.prevSelectionEnd = null;
+}
+
+export default ClearSelectRange;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/CreateHiddenTextEdit.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/CreateHiddenTextEdit.js
new file mode 100644
index 000000000..c710c7867
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/CreateHiddenTextEdit.js
@@ -0,0 +1,23 @@
+import HiddenTextEdit from './HiddenTextEdit.js';
+import CopyProperty from '../../../../utils/object/CopyProperty.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+const PropertiesList = [
+ 'inputType',
+ 'onOpen', 'onFocus', 'onClose', 'onBlur', 'onUpdate',
+ 'enterClose',
+ 'readOnly', 'maxLength', 'minLength', 'selectAll'
+];
+
+var CreateHiddenTextEdit = function (parent, parentConfig) {
+ var config = GetValue(parentConfig, 'edit');
+ if (config === undefined) {
+ config = {};
+ }
+
+ CopyProperty(parentConfig, config, PropertiesList);
+
+ return new HiddenTextEdit(parent, config);
+}
+
+export default CreateHiddenTextEdit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.d.ts
new file mode 100644
index 000000000..dd26fbcda
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.d.ts
@@ -0,0 +1,2 @@
+import HiddenTextEditBase from '../../../../behaviors/hiddentextedit/HiddenTextEditBase';
+export default HiddenTextEditBase;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.js
new file mode 100644
index 000000000..74f922639
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/HiddenTextEdit.js
@@ -0,0 +1,119 @@
+import HiddenTextEditBase from '../../../../behaviors/hiddentextedit/HiddenTextEditBase.js';
+import NumberInputUpdateCallback from '../../../../behaviors/hiddentextedit/defaultcallbacks/NumberInputUpdateCallback.js';
+import SelectRange from './SelectRange.js';
+import MoveCursor from './MoveCursor.js';
+import ClearSelectRange from './ClearSelectRange.js';
+import ClearCursor from './ClearCursor.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class HiddenTextEdit extends HiddenTextEditBase {
+ constructor(gameObject, config) {
+ super(gameObject, config);
+ // this.parent = gameObject;
+
+ this.setSelectAllWhenFocusEnable(GetValue(config, 'selectAll', false));
+
+ this.cursorMoveStartIndex = null;
+ this.prevCursorPosition = null;
+ this.prevSelectionStart = null;
+ this.prevSelectionEnd = null;
+ this.firstClickAfterOpen = false;
+
+
+ gameObject
+ // Open editor by 'pointerdown' event
+ // Then set cursor position to nearest char
+ .on('pointerdown', function (pointer, localX, localY, event) {
+ var child = gameObject.getNearestChild(localX, localY);
+ var charIndex = gameObject.getCharIndex(child);
+
+ if (!this.selectAllWhenFocus || !this.firstClickAfterOpen) {
+ this.setCursorPosition(charIndex);
+ }
+
+ this.cursorMoveStartIndex = charIndex;
+ this.firstClickAfterOpen = false;
+ }, this)
+ .on('pointermove', function (pointer, localX, localY, event) {
+ if (!pointer.isDown) {
+ return;
+ }
+ var child = gameObject.getNearestChild(localX, localY);
+ var charIndex = gameObject.getCharIndex(child);
+ if (this.cursorMoveStartIndex < charIndex) {
+ this.selectText(this.cursorMoveStartIndex, charIndex + 1);
+ } else {
+ this.selectText(charIndex, this.cursorMoveStartIndex + 1);
+ }
+ }, this)
+
+ this
+ .on('open', function () {
+ if (this.selectAllWhenFocus) {
+ this.selectAll();
+ }
+ this.firstClickAfterOpen = true;
+
+ gameObject.emit('open');
+ }, this)
+ .on('close', function () {
+ gameObject.emit('close');
+ }, this)
+ }
+
+ initText() {
+ var textObject = this.parent;
+ this.prevCursorPosition = null;
+ this.setText(textObject.text);
+ return this;
+ }
+
+ updateText() {
+ var textObject = this.parent;
+
+ var text = this.text;
+ if (this.onUpdateCallback) {
+ var newText = this.onUpdateCallback(text, textObject, this);
+ if (newText != null) {
+ text = newText;
+ }
+ }
+
+ if (textObject.text !== text) {
+ textObject.setText(text);
+ textObject.emit('textchange', text, textObject, this);
+ }
+
+ if (this.isOpened) {
+ if (this.selectionStart !== this.selectionEnd) {
+ ClearCursor(this);
+ SelectRange(this);
+ } else {
+ ClearSelectRange(this);
+ MoveCursor(this);
+ }
+ } else {
+ ClearSelectRange(this);
+ ClearCursor(this);
+ }
+
+ return this;
+ }
+
+ setNumberInput() {
+ this.onUpdateCallback = NumberInputUpdateCallback;
+ return this;
+ }
+
+ setSelectAllWhenFocusEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ this.selectAllWhenFocus = enable;
+ return this;
+ }
+}
+
+export default HiddenTextEdit;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/MoveCursor.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/MoveCursor.js
new file mode 100644
index 000000000..c7da4fd8c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/MoveCursor.js
@@ -0,0 +1,33 @@
+var MoveCursor = function (hiddenTextEdit) {
+ var textObject = hiddenTextEdit.parent;
+ var text = textObject.text;
+
+ var cursorPosition = hiddenTextEdit.cursorPosition;
+ if (hiddenTextEdit.prevCursorPosition === cursorPosition) {
+ return;
+ }
+
+ if (hiddenTextEdit.prevCursorPosition !== null) {
+ if (hiddenTextEdit.prevCursorPosition > text.length) {
+ hiddenTextEdit.prevCursorPosition = null;
+ }
+ }
+
+ if (hiddenTextEdit.prevCursorPosition !== null) {
+ var child = textObject.getCharChild(hiddenTextEdit.prevCursorPosition);
+ if (child) {
+ textObject.emit('cursorout', child, hiddenTextEdit.prevCursorPosition, textObject);
+ }
+ }
+ if (cursorPosition != null) {
+ var child = textObject.getCharChild(cursorPosition);
+ if (child) {
+ textObject.emit('cursorin', child, cursorPosition, textObject);
+ }
+ }
+ textObject.emit('movecursor', cursorPosition, hiddenTextEdit.prevCursorPosition, textObject);
+
+ hiddenTextEdit.prevCursorPosition = cursorPosition;
+}
+
+export default MoveCursor;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/SelectRange.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/SelectRange.js
new file mode 100644
index 000000000..dc0e6f8d1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/canvasinput/textedit/SelectRange.js
@@ -0,0 +1,46 @@
+var OnSelectRange = function (hiddenTextEdit) {
+ var textObject = hiddenTextEdit.parent;
+ // var text = textObject.text;
+ var selectionStart = (hiddenTextEdit.isOpened) ? hiddenTextEdit.selectionStart : null;
+ var selectionEnd = (hiddenTextEdit.isOpened) ? hiddenTextEdit.selectionEnd : null;
+ var prevSelectionStart = hiddenTextEdit.prevSelectionStart;
+ var prevSelectionEnd = hiddenTextEdit.prevSelectionEnd;
+
+ if ((prevSelectionStart === selectionStart) && (prevSelectionEnd === selectionEnd)) {
+ return;
+ }
+
+ var min, max;
+ if (prevSelectionStart === null) {
+ min = selectionStart;
+ max = selectionEnd;
+ } else {
+ min = Math.min(prevSelectionStart, selectionStart);
+ max = Math.max(prevSelectionEnd, selectionEnd);
+ }
+
+ for (var i = min; i < max; i++) {
+ var inPrevSelectionRange = (prevSelectionStart === null) ? false :
+ (i >= prevSelectionStart) && (i < prevSelectionEnd);
+ var inSelectionRange = (i >= selectionStart) && (i < selectionEnd);
+
+
+ if (inPrevSelectionRange && inSelectionRange) {
+ continue;
+ }
+
+ var child = textObject.getCharChild(i);
+ if (child) {
+ if (inPrevSelectionRange) {
+ textObject.emit('cursorout', child, i, textObject);
+ } else {
+ textObject.emit('cursorin', child, i, textObject);
+ }
+ }
+ }
+
+ hiddenTextEdit.prevSelectionStart = selectionStart;
+ hiddenTextEdit.prevSelectionEnd = selectionEnd;
+}
+
+export default OnSelectRange;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Creator.js
new file mode 100644
index 000000000..73ce534dc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Creator.js
@@ -0,0 +1,16 @@
+import DynamicText from './DynamicText.js'
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new DynamicText(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.d.ts
new file mode 100644
index 000000000..989da50b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.d.ts
@@ -0,0 +1,325 @@
+// import * as Phaser from 'phaser';
+import Canvas from '../../canvas/canvas/Canvas';
+import Background from './bob/background/Background';
+import InnerBounds from './bob/innerbounds/InnerBounds';
+import { IConfigTextStyle as IConfigTextStyleBase } from './bob/char/TextStyle';
+import BobBaseClass from './bob/Base';
+import CharBobClass from './bob/char/CharData';
+import ImageBobClass from './bob/image/ImageData';
+import DrawBobClass from './bob/drawer/Drawer';
+import CommandBobClass from './bob/command/Command';
+
+
+export default DynamicText;
+
+declare namespace DynamicText {
+
+ type PaddingTypes = number |
+ { left?: number, right?: number, top?: number, bottom?: number };
+
+ interface IRadiusConfig {
+ tl?: (number | { x?: number, y?: number }),
+ tr?: (number | { x?: number, y?: number }),
+ bl?: (number | { x?: number, y?: number }),
+ br?: (number | { x?: number, y?: number })
+ }
+
+ interface IConfigBackground {
+ color?: string | number | null,
+ color2?: string | number | null,
+ horizontalGradient?: boolean,
+
+ stroke?: string | number | null,
+ strokeThickness?: number,
+
+ cornerRadius?: number |
+ ({ x?: number, y?: number }) |
+ IRadiusConfig,
+ cornerIteration?: number
+ }
+
+ interface IConfigInnerBounds {
+ color?: string | number | null,
+ color2?: string | number | null,
+ horizontalGradient?: boolean,
+
+ stroke?: string | number | null,
+ strokeThickness?: number,
+ }
+
+ interface IConfigTextStyle extends IConfigTextStyleBase {
+
+ }
+
+ interface IConfigImage {
+ width?: number,
+ height?: number,
+ scaleX?: number,
+ scaleY?: number,
+ }
+
+ type HAlignTypes = 0 | 1 | 2 | 'left' | 'center' | 'right';
+ type VAlignTypes = 0 | 1 | 2 | 'top' | 'center' | 'bottom';
+
+ interface IConfigWrapBase {
+ callback?: string | Function,
+ hAlign?: HAlignTypes,
+ vAlign?: VAlignTypes,
+ }
+
+ interface IConfigWordWrap extends IConfigWrapBase {
+ padding?: {
+ top?: number, left?: number, right?: number, bottom?: number,
+ },
+ ascent?: number,
+ lineHeight?: number,
+ useDefaultTextHeight?: boolean,
+ maxLines?: number,
+ wrapWidth?: number,
+ letterSpacing?: number,
+ charWrap?: boolean
+ }
+
+ interface IConfigVerticalWrap extends IConfigWrapBase {
+ padding: {
+ top?: number, left?: number, right?: number, bottom?: number,
+ },
+ lineWidth?: number,
+ maxLines?: number,
+ fixedChildHeight?: number,
+ charPerLine?: number,
+ wrapHeight?: number,
+ letterSpacing?: number,
+ rtl?: boolean,
+ }
+
+ type BobBase = BobBaseClass;
+ type CharBob = CharBobClass;
+ type ImageBob = ImageBobClass;
+ type DrawBob = DrawBobClass;
+ type CommandBob = CommandBobClass;
+ type RenderChildTypes = CharBob | ImageBob | DrawBob;
+
+ interface IWrapResult {
+ children: BobBase[],
+ lines: ({
+ children: BobBase[],
+ width: number,
+ height: number
+ })[],
+ isLastPage: boolean
+ }
+
+ interface IConfig {
+ x?: number, y?: number,
+ width?: number, height?: number,
+
+ padding: PaddingTypes,
+
+ background?: IConfigBackground,
+
+ innerBounds?: IConfigInnerBounds,
+
+ style?: IConfigTextStyle,
+
+ text?: string,
+
+ wrap?: IConfigWordWrap | IConfigVerticalWrap | IConfigWordWrap,
+
+ testString?: string,
+
+ childrenInteractive?: boolean,
+ }
+
+}
+
+declare class DynamicText extends Canvas {
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ fixedWidth?: number, fixedHeight?: number,
+ config?: DynamicText.IConfig
+ );
+ constructor(
+ scene: Phaser.Scene,
+ x?: number, y?: number,
+ config?: DynamicText.IConfig
+ );
+ constructor(
+ scene: Phaser.Scene,
+ config?: DynamicText.IConfig
+ );
+
+ background: Background;
+ innerBounds: InnerBounds;
+ children: DynamicText.BobBase[];
+ lastAppendedChildren: DynamicText.BobBase[];
+
+ clearContent(): this;
+
+ createCharChild(
+ text: string,
+ style?: DynamicText.IConfigTextStyle
+ ): DynamicText.CharBob;
+ createCharChildren(
+ text: string,
+ style?: DynamicText.IConfigTextStyle
+ ): DynamicText.CharBob[];
+ setText(
+ text: string,
+ style?: DynamicText.IConfigTextStyle
+ ): this;
+ appendText(
+ text: string,
+ style?: DynamicText.IConfigTextStyle
+ ): this;
+ insertText(
+ index: number,
+ text: string,
+ style?: DynamicText.IConfigTextStyle
+ ): this;
+ getText(activeOnly?: boolean): string;
+ resetTextStyle(): this;
+ modifyTextStyle(style: DynamicText.IConfigTextStyle): this;
+ modifyDefaultTextStyle(style: DynamicText.IConfigTextStyle): this;
+ text: string;
+
+ setTestString(testString: string): this;
+ testString: string;
+
+ getCharChild(
+ charIndex: number,
+ activeOnly?: boolean
+ ): DynamicText.CharBob;
+ getCharChildIndex(
+ charIndex: number,
+ activeOnly?: boolean
+ ): DynamicText.CharBob;
+ getCharChildren(
+ activeOnly?: boolean,
+ out?: DynamicText.CharBob[]
+ ): DynamicText.CharBob[]
+
+ createImageChild(
+ key: string, frame?: string | null,
+ config?: DynamicText.IConfigImage
+ ): DynamicText.ImageBob;
+ appendImage(
+ key: string, frame?: string | null,
+ config?: DynamicText.IConfigImage
+ ): this;
+
+ createDrawerChild(
+ renderCallback: (this: DynamicText.DrawBob) => void,
+ width?: number,
+ height?: number
+ ): DynamicText.DrawBob;
+ appendDrawer(
+ renderCallback: (this: DynamicText.DrawBob) => void,
+ width?: number,
+ height?: number
+ ): this;
+
+ createCommandChild(
+ name: string,
+ callback: (param: unknown, name: string) => any,
+ param: unknown,
+ scope?: Object
+ ): DynamicText.CommandBob;
+ appendCommand(
+ name: string,
+ callback: (param: unknown, name: string) => any,
+ param: unknown,
+ scope?: Object
+ ): this;
+
+ removeChild(child: DynamicText.BobBase): this;
+ removeChildren(): this;
+ removeText(index: number, length?: number): this;
+
+ popChild(child: DynamicText.BobBase): this;
+
+ moveChildToFist(child: DynamicText.BobBase): this;
+ moveChildToLast(child: DynamicText.BobBase): this;
+ movechildUp(child: DynamicText.BobBase): this;
+ movechildDown(child: DynamicText.BobBase): this;
+ movechildAbove(
+ child: DynamicText.BobBase,
+ baseChild: DynamicText.BobBase
+ ): this;
+ movechildBelow(
+ child: DynamicText.BobBase,
+ baseChild: DynamicText.BobBase
+ ): this;
+
+ runWordWrap(
+ config?: DynamicText.IConfigWordWrap
+ ): DynamicText.IWrapResult;
+
+ runVerticalWrap(
+ config?: DynamicText.IConfigVerticalWrap
+ ): DynamicText.IWrapResult;
+
+ runWrap(
+ config?: DynamicText.IConfigWordWrap | DynamicText.IConfigVerticalWrap | DynamicText.IConfigWrapBase
+ ): DynamicText.IWrapResult;
+
+ setVAlign(align: DynamicText.VAlignTypes): this;
+ setHAlign(align: DynamicText.HAlignTypes): this;
+
+ getChildren(): DynamicText.BobBase[];
+ getLastAppendedChildren(): DynamicText.BobBase[];
+ getActiveChildren(): DynamicText.BobBase[];
+
+ setBackgroundColor(
+ color?: string | number | null,
+ color2?: string | number | null,
+ horizontalGradient?: boolean
+ ): this;
+ setBackgroundStroke(
+ stroke?: string | number | null,
+ strokeThickness?: number,
+ ): this;
+ setBackgroundCornerRadius(
+ cornerRadius?: number |
+ ({ x?: number, y?: number }) |
+ DynamicText.IRadiusConfig,
+ cornerIteration?: number
+ ): this;
+
+ setInnerBoundsColor(
+ color?: string | number | null,
+ color2?: string | number | null,
+ horizontalGradient?: boolean
+ ): this;
+ setInnerBoundsStroke(
+ stroke?: string | number | null,
+ strokeThickness?: number,
+ ): this;
+
+ getNearestChild(
+ localX: number,
+ localY: number
+ ): DynamicText.BobBase;
+
+ getCharWorldPosition(
+ child: DynamicText.BobBase,
+ offsetX?: number, offsetY?: number, out?: { x?: number, y?: number }
+ ): { x: number, y: number };
+
+ getCharWorldPosition(
+ child: DynamicText.BobBase,
+ out?: { x?: number, y?: number }
+ ): { x: number, y: number };
+
+ getCharWorldPosition(
+ childIndex: number,
+ offsetX?: number, offsetY?: number, out?: { x?: number, y?: number }
+ ): { x: number, y: number };
+
+ getCharWorldPosition(
+ childIndex: number,
+ out?: { x?: number, y?: number }
+ ): { x: number, y: number };
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.js
new file mode 100644
index 000000000..fe04573c1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/DynamicText.js
@@ -0,0 +1,83 @@
+import Canvas from '../../canvas/canvasbase/Canvas.js';
+import { SetPadding } from '../../../utils/padding/PaddingMethods';
+import Background from './bob/background/Background.js';
+import InnerBounds from './bob/innerbounds/InnerBounds.js';
+import TextStyle from './bob/char/TextStyle.js';
+import Methods from './methods/Methods';
+import PoolManager from './poolmanager/PoolManager.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class DynamicText extends Canvas {
+ constructor(scene, x, y, fixedWidth, fixedHeight, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ x = GetValue(config, 'x', 0);
+ y = GetValue(config, 'y', 0);
+ fixedWidth = GetValue(config, 'width', 0);
+ fixedHeight = GetValue(config, 'height', 0);
+ } else if (IsPlainObject(fixedWidth)) {
+ config = fixedWidth;
+ fixedWidth = GetValue(config, 'width', 0);
+ fixedHeight = GetValue(config, 'height', 0);
+ }
+
+ var width = (fixedWidth === 0) ? 1 : fixedWidth;
+ var height = (fixedHeight === 0) ? 1 : fixedHeight;
+ super(scene, x, y, width, height);
+ this.type = 'rexDynamicText';
+ this.autoRound = true;
+ this.padding = SetPadding();
+ this.wrapPadding = SetPadding();
+
+ var textStyleConfig = GetValue(config, 'style', undefined);
+ this.defaultTextStyle = new TextStyle(null, textStyleConfig);
+ this.textStyle = this.defaultTextStyle.clone();
+ this.setTestString(GetValue(config, 'testString', '|MÉqgy'));
+
+ this.background = new Background(this, GetValue(config, 'background', undefined));
+ this.innerBounds = new InnerBounds(this, GetValue(config, 'innerBounds', undefined));
+ this.children = [];
+ this.lastAppendedChildren = [];
+ this.lastOverChild = null;
+ this.poolManager = new PoolManager(config);
+
+ this.setFixedSize(fixedWidth, fixedHeight);
+ this.setPadding(GetValue(config, 'padding', 0));
+ this.setWrapConfig(GetValue(config, 'wrap', undefined));
+ this.setChildrenInteractiveEnable(GetValue(config, 'childrenInteractive', false));
+
+ var text = GetValue(config, 'text', undefined);
+ if (text) {
+ this.setText(text);
+ }
+ }
+
+ updateTexture() {
+ this.renderContent();
+ super.updateTexture();
+ return this;
+ }
+
+ get text() {
+ return this.getText(true);
+ }
+
+ set text(value) {
+ this.setText(value);
+ }
+
+ setSize(width, height) {
+ this.setFixedSize(width, height);
+ return this;
+ }
+}
+
+Object.assign(
+ DynamicText.prototype,
+ Methods
+);
+
+
+export default DynamicText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Factory.js
new file mode 100644
index 000000000..6b260229e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/Factory.js
@@ -0,0 +1,7 @@
+import DynamicText from './DynamicText.js'
+
+export default function (x, y, width, height, config) {
+ var gameObject = new DynamicText(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.d.ts
new file mode 100644
index 000000000..dc9c9beef
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.d.ts
@@ -0,0 +1,21 @@
+// import * as Phaser from 'phaser';
+import DataMethods from '../../../../utils/data/DataMethods';
+import DynamicText from '../DynamicText';
+
+export default class Base extends DataMethods {
+ parent: DynamicText;
+ readonly type: string;
+ readonly renderable: boolean;
+
+ setActive(active?: boolean): this;
+ active: boolean;
+
+ setDirty(dirty?: boolean): this;
+
+ scene: Phaser.Scene;
+ canvas: HTMLCanvasElement;
+ context: CanvasRenderingContext2D;
+
+ render(): void;
+ contains(x: number, y: number): boolean;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.js
new file mode 100644
index 000000000..d83012068
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Base.js
@@ -0,0 +1,85 @@
+import DataMethods from '../../../../utils/data/DataMethods.js'
+
+class Base {
+ constructor(parent, type) {
+ this.setParent(parent);
+ this.type = type;
+ this.renderable = false;
+
+ this.reset().setActive();
+ }
+
+ destroy() {
+ this.parent.removeChild(this);
+ }
+
+ setParent(parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ get scene() {
+ return this.parent.scene;
+ }
+
+ get canvas() {
+ return (this.parent) ? this.parent.canvas : null;
+ }
+
+ get context() {
+ return (this.parent) ? this.parent.context : null;
+ }
+
+ setDirty(dirty) {
+ if (dirty && this.parent) {
+ this.parent.dirty = true;
+ }
+ return this;
+ }
+
+ get active() {
+ return this._active;
+ }
+
+ set active(value) {
+ this.setDirty(this._active != value);
+ this._active = value;
+ }
+
+ setActive(active) {
+ if (active === undefined) {
+ active = true;
+ }
+ this.active = active;
+ return this;
+ }
+
+ modifyPorperties(o) {
+ return this;
+ }
+
+ // Override
+ onFree() {
+ this.reset().setParent();
+ }
+
+ // Override
+ reset() {
+ return this;
+ }
+
+ // Override
+ render() { }
+
+ // Override
+ contains(x, y) {
+ return false;
+ }
+}
+
+Object.assign(
+ Base.prototype,
+ DataMethods
+);
+
+export default Base;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Types.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Types.js
new file mode 100644
index 000000000..64a3a925c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/Types.js
@@ -0,0 +1,38 @@
+const CharTypeName = 'text';
+const ImageTypeName = 'image';
+const DrawerTypeName = 'drawer';
+const SpaceTypeName = 'space';
+const CmdTypeName = 'command';
+
+var IsNewLineChar = function (bob) {
+ return (bob.type === CharTypeName) && (bob.text === '\n');
+}
+
+var IsPageBreakChar = function (bob) {
+ return (bob.type === CharTypeName) && (bob.text === '\f');
+}
+
+var IsSpaceChar = function (bob) {
+ return (bob.type === CharTypeName) && (bob.text === ' ');
+}
+
+var IsChar = function (bob) {
+ return (bob.type === CharTypeName);
+}
+
+var IsCommand = function (bob) {
+ return bob.type === CmdTypeName;
+}
+
+export {
+ CharTypeName,
+ ImageTypeName,
+ DrawerTypeName,
+ SpaceTypeName,
+ CmdTypeName,
+ IsNewLineChar,
+ IsPageBreakChar,
+ IsSpaceChar,
+ IsChar,
+ IsCommand
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.d.ts
new file mode 100644
index 000000000..59883ff23
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.d.ts
@@ -0,0 +1,22 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class Background extends RenderBase {
+ readonly type: 'background';
+
+ setColor(
+ color?: string | number | null,
+ color2?: string | number | null,
+ isHorizontalGradient?: boolean
+ ): this;
+
+ setStroke(
+ color?: string | number | null,
+ lineWidth?: number
+ ): this;
+
+ setCornerRadius(
+ radius?: number,
+ iteration?: number | null
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.js
new file mode 100644
index 000000000..fe9f3ebfa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/background/Background.js
@@ -0,0 +1,169 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import GetStyle from '../../../../../utils/canvas/GetStyle.js';
+import GetProperty from '../utils/GetProperty.js';
+import DrawRoundRectangleBackground from '../../../../canvas/utils/DrawRoundRectangleBackground.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Background extends RenderBase {
+ constructor(parent, config) {
+ super(parent, 'background');
+
+ this.setColor(
+ GetValue(config, 'color', null),
+ GetValue(config, 'color2', null),
+ GetValue(config, 'horizontalGradient', true)
+ );
+
+ this.setStroke(
+ GetValue(config, 'stroke', null),
+ GetValue(config, 'strokeThickness', 2)
+ );
+
+ this.setCornerRadius(
+ GetValue(config, 'cornerRadius', 0),
+ GetValue(config, 'cornerIteration', null)
+ );
+ }
+
+ set color(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._color != value);
+ this._color = value;
+ }
+
+ get color() {
+ return this._color;
+ }
+
+ set color2(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._color2 != value);
+ this._color2 = value;
+ }
+
+ get color2() {
+ return this._color2;
+ }
+
+ set horizontalGradient(value) {
+ this.setDirty(this._horizontalGradient != value);
+ this._horizontalGradient = value;
+ }
+
+ get horizontalGradient() {
+ return this._horizontalGradient;
+ }
+
+ setColor(color, color2, isHorizontalGradient) {
+ if (isHorizontalGradient === undefined) {
+ isHorizontalGradient = true;
+ }
+
+ this.color = color;
+ this.color2 = color2;
+ this.horizontalGradient = isHorizontalGradient;
+ return this;
+ }
+
+ set stroke(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._stroke != value);
+ this._stroke = value;
+ }
+
+ get stroke() {
+ return this._stroke;
+ }
+
+ set strokeThickness(value) {
+ this.setDirty(this._strokeThickness != value);
+ this._strokeThickness = value;
+ }
+
+ get strokeThickness() {
+ return this._strokeThickness;
+ }
+
+ setStroke(color, lineWidth) {
+ if (color != null) {
+ if (lineWidth === undefined) {
+ lineWidth = 2;
+ }
+ }
+ this.stroke = color;
+ this.strokeThickness = lineWidth;
+ return this;
+ }
+
+ set cornerRadius(value) {
+ this.setDirty(this._cornerRadius != value);
+ this._cornerRadius = value;
+ }
+
+ get cornerRadius() {
+ return this._cornerRadius;
+ }
+
+ set cornerIteration(value) {
+ this.setDirty(this._cornerIteration != value);
+ this._cornerIteration = value;
+ }
+
+ get cornerIteration() {
+ return this._cornerIteration;
+ }
+
+ modifyStyle(o) {
+ if (o.hasOwnProperty('color')) {
+ this.setColor(
+ o.color,
+ GetProperty('color2', o, this),
+ GetProperty('horizontalGradient', o, this),
+ );
+ }
+ if (o.hasOwnProperty('stroke')) {
+ this.setStroke(
+ o.stroke,
+ GetProperty('strokeThickness', o, this),
+ );
+ }
+ if (o.hasOwnProperty('cornerRadius')) {
+ this.setCornerRadius(
+ o.cornerRadius,
+ GetProperty('cornerIteration', o, this),
+ );
+ }
+
+ return this;
+ }
+
+ modifyPorperties(o) {
+ super.modifyPorperties(o);
+
+ this.modifyStyle(o);
+
+ return this;
+ }
+
+ setCornerRadius(radius, iteration) {
+ this.cornerRadius = radius;
+ this.cornerIteration = iteration;
+ return this;
+ }
+
+ renderContent() {
+ DrawRoundRectangleBackground(
+ this.parent,
+ this.color,
+ this.stroke,
+ this.strokeThickness,
+ this.cornerRadius,
+ this.color2,
+ this.horizontalGradient,
+ this.cornerIteration
+ );
+ }
+}
+
+export default Background;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.d.ts
new file mode 100644
index 000000000..b63504ee3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.d.ts
@@ -0,0 +1,11 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class CharData extends RenderBase {
+ readonly type: 'text';
+
+ readonly textWidth: number;
+ readonly textHeight: number;
+ readonly ascent: number;
+ readonly descent: number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.js
new file mode 100644
index 000000000..7015623d5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/CharData.js
@@ -0,0 +1,221 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import { CharTypeName } from '../Types.js';
+import TextStyle from './TextStyle.js';
+
+class CharData extends RenderBase {
+ constructor(
+ parent,
+ text,
+ style
+ ) {
+ super(parent, CharTypeName);
+ this.updateTextFlag = false;
+ this.style = new TextStyle(this, style);
+ this.setText(text);
+ }
+
+ get autoRound() {
+ return this.parent.autoRound;
+ }
+
+ get offsetX() {
+ return this.style.offsetX;
+ }
+
+ set offsetX(value) {
+ if (this.style) {
+ this.style.offsetX = value;
+ }
+ }
+
+ get offsetY() {
+ return this.style.offsetY;
+ }
+
+ set offsetY(value) {
+ if (this.style) {
+ this.style.offsetY = value;
+ }
+ }
+
+ get leftSpace() {
+ return this.style.leftSpace * this.scaleX;
+ }
+
+ set leftSpace(value) {
+ if (this.style) {
+ this.style.leftSpace = value;
+ }
+ super.leftSpace = value;
+ }
+
+ get rightSpace() {
+ return this.style.rightSpace * this.scaleX;
+ }
+
+ set rightSpace(value) {
+ if (this.style) {
+ this.style.rightSpace = value;
+ }
+ super.rightSpace = value;
+ }
+
+ get align() {
+ return this.style.align;
+ }
+
+ set align(value) {
+ if (this.style) {
+ this.style.align = value;
+ }
+ }
+
+ modifyStyle(style) {
+ this.setDirty(true);
+ this.style.modify(style);
+
+ if (this.updateTextFlag) {
+ this.updateTextSize();
+ }
+ return this;
+ }
+
+ modifyPorperties(o) {
+ if (!o) {
+ return this;
+ }
+
+ this.modifyStyle(o);
+ super.modifyPorperties(o);
+ return this;
+ }
+
+ setText(text) {
+ this.setDirty(this.text != text);
+ this.text = text;
+
+ this.updateTextSize();
+
+ return this;
+ }
+
+ updateTextSize() {
+ var text = this.text;
+ // Is new-line, page-break, or empty character
+ if ((text === '\n') || (text === '\f') || (text === '')) {
+ this.textWidth = 0;
+ this.textHeight = 0;
+ this.ascent = 0;
+ this.descent = 0;
+
+ } else {
+ var metrics = this.style.getTextMetrics(this.context, this.text);
+ this.textWidth = metrics.width;
+
+ var ascent, descent;
+ if ('actualBoundingBoxAscent' in metrics) {
+ ascent = metrics.actualBoundingBoxAscent;
+ descent = metrics.actualBoundingBoxDescent;
+ } else {
+ ascent = 0;
+ descent = 0;
+ }
+
+ this.textHeight = ascent + descent;
+ this.ascent = ascent;
+ this.descent = descent;
+ }
+
+ this.updateTextFlag = false;
+ return this;
+ }
+
+ get width() {
+ return this.textWidth * this.scaleX;
+ }
+
+ set width(value) {
+ if (this.textWidth > 0) {
+ this.scaleX = value / this.textWidth;
+ } else {
+ this.scaleX = 1;
+ }
+ }
+
+ get height() {
+ return this.textHeight * this.scaleY;
+ }
+
+ set height(value) {
+ if (this.textHeight > 0) {
+ this.scaleY = value / this.textHeight;
+ } else {
+ this.scaleY = 1;
+ }
+ }
+
+ get willRender() {
+ var text = this.text;
+ if ((text === '\n') || (text === '\f')) {
+ return false;
+ }
+
+ return super.willRender;
+ }
+
+ renderContent() {
+ var context = this.context;
+ var textStyle = this.style;
+
+ if (textStyle.hasBackgroundColor) {
+ context.fillStyle = textStyle.backgroundColor;
+
+ var x = this.drawTLX;
+ var width = this.drawTRX - x;
+
+ var bottomY = textStyle.backgroundBottomY;
+ if (bottomY == null) {
+ bottomY = this.drawBLY;
+ }
+ var height = textStyle.backgroundHeight;
+ if (height == null) {
+ height = bottomY - this.drawTLY;
+ }
+ var y = bottomY - height;
+
+ context.fillRect(x, y, width, height);
+ }
+
+ var hasFill = textStyle.hasFill,
+ hasStroke = textStyle.hasStroke;
+
+ if (!hasFill && !hasStroke) {
+ return;
+ }
+
+ textStyle.syncFont(context).syncStyle(context);
+ // textBaseline = 'alphabetic'
+
+ if (hasStroke) {
+ textStyle.syncShadow(context);
+ context.strokeText(this.text, 0, 0);
+ }
+
+ if (hasFill) {
+ textStyle.syncShadow(context);
+ context.fillText(this.text, 0, 0);
+ }
+ }
+
+ get drawTLX() { return -this.leftSpace; }
+ get drawTLY() { return -this.ascent; }
+ get drawBLX() { return -this.leftSpace; }
+ get drawBLY() { return this.descent; }
+ get drawTRX() { return this.textWidth + this.rightSpace; }
+ get drawTRY() { return -this.ascent; }
+ get drawBRX() { return this.textWidth + this.rightSpace; }
+ get drawBRY() { return this.descent; }
+
+}
+
+export default CharData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.d.ts
new file mode 100644
index 000000000..3f9477287
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.d.ts
@@ -0,0 +1,74 @@
+export interface IConfigTextStyle {
+ bold?: boolean,
+ italic?: boolean,
+ fontSize?: string | number,
+ fontFamily?: string,
+ color?: string | number | null,
+ stroke?: string | number | null,
+ strokeThickness?: number,
+ shadowColor?: string | number | null,
+ shadowOffsetX?: number,
+ shadowOffsetY?: number,
+ shadowBlur?: number,
+ backgroundColor?: string | number | null,
+ offsetX?: number,
+ offsetY?: number,
+ leftSpace?: number,
+ rightSpace?: number,
+}
+
+export default class TextStyle {
+ modify(o?: IConfigTextStyle): this;
+
+ setBold(bold?: boolean): this;
+ bold: boolean;
+ setItalic(italic?: boolean): this;
+ italic: boolean;
+
+ setFontSize(fontSize: string | number): this;
+ fontSize: string;
+
+ setFontFamily(fontFamily: string): this;
+ fontFamily: string;
+
+ readonly font: string;
+
+ setColor(color?: number | string | null): this;
+ color: string | null;
+
+ setStrokeStyle(
+ stroke?: number | string | null,
+ strokeThickness?: number
+ ): this;
+ stroke: string | null;
+ strokeThickness: number;
+
+ setShadowColor(color?: number | string | null): this;
+ shadowColor: string | null;
+ setShadowOffset(offsetX?: number, offsetY?: number): this;
+ shadowOffsetX: number;
+ shadowOffsetY: number;
+ setShadowBlur(blur?: number): this;
+ shaodwBlur: number;
+ setShadow(
+ color?: number | string | null,
+ offsetX?: number,
+ offsetY?: number,
+ blur?: number
+ ): this;
+
+ setBackgroundColor(color?: number | string | null): this;
+
+ setOffsetX(offsetX?: number): this;
+ offsetX: number;
+ setOffsetY(offsetY?: number): this;
+ offsetY: number;
+ setOffset(offsetX?: number, offsetY?: number): this;
+
+ setLeftSpace(space?: number): this;
+ leftSpace: number;
+ setRightSpace(space?: number): this;
+ rightSpace: number;
+ setSpace(leftSpace?: number, rightSpace?: number): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.js
new file mode 100644
index 000000000..c4da50805
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/char/TextStyle.js
@@ -0,0 +1,383 @@
+import GetStyle from '../../../../../utils/canvas/GetStyle.js';
+import GetProperty from '../utils/GetProperty.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TextStyle {
+ constructor(parent, config) {
+ this.parent = parent;
+ this.set(config);
+ }
+
+ toJSON() {
+ return {
+ bold: this.bold,
+ italic: this.italic,
+ fontSize: this.fontSize,
+ fontFamily: this.fontFamily,
+ color: this.color,
+ stroke: this.stroke,
+ strokeThickness: this.strokeThickness,
+ shaodwColor: this.shadowColor,
+ shadowBlur: this.shadowBlur,
+ shadowOffsetX: this.shadowOffsetX,
+ shadowOffsetY: this.shadowOffsetY,
+ offsetX: this.offsetX,
+ offsetY: this.offsetY,
+ leftSpace: this.leftSpace,
+ rightSpace: this.rightSpace,
+ backgroundHeight: this.backgroundHeight,
+ backgroundBottomY: this.backgroundBottomY,
+ align: this.align
+ }
+ }
+
+ set(o) {
+ this.setBold(GetValue(o, 'bold', false));
+ this.setItalic(GetValue(o, 'italic', false));
+ this.setFontSize(GetValue(o, 'fontSize', '16px'));
+ this.setFontFamily(GetValue(o, 'fontFamily', 'Courier'));
+ this.setColor(GetValue(o, 'color', '#fff'));
+ this.setStrokeStyle(
+ GetValue(o, 'stroke', null),
+ GetValue(o, 'strokeThickness', 0)
+ );
+ this.setShadow(
+ GetValue(o, 'shadowColor', null),
+ GetValue(o, 'shadowOffsetX', 0),
+ GetValue(o, 'shadowOffsetY', 0),
+ GetValue(o, 'shadowBlur', 0)
+ );
+ this.setOffset(
+ GetValue(o, 'offsetX', 0),
+ GetValue(o, 'offsetY', 0)
+ );
+ this.setSpace(
+ GetValue(o, 'leftSpace', 0),
+ GetValue(o, 'rightSpace', 0)
+ );
+ this.setAlign(GetValue(o, 'align', undefined));
+ this.setBackgroundColor(GetValue(o, 'backgroundColor', null));
+ this.setBackgroundHeight(GetValue(o, 'backgroundHeight', undefined));
+ this.setBackgroundBottomY(GetValue(o, 'backgroundBottomY', undefined));
+
+ return this;
+ }
+
+ modify(o) {
+ if (o.hasOwnProperty('bold')) {
+ this.setBold(o.bold);
+ }
+ if (o.hasOwnProperty('italic')) {
+ this.setItalic(o.italic);
+ }
+ if (o.hasOwnProperty('fontSize')) {
+ this.setFontSize(o.fontSize);
+ }
+ if (o.hasOwnProperty('fontFamily')) {
+ this.setFontFamily(o.fontFamily);
+ }
+ if (o.hasOwnProperty('color')) {
+ this.setColor(o.color);
+ }
+ if (o.hasOwnProperty('stroke') || o.hasOwnProperty('strokeThickness')) {
+ this.setStrokeStyle(
+ GetProperty('stroke', o, this),
+ GetProperty('strokeThickness', o, this)
+ );
+ }
+
+ if (o.hasOwnProperty('shadowColor')) {
+ this.setShadowColor(o.shadowColor);
+ }
+
+ if (o.hasOwnProperty('shadowOffsetX') || o.hasOwnProperty('shadowOffsetY')) {
+ this.setShadowOffset(
+ GetProperty('shadowOffsetX', o, this),
+ GetProperty('shadowOffsetY', o, this),
+ );
+ }
+
+ if (o.hasOwnProperty('shadowBlur')) {
+ this.setShadowBlur(o.shaodwBlur);
+ }
+
+ if (o.hasOwnProperty('offsetX')) {
+ this.setOffsetX(o.offsetX);
+ }
+ if (o.hasOwnProperty('offsetY')) {
+ this.setOffsetY(o.offsetY);
+ }
+
+ if (o.hasOwnProperty('leftSpace')) {
+ this.setLeftSpace(o.leftSpace);
+ }
+ if (o.hasOwnProperty('rightSpace')) {
+ this.setRightSpace(o.rightSpace);
+ }
+
+ if (o.hasOwnProperty('align')) {
+ this.setAlign(o.align);
+ }
+
+ if (o.hasOwnProperty('backgroundColor')) {
+ this.setBackgroundColor(o.backgroundColor);
+ }
+
+ if (o.hasOwnProperty('backgroundHeight')) {
+ this.setBackgroundHeight(o.backgroundHeight);
+ }
+ if (o.hasOwnProperty('backgroundBottomY')) {
+ this.setBackgroundBottomY(o.backgroundBottomY);
+ }
+
+ return this;
+ }
+
+ setUpdateTextFlag() {
+ if (this.parent) {
+ this.parent.updateTextFlag = true;
+ }
+ return this;
+ }
+
+ clone() {
+ return new TextStyle(null, this.toJSON());
+ }
+
+ copyFrom(sourceTextStyle) {
+ this.set(sourceTextStyle.toJSON());
+ return this;
+ }
+
+ copyTo(targetTextStyle) {
+ targetTextStyle.set(this.toJSON());
+ return this;
+ }
+
+ setBold(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.bold = value;
+ this.setUpdateTextFlag();
+ return this;
+ }
+
+ setItalic(value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.italic = value;
+ this.setUpdateTextFlag();
+ return this;
+ }
+
+ get fontStyle() {
+ if (this.bold && this.italic) {
+ return 'bold italic';
+ } else if (this.bold) {
+ return 'bold';
+ } else if (this.italic) {
+ return 'italic';
+ } else {
+ return '';
+ }
+ }
+
+ setFontSize(fontSize) {
+ if (typeof (fontSize) === 'number') {
+ fontSize = `${fontSize}px`;
+ }
+ this.fontSize = fontSize;
+ this.setUpdateTextFlag();
+ return this;
+ }
+
+ setFontFamily(fontFamily) {
+ this.fontFamily = fontFamily;
+ this.setUpdateTextFlag();
+ return this;
+ }
+
+ get font() {
+ return `${this.fontStyle} ${this.fontSize} ${this.fontFamily}`;
+ }
+
+ setColor(color) {
+ this.color = GetStyle(color);
+ return this;
+ }
+
+ get hasFill() {
+ return this.color != null;
+ }
+
+ setStrokeStyle(stroke, strokeThickness) {
+ this.stroke = GetStyle(stroke);
+ if (strokeThickness !== undefined) {
+ this.strokeThickness = strokeThickness;
+ }
+ return this;
+ }
+
+ setStrokeThickness(strokeThickness) {
+ this.strokeThickness = strokeThickness;
+ return this;
+ }
+
+ get hasStroke() {
+ return (this.stroke != null) && (this.strokeThickness > 0);
+ }
+
+ setShadowColor(color) {
+ this.shadowColor = GetStyle(color);
+ return this;
+ }
+
+ setShadowOffset(offsetX, offsetY) {
+ if (offsetX === undefined) {
+ offsetX = 0
+ }
+ if (offsetY === undefined) {
+ offsetY = 0
+ }
+
+ this.shadowOffsetX = offsetX;
+ this.shadowOffsetY = offsetY;
+ return this;
+ }
+
+ setShadowBlur(blur) {
+ if (blur === undefined) {
+ blur = 0
+ }
+
+ this.shaodwBlur = blur;
+ return this;
+ }
+
+ setShadow(color, offsetX, offsetY, blur) {
+ this
+ .setShadowColor(color)
+ .setShadowOffset(offsetX, offsetY)
+ .setShadowBlur(blur);
+ return this;
+ }
+
+ setBackgroundColor(color) {
+ this.backgroundColor = GetStyle(color);
+ return this;
+ }
+
+ get hasBackgroundColor() {
+ return this.backgroundColor != null;
+ }
+
+ setBackgroundHeight(height) {
+ this.backgroundHeight = height;
+ return this;
+ }
+
+ setBackgroundBottomY(y) {
+ this.backgroundBottomY = y;
+ return this;
+ }
+
+ setOffsetX(offsetX) {
+ if (offsetX === undefined) {
+ offsetX = 0
+ }
+
+ this.offsetX = offsetX;
+ return this;
+ }
+
+ setOffsetY(offsetY) {
+ if (offsetY === undefined) {
+ offsetY = 0
+ }
+
+ this.offsetY = offsetY;
+ return this;
+ }
+
+ setOffset(offsetX, offsetY) {
+ this
+ .setOffsetX(offsetX)
+ .setOffsetY(offsetY);
+ return this;
+ }
+
+ setLeftSpace(space) {
+ if (space === undefined) {
+ space = 0
+ }
+
+ this.leftSpace = space;
+ return this;
+ }
+
+ setRightSpace(space) {
+ if (space === undefined) {
+ space = 0
+ }
+
+ this.rightSpace = space;
+ return this;
+ }
+
+ setSpace(leftSpace, rightSpace) {
+ this
+ .setLeftSpace(leftSpace)
+ .setRightSpace(rightSpace);
+ return this;
+ }
+
+ setAlign(align) {
+ this.align = align;
+ return this;
+ }
+
+ syncFont(context) {
+ context.font = this.font;
+ return this;
+ }
+
+ syncStyle(context) {
+ context.textBaseline = 'alphabetic';
+
+ var hasFill = this.hasFill;
+ var hasStroke = this.hasStroke;
+ context.fillStyle = (hasFill) ? this.color : '#000';
+
+ context.strokeStyle = (hasStroke) ? this.stroke : '#000';
+ context.lineWidth = (hasStroke) ? this.strokeThickness : 0;
+ context.lineCap = 'round';
+ context.lineJoin = 'round';
+
+ return this;
+ }
+
+ syncShadow(context) {
+ if (context.shadowColor != null) {
+ context.shadowColor = this.shadowColor;
+ context.shadowOffsetX = this.shadowOffsetX;
+ context.shadowOffsetY = this.shadowOffsetY;
+ context.shadowBlur = this.shadowBlur;
+ } else {
+ context.shadowColor = 0;
+ context.shadowOffsetX = 0;
+ context.shadowOffsetY = 0;
+ context.shadowBlur = 0;
+ }
+ }
+
+ getTextMetrics(context, text) {
+ this.syncFont(context).syncStyle(context);
+ return context.measureText(text);
+ }
+
+}
+
+export default TextStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.d.ts
new file mode 100644
index 000000000..c5c95cc5a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.d.ts
@@ -0,0 +1,8 @@
+import Base from '../Base';
+
+export default class Command extends Base {
+ readonly type: 'command';
+
+ name: string;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.js
new file mode 100644
index 000000000..2bc1fe80c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/command/Command.js
@@ -0,0 +1,49 @@
+import Base from '../Base.js';
+import { CmdTypeName } from '../Types.js';
+
+class Command extends Base {
+ constructor(parent, name, callback, param, scope) {
+ super(parent, CmdTypeName);
+
+ this
+ .setName(name)
+ .setParameter(param)
+ .setCallback(callback, scope);
+ }
+
+ setName(name) {
+ this.name = name;
+ return this;
+ }
+
+ setParameter(param) {
+ this.param = param;
+ return this;
+ }
+
+ setCallback(callback, scope) {
+ this.callback = callback;
+ this.scope = scope;
+ return this;
+ }
+
+ exec() {
+ var result;
+ if (this.scope) {
+ result = this.callback.call(this.scope, this.param, this.name);
+ } else {
+ result = this.callback(this.param, this.name);
+ }
+ return result;
+ }
+
+ onFree() {
+ super.onFree();
+ this
+ .setName()
+ .setCallback()
+ .setParameter()
+ }
+}
+
+export default Command;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.d.ts
new file mode 100644
index 000000000..5b3ee1147
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.d.ts
@@ -0,0 +1,13 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class Drawer extends RenderBase {
+ readonly type: 'drawer';
+
+ setRenderCallback(callback?: (this: Drawer) => void): this;
+
+ setDrawerSize(isAllSize: true): this;
+ setDrawerSize(width?: number, height?: number): this;
+ readonly drawerWidth: number;
+ readonly drawerHeight: number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.js
new file mode 100644
index 000000000..3d4fed472
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/drawer/Drawer.js
@@ -0,0 +1,85 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import { DrawerTypeName } from '../Types.js';
+
+class Drawer extends RenderBase {
+ constructor(parent, renderCallback, width, height) {
+ super(parent, DrawerTypeName);
+
+ this.setRenderCallback(renderCallback);
+ this.setDrawerSize(width, height);
+ }
+
+ setRenderCallback(callback) {
+ if (callback) {
+ this.renderContent = callback.bind(this);
+ } else {
+ delete this.renderContent;
+ }
+ return this;
+ }
+
+ setDrawerSize(width, height) {
+ // Whole canvas
+ if (width === true) {
+ this.toLocalPosition = false;
+ width = undefined;
+ height = undefined;
+ } else {
+ this.toLocalPosition = true;
+ }
+
+ if (width === undefined) {
+ width = 0;
+ }
+ if (height === undefined) {
+ height = width;
+ }
+
+ this.drawerWidth = width;
+ this.drawerHeight = height;
+
+ return this;
+ }
+
+ onFree() {
+ super.onFree();
+ this
+ .setRenderCallback()
+ }
+
+ get width() {
+ return this.drawerWidth * this.scaleX;
+ }
+
+ set width(value) {
+ this.setDirty(this.width !== value);
+ this.scaleX = (this.drawerWidth > 0) ? value / this.drawerWidth : 1;
+ }
+
+ get height() {
+ return this.drawerHeight * this.scaleY;
+ }
+
+ set height(value) {
+ this.setDirty(this.height !== value);
+ this.scaleY = (this.drawerHeight > 0) ? value / this.drawerHeight : 1;
+ }
+
+ get offsetY() {
+ return -this.height;
+ }
+
+ set offsetY(value) { }
+
+ get drawTLX() { return -this.leftSpace; }
+ get drawTLY() { return 0; }
+ get drawBLX() { return -this.leftSpace; }
+ get drawBLY() { return this.drawerHeight; }
+ get drawTRX() { return this.drawerWidth + this.rightSpace; }
+ get drawTRY() { return 0; }
+ get drawBRX() { return this.drawerWidth + this.rightSpace; }
+ get drawBRY() { return this.drawerHeight; }
+
+}
+
+export default Drawer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.d.ts
new file mode 100644
index 000000000..f16e06add
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.d.ts
@@ -0,0 +1,13 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class ImageData extends RenderBase {
+ readonly type: 'image';
+
+ setTexture(key: string, frame?: string | null): this;
+ key: string;
+ frame: string | null;
+
+ readonly frameWidth: number;
+ readonly frameHeight: number;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.js
new file mode 100644
index 000000000..85f705ff1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/image/ImageData.js
@@ -0,0 +1,106 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import { ImageTypeName } from '../Types.js';
+
+class ImageData extends RenderBase {
+ constructor(
+ parent,
+ key, frame
+ ) {
+ super(parent, ImageTypeName);
+ this.setTexture(key, frame);
+ }
+
+ get frameWidth() {
+ return (this.frameObj) ? this.frameObj.cutWidth : 0;
+ }
+
+ get frameHeight() {
+ return (this.frameObj) ? this.frameObj.cutHeight : 0;
+ }
+
+ get offsetY() {
+ return -this.height;
+ }
+
+ set offsetY(value) { }
+
+ get key() {
+ return this._key;
+ }
+
+ set key(value) {
+ this.setDirty(this._key != value);
+ this._key = value;
+ }
+
+ get frame() {
+ return this._frame;
+ }
+
+ set frame(value) {
+ this.setDirty(this._frame != value);
+ this._frame = value;
+ }
+
+ setTexture(key, frame) {
+ this.key = key;
+ this.frame = frame;
+
+ this.frameObj = this.scene.sys.textures.getFrame(key, frame);
+ return this;
+ }
+
+ get width() {
+ return this.frameWidth * this.scaleX;
+ }
+
+ set width(value) {
+ this.setDirty(this.width !== value);
+ this.scaleX = value / this.frameWidth;
+ }
+
+ get height() {
+ return this.frameHeight * this.scaleY;
+ }
+
+ set height(value) {
+ this.setDirty(this.height !== value);
+ this.scaleY = value / this.frameHeight;
+ }
+
+ setHeight(height, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+ this.height = height;
+
+ if (keepAspectRatio) {
+ this.scaleX = this.scaleY;
+ }
+ return this;
+ }
+
+ renderContent() {
+ var context = this.context;
+ var frame = this.frameObj;
+
+ var width = this.frameWidth,
+ height = this.frameHeight;
+ context.drawImage(
+ frame.source.image, // image
+ frame.cutX, frame.cutY, width, height,
+ 0, 0, width, height,
+ );
+ }
+
+ get drawTLX() { return -this.leftSpace; }
+ get drawTLY() { return 0; }
+ get drawBLX() { return -this.leftSpace; }
+ get drawBLY() { return this.frameHeight; }
+ get drawTRX() { return this.frameWidth + this.rightSpace; }
+ get drawTRY() { return 0; }
+ get drawBRX() { return this.frameWidth + this.rightSpace; }
+ get drawBRY() { return this.frameHeight; }
+}
+
+export default ImageData;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.d.ts
new file mode 100644
index 000000000..f53a51e71
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.d.ts
@@ -0,0 +1,17 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class InnerBounds extends RenderBase {
+ readonly type: 'innerbounds';
+
+ setColor(
+ color?: string | number | null,
+ color2?: string | number | null,
+ isHorizontalGradient?: boolean
+ ): this;
+
+ setStroke(
+ color?: string | number | null,
+ lineWidth?: number
+ ): this;
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.js
new file mode 100644
index 000000000..2ea48e8e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/innerbounds/InnerBounds.js
@@ -0,0 +1,145 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import GetStyle from '../../../../../utils/canvas/GetStyle.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class InnerBounds extends RenderBase {
+ constructor(parent, config) {
+ super(parent, 'innerbounds');
+
+ this.setColor(
+ GetValue(config, 'color', null),
+ GetValue(config, 'color2', null),
+ GetValue(config, 'horizontalGradient', true)
+ );
+
+ this.setStroke(
+ GetValue(config, 'stroke', null),
+ GetValue(config, 'strokeThickness', 2)
+ );
+ }
+
+ set color(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._color != value);
+ this._color = value;
+ }
+
+ get color() {
+ return this._color;
+ }
+
+ set color2(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._color2 != value);
+ this._color2 = value;
+ }
+
+ get color2() {
+ return this._color2;
+ }
+
+ set horizontalGradient(value) {
+ this.setDirty(this._horizontalGradient != value);
+ this._horizontalGradient = value;
+ }
+
+ get horizontalGradient() {
+ return this._horizontalGradient;
+ }
+
+ setColor(color, color2, isHorizontalGradient) {
+ if (isHorizontalGradient === undefined) {
+ isHorizontalGradient = true;
+ }
+
+ this.color = color;
+ this.color2 = color2;
+ this.horizontalGradient = isHorizontalGradient;
+ return this;
+ }
+
+ set stroke(value) {
+ value = GetStyle(value, this.canvas, this.context);
+ this.setDirty(this._stroke != value);
+ this._stroke = value;
+ }
+
+ get stroke() {
+ return this._stroke;
+ }
+
+ set strokeThickness(value) {
+ this.setDirty(this._strokeThickness != value);
+ this._strokeThickness = value;
+ }
+
+ get strokeThickness() {
+ return this._strokeThickness;
+ }
+
+ setStroke(color, lineWidth) {
+ if (color != null) {
+ if (lineWidth === undefined) {
+ lineWidth = 2;
+ }
+ }
+ this.stroke = color;
+ this.strokeThickness = lineWidth;
+ return this;
+ }
+
+ modifyPorperties(o) {
+ super.modifyPorperties(o);
+
+ if (o.hasOwnProperty('color')) {
+ this.setColor(
+ o.color,
+ GetValue(o, 'color2', null),
+ GetValue(o, 'horizontalGradient', true)
+ );
+ }
+ if (o.hasOwnProperty('stroke')) {
+ this.setStroke(
+ o.stroke,
+ GetValue(o, 'strokeThickness', 2)
+ );
+ }
+ }
+
+ renderContent() {
+ var padding = this.parent.padding;
+ var x = padding.left,
+ y = padding.top,
+ width = this.parent.width - padding.left - padding.right,
+ height = this.parent.height - padding.top - padding.bottom;
+ var context = this.context;
+ if (this.color != null) {
+ var fillStyle;
+ if (this.color2 != null) {
+ var grd;
+ if (this.horizontalGradient) {
+ grd = context.createLinearGradient(0, 0, width, 0);
+ } else {
+ grd = context.createLinearGradient(0, 0, 0, height);
+ }
+ grd.addColorStop(0, this.color);
+ grd.addColorStop(1, this.color2);
+ fillStyle = grd;
+ } else {
+ fillStyle = this.color;
+ }
+
+ context.fillStyle = fillStyle;
+ context.fillRect(x, y, width, height);
+ }
+
+ if ((this.stroke != null) && (this.strokeThickness > 0)) {
+ context.strokeStyle = this.stroke;
+ context.lineWidth = this.strokeThickness;
+ context.strokeRect(x, y, width, height);
+ }
+ }
+}
+
+export default InnerBounds;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Contains.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Contains.js
new file mode 100644
index 000000000..fbeb21c62
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Contains.js
@@ -0,0 +1,28 @@
+import CanvasPositionToBobPosition from '../../methods/utils/transform/CanvasPositionToBobPosition.js';
+
+const Rectangle = Phaser.Geom.Rectangle;
+
+var Contains = function (canvasX, canvasY) {
+ if ((this.width === 0) || (this.height === 0)) {
+ return false;
+ }
+
+ var bobPosition = CanvasPositionToBobPosition(canvasX, canvasY, this, true);
+ return GetBounds(this).contains(bobPosition.x, bobPosition.y);
+}
+
+var GetBounds = function (bob) {
+ if (globBounds === undefined) {
+ globBounds = new Rectangle();
+ }
+
+ var x = bob.drawTLX,
+ y = bob.drawTLY;
+ globBounds.setTo(x, y, (bob.drawTRX - x), (bob.drawBLY - y));
+
+ return globBounds;
+}
+
+var globBounds;
+
+export default Contains;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/GetWorldPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/GetWorldPosition.js
new file mode 100644
index 000000000..4b628942e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/GetWorldPosition.js
@@ -0,0 +1,7 @@
+import GetBobWorldPosition from '../../methods/utils/transform/GetBobWorldPosition.js';
+
+var GetWorldPosition = function (offsetX, offsetY, out) {
+ return GetBobWorldPosition(this.parent, this, offsetX, offsetY, out);
+}
+
+export default GetWorldPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Methods.js
new file mode 100644
index 000000000..7e09ee0b7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/Methods.js
@@ -0,0 +1,15 @@
+import RenderMethods from './RenderMethods.js';
+import Contains from './Contains.js';
+import GetWorldPosition from './GetWorldPosition.js';
+
+var Methods = {
+ contains: Contains,
+ getWorldPosition: GetWorldPosition,
+}
+
+Object.assign(
+ Methods,
+ RenderMethods
+)
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.d.ts
new file mode 100644
index 000000000..a3ba97be5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.d.ts
@@ -0,0 +1,74 @@
+// import * as Phaser from 'phaser';
+import Base from '../Base';
+
+export default RenderBase;
+
+declare namespace RenderBase {
+ type DrawCallbackType = (
+ bob: RenderBase
+ ) => void;
+}
+
+declare class RenderBase extends Base {
+ renderable: boolean;
+ removed: boolean;
+ toLocalPosition: boolean;
+
+ setVisible(visible?: boolean): this;
+ visible: boolean;
+
+ setAlpha(alpha: number): this;
+ alpha: number;
+
+ setPosition(x: number, y: number): this;
+ setX(x: number): this;
+ setY(y: number): this;
+ x: number;
+ y: number;
+
+ setAngle(degrees: number): this;
+ setRotation(radians: number): this;
+ angle: number;
+ rotation: number;
+
+ setScale(scaleX: number, scaleY?: number): this;
+ setScaleX(scaleX: number): this;
+ setScaleY(scaleY: number): this;
+ scaleX: number;
+ scaleY: number;
+
+ setWidth(width: number, keepAspectRatio?: boolean): this;
+ width: number;
+ setLeftSpace(value: number): this;
+ leftSpace: number;
+ setRightSpace(value: number): this;
+ rightSpace: number;
+ readonly outerWidth: number;
+
+ setHeight(height: number, keepAspectRatio?: boolean): this;
+ height: number;
+
+ setOrigin(x: number): this;
+ originX: number;
+ offsetX: number;
+ offsetY: number;
+
+ readonly willRender: boolean;
+
+ setDrawBelowCallback(
+ callback?: RenderBase.DrawCallbackType
+ ): this;
+
+ setDrawAboveCallback(
+ callback?: RenderBase.DrawCallbackType
+ ): this;
+
+ readonly drawTLX: number;
+ readonly drawTLY: number;
+ readonly drawBLX: number;
+ readonly drawBLY: number;
+ readonly drawTRX: number;
+ readonly drawTRY: number;
+ readonly drawBRX: number;
+ readonly drawBRY: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.js
new file mode 100644
index 000000000..3067d6568
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderBase.js
@@ -0,0 +1,337 @@
+import Base from '../Base.js';
+import Methods from './Methods.js';
+
+const DegToRad = Phaser.Math.DegToRad;
+const RadToDeg = Phaser.Math.RadToDeg;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class RenderBase extends Base {
+ constructor(parent, type) {
+ super(parent, type);
+
+ this.renderable = true;
+ this.toLocalPosition = true;
+ this.originX = 0;
+ this.offsetX = 0; // Override
+ this.offsetY = 0; // Override
+ }
+
+ get visible() {
+ return this._visible;
+ }
+
+ set visible(value) {
+ this.setDirty(this._visible != value);
+ this._visible = value;
+ }
+
+ setVisible(visible) {
+ if (visible === undefined) {
+ visible = true;
+ }
+
+ this.visible = visible;
+ return this;
+ }
+
+ get alpha() { return this._alpha; }
+
+ set alpha(value) {
+ this.setDirty(this._alpha != value);
+ this._alpha = value;
+ }
+
+ setAlpha(alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ get x() { return this._x; }
+
+ set x(value) {
+ this.setDirty(this._x != value);
+ this._x = value;
+ }
+
+ setX(x) {
+ this.x = x;
+ return this;
+ }
+
+ get y() { return this._y; }
+
+ set y(value) {
+ this.setDirty(this._y != value);
+ this._y = value;
+ }
+
+ setY(y) {
+ this.y = y;
+ return this;
+ }
+
+ setPosition(x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ }
+
+ setInitialPosition(x, y) {
+ this.x0 = x;
+ this.y0 = y;
+ return this;
+ }
+
+ get rotation() { return this._rotation; }
+
+ set rotation(value) {
+ this.setDirty(this._rotation != value);
+ this._rotation = value;
+ }
+
+ setRotation(rotation) {
+ this.rotation = rotation;
+ return this;
+ }
+
+ get angle() { return RadToDeg(this._rotation); }
+
+ set angle(value) {
+ this.rotation = DegToRad(value);
+ }
+
+ setAngle(angle) {
+ this.angle = angle;
+ return this;
+ }
+
+ get scaleX() { return this._scaleX; }
+
+ set scaleX(value) {
+ this.setDirty(this._scaleX !== value);
+ this._scaleX = value;
+ }
+
+ setScaleX(scaleX) {
+ this.scaleX = scaleX;
+ return this;
+ }
+
+ // Override
+ get width() { return 0; }
+
+ // Override
+ set width(value) { }
+
+ setWidth(width, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+ this.width = width;
+
+ if (keepAspectRatio) {
+ this.scaleY = this.scaleX;
+ }
+ return this;
+ }
+
+ get leftSpace() { return this._leftSpace; }
+
+ set leftSpace(value) {
+ this.setDirty(this._leftSpace !== value);
+ this._leftSpace = value;
+ }
+
+ setLeftSpace(value) {
+ this.leftSpace = value;
+ return this;
+ }
+
+ get rightSpace() { return this._rightSpace; }
+
+ set rightSpace(value) {
+ this.setDirty(this._rightSpace !== value);
+ this._rightSpace = value;
+ }
+
+ setRightSpace(value) {
+ this.rightSpace = value;
+ return this;
+ }
+
+ get outerWidth() {
+ return this.width + this.leftSpace + this.rightSpace;
+ }
+
+ get scaleY() { return this._scaleY; }
+
+ set scaleY(value) {
+ this.setDirty(this._scaleY !== value);
+ this._scaleY = value;
+ }
+
+ setScaleY(scaleY) {
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ // Override
+ get height() { return 0; }
+
+ // Override
+ set height(value) { }
+
+ setHeight(height, keepAspectRatio) {
+ if (keepAspectRatio === undefined) {
+ keepAspectRatio = false;
+ }
+ this.height = height;
+
+ if (keepAspectRatio) {
+ this.scaleX = this.scaleY;
+ }
+ return this;
+ }
+
+ setScale(scaleX, scaleY) {
+ if (scaleY === undefined) {
+ scaleY = scaleX;
+ }
+
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ return this;
+ }
+
+ setOrigin(x) {
+ this.originX = x;
+ return this;
+ }
+
+ setAlign(align) {
+ this.align = align;
+ return this;
+ }
+
+ modifyPorperties(o) {
+ if (!o) {
+ return this;
+ }
+
+ if (o.hasOwnProperty('x')) {
+ this.setX(o.x);
+ }
+ if (o.hasOwnProperty('y')) {
+ this.setY(o.y);
+ }
+
+ if (o.hasOwnProperty('rotation')) {
+ this.setRotation(o.rotation);
+ } else if (o.hasOwnProperty('angle')) {
+ this.setAngle(o.angle);
+ }
+
+ if (o.hasOwnProperty('alpha')) {
+ this.setAlpha(o.alpha);
+ }
+
+ // ScaleX, ScaleY
+ var width = GetValue(o, 'width', undefined);
+ var height = GetValue(o, 'height', undefined);
+ var scaleX = GetValue(o, 'scaleX', undefined);
+ var scaleY = GetValue(o, 'scaleY', undefined);
+
+ if (width !== undefined) {
+ if ((height === undefined) && (scaleY === undefined)) {
+ this.setWidth(width, true);
+ } else {
+ this.setWidth(width);
+ }
+ } else if (scaleX !== undefined) {
+ this.setScaleX(scaleX);
+ }
+ if (height !== undefined) {
+ if ((width === undefined) && (scaleX === undefined)) {
+ this.setHeight(height, true);
+ } else {
+ this.setHeight(height);
+ }
+ } else if (scaleY !== undefined) {
+ this.setScaleY(scaleY);
+ }
+
+ if (o.hasOwnProperty('leftSpace')) {
+ this.setLeftSpace(o.leftSpace);
+ }
+ if (o.hasOwnProperty('rightSpace')) {
+ this.setRightSpace(o.rightSpace);
+ }
+
+ if (o.hasOwnProperty('align')) {
+ this.setAlign(o.align);
+ }
+
+ return this;
+ }
+
+ setDrawBelowCallback(callback) {
+ this.drawBelowCallback = callback;
+ return this;
+ }
+
+ setDrawAboveCallback(callback) {
+ this.drawAboveCallback = callback;
+ return this;
+ }
+
+ reset() {
+ this
+ .setVisible()
+ .setAlpha(1)
+ .setPosition(0, 0)
+ .setRotation(0)
+ .setScale(1, 1)
+ .setLeftSpace(0).setRightSpace(0)
+ .setOrigin(0)
+ .setAlign()
+ .setDrawBelowCallback()
+ .setDrawAboveCallback()
+ return this;
+ }
+
+ // Override
+ get willRender() {
+ return this.visible && (this.alpha > 0);
+ }
+
+ get drawX() {
+ return this.x + this.leftSpace + this.offsetX - (this.originX * this.width);
+ }
+ get drawY() {
+ return this.y + this.offsetY;
+ }
+
+ // Override
+ get drawTLX() { return 0; }
+ get drawTLY() { return 0; }
+ get drawBLX() { return 0; }
+ get drawBLY() { return 0; }
+ get drawTRX() { return 0; }
+ get drawTRY() { return 0; }
+ get drawBRX() { return 0; }
+ get drawBRY() { return 0; }
+
+ get drawCenterX() {
+ return (this.drawTRX + this.drawTLX) / 2;
+ }
+ get drawCenterY() {
+ return (this.drawBLY + this.drawTLY) / 2;
+ }
+}
+
+Object.assign(
+ RenderBase.prototype,
+ Methods,
+)
+
+export default RenderBase;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderMethods.js
new file mode 100644
index 000000000..d7d3c12dd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/renderbase/RenderMethods.js
@@ -0,0 +1,43 @@
+export default {
+ // Override
+ renderContent() {
+
+ },
+
+ // Override
+ render() {
+ if (!this.willRender) {
+ return this;
+ }
+
+ var context = this.context;
+ context.save();
+ context.globalAlpha = this.alpha;
+
+ if (this.toLocalPosition) {
+ var x = this.drawX, y = this.drawY;
+ if (this.autoRound) {
+ x = Math.round(x);
+ y = Math.round(y);
+ }
+
+ context.translate(x, y);
+ context.scale(this.scaleX, this.scaleY);
+ context.rotate(this.rotation);
+ }
+
+ if (this.drawBelowCallback) {
+ this.drawBelowCallback(this);
+ }
+
+ this.renderContent();
+
+ if (this.drawAboveCallback) {
+ this.drawAboveCallback(this);
+ }
+
+ context.restore();
+
+ return this;
+ },
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.d.ts
new file mode 100644
index 000000000..9a083dc02
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.d.ts
@@ -0,0 +1,8 @@
+import RenderBase from '../renderbase/RenderBase';
+
+export default class Space extends RenderBase {
+ readonly type: 'space';
+
+ setSpaceWidth(width: number): this;
+ spaceWidth: number;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.js
new file mode 100644
index 000000000..0f0b8d10d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/space/Space.js
@@ -0,0 +1,32 @@
+import RenderBase from '../renderbase/RenderBase.js';
+import { SpaceTypeName } from '../Types.js';
+
+class Space extends RenderBase {
+ constructor(
+ parent,
+ width
+ ) {
+ super(parent, SpaceTypeName);
+ this.setSpaceWidth(width);
+ }
+
+ get width() {
+ return this.spaceWidth * this.scaleX;
+ }
+
+ set width(value) {
+ if (this.spaceWidth > 0) {
+ this.scaleX = value / this.spaceWidth;
+ } else {
+ this.scaleX = 1;
+ }
+ }
+
+ setSpaceWidth(width) {
+ this.spaceWidth = width;
+ return this;
+ }
+
+}
+
+export default Space;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/utils/GetProperty.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/utils/GetProperty.js
new file mode 100644
index 000000000..8c44d9639
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/bob/utils/GetProperty.js
@@ -0,0 +1,9 @@
+var GetProperty = function (name, config, defaultConfig) {
+ if (config.hasOwnProperty(name)) {
+ return config[name];
+ } else {
+ return defaultConfig[name];
+ }
+}
+
+export default GetProperty;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AddChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AddChild.js
new file mode 100644
index 000000000..a1e814c8c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AddChild.js
@@ -0,0 +1,33 @@
+// const RemoveItem = Phaser.Utils.Array.Remove;
+
+var AddChild = function (child, index) {
+ var areChildren = Array.isArray(child);
+
+ // Remove existed child(s)
+ // RemoveItem(this.children, child);
+
+ if ((index === undefined) || (index === this.children.length)) {
+ if (areChildren) {
+ this.children.push(...child);
+ } else {
+ this.children.push(child);
+ }
+ } else {
+ if (areChildren) {
+ this.children.splice(index, 0, ...child)
+ } else {
+ this.children.splice(index, 0, child);
+ }
+ }
+
+ this.lastAppendedChildren.length = 0;
+ if (areChildren) {
+ this.lastAppendedChildren.push(...child);
+ } else {
+ this.lastAppendedChildren.push(child);
+ }
+
+ return this;
+}
+
+export default AddChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendCommand.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendCommand.js
new file mode 100644
index 000000000..ff062cde9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendCommand.js
@@ -0,0 +1,8 @@
+var AppendCommand = function (name, callback, param, scope) {
+ var child = this.createCommandChild(name, callback, param, scope);
+ this.addChild(child);
+
+ return this;
+}
+
+export default AppendCommand;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendDrawer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendDrawer.js
new file mode 100644
index 000000000..257004a8f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendDrawer.js
@@ -0,0 +1,8 @@
+var AppendDrawer = function (renderCallback, width, height) {
+ var child = this.createDrawerChild(renderCallback, width, height);
+ this.addChild(child);
+
+ return this;
+};
+
+export default AppendDrawer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendImage.js
new file mode 100644
index 000000000..485f52739
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendImage.js
@@ -0,0 +1,8 @@
+var AppendImage = function (key, frame, properties) {
+ var child = this.createImageChild(key, frame, properties);
+ this.addChild(child);
+
+ return this;
+};
+
+export default AppendImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendSpace.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendSpace.js
new file mode 100644
index 000000000..73bbb047f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendSpace.js
@@ -0,0 +1,8 @@
+var AppendSpace = function (width) {
+ var child = this.createSpaceChild(width);
+ this.addChild(child);
+
+ return this;
+};
+
+export default AppendSpace;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendText.js
new file mode 100644
index 000000000..c68aaee86
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/AppendText.js
@@ -0,0 +1,7 @@
+var AppendText = function (text, style) {
+ var children = this.createCharChildren(text, style);
+ this.addChild(children);
+ return this;
+};
+
+export default AppendText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/BackgroundMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/BackgroundMethods.js
new file mode 100644
index 000000000..b8a6d0c43
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/BackgroundMethods.js
@@ -0,0 +1,16 @@
+export default {
+ setBackgroundColor(color, color2, isHorizontalGradient) {
+ this.background.setColor(color, color2, isHorizontalGradient);
+ return this;
+ },
+
+ setBackgroundStroke(color, lineWidth) {
+ this.background.setStroke(color, lineWidth);
+ return this;
+ },
+
+ setBackgroundCornerRadius(radius, iteration) {
+ this.background.setCornerRadius(radius, iteration);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ClearContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ClearContent.js
new file mode 100644
index 000000000..25a77066f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ClearContent.js
@@ -0,0 +1,7 @@
+var ClearContent = function() {
+ this.setText();
+ return this;
+}
+
+export default ClearContent;
+
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChild.js
new file mode 100644
index 000000000..405dcbde7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChild.js
@@ -0,0 +1,27 @@
+import CharData from '../bob/char/CharData.js';
+import { CharTypeName } from '../bob/Types.js';
+
+var CreateCharChild = function (text, style) {
+ if (style) {
+ this.textStyle.modify(style);
+ }
+
+ var child = this.poolManager.allocate(CharTypeName);
+ if (child === null) {
+ child = new CharData(
+ this, // parent
+ text, // text
+ this.textStyle, // style
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .modifyStyle(this.textStyle)
+ .setText(text);
+ }
+
+ return child;
+}
+
+export default CreateCharChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChildren.js
new file mode 100644
index 000000000..450568b55
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCharChildren.js
@@ -0,0 +1,34 @@
+import CharData from '../bob/char/CharData.js';
+import { CharTypeName } from '../bob/Types.js';
+
+var CreateCharChildren = function (text, style) {
+ if (style) {
+ this.textStyle.modify(style);
+ }
+
+ var children = [];
+ for (var i = 0, cnt = text.length; i < cnt; i++) {
+ var char = text.charAt(i);
+ var child = this.poolManager.allocate(CharTypeName);
+ if (child === null) {
+ child = new CharData(
+ this, // parent
+ char, // text
+ this.textStyle, // style
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .modifyStyle(this.textStyle)
+ .setText(char);
+ }
+ // child.modifyPorperties(properties); // Warning: Will modify text-style twice
+
+ children.push(child);
+ }
+
+ return children;
+}
+
+export default CreateCharChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCommandChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCommandChild.js
new file mode 100644
index 000000000..525283089
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateCommandChild.js
@@ -0,0 +1,26 @@
+import { CmdTypeName } from '../bob/Types.js';
+import Command from '../bob/command/Command.js';
+
+var CreateCommandChild = function (name, callback, param, scope) {
+ var child = this.poolManager.allocate(CmdTypeName);
+
+ if (child === null) {
+ child = new Command(
+ this, // parent
+ name,
+ callback, param, scope,
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .setName(name)
+ .setCallback(callback, scope)
+ .setParameter(param)
+
+ }
+
+ return child;
+}
+
+export default CreateCommandChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateDrawerChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateDrawerChild.js
new file mode 100644
index 000000000..a019a87ce
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateDrawerChild.js
@@ -0,0 +1,23 @@
+import Drawer from '../bob/drawer/Drawer.js';
+import { DrawerTypeName } from '../bob/Types.js';
+
+var CreateDrawerChild = function (renderCallback, width, height) {
+ var child = this.poolManager.allocate(DrawerTypeName);
+
+ if (child === null) {
+ child = new Drawer(
+ this, // parent
+ renderCallback,
+ width, height
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .setRenderCallback(renderCallback)
+ .setDrawerSize(width, height)
+ }
+
+ return child;
+}
+export default CreateDrawerChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateImageChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateImageChild.js
new file mode 100644
index 000000000..02cfbc973
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateImageChild.js
@@ -0,0 +1,24 @@
+import ImageData from '../bob/image/ImageData.js';
+import { ImageTypeName } from '../bob/Types.js';
+
+var CreateImageChild = function(key, frame, properties) {
+ var child = this.poolManager.allocate(ImageTypeName);
+
+ if (child === null) {
+ child = new ImageData(
+ this, // parent
+ key,
+ frame
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .setTexture(key, frame)
+ }
+ child.modifyPorperties(properties);
+
+ return child;
+}
+
+export default CreateImageChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateSpaceChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateSpaceChild.js
new file mode 100644
index 000000000..d0cd30ad6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/CreateSpaceChild.js
@@ -0,0 +1,21 @@
+import Space from '../bob/space/Space.js';
+import { SpaceTypeName } from '../bob/Types.js';
+
+var CreateSpaceChild = function (width) {
+ var child = this.poolManager.allocate(SpaceTypeName);
+
+ if (child === null) {
+ child = new Space(
+ this, // parent
+ width
+ );
+ } else {
+ child
+ .setParent(this)
+ .setActive()
+ .setSpaceWidth(width)
+ }
+ return child;
+}
+
+export default CreateSpaceChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachCharChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachCharChild.js
new file mode 100644
index 000000000..767c0f8b8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachCharChild.js
@@ -0,0 +1,35 @@
+import { IsChar } from '../bob/Types.js';
+
+var ForEachCharChild = function (callback, scope, activeOnly) {
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ var charIndex = 0;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ if (IsChar(child) && !child.removed) {
+ var isBreak;
+ if (scope) {
+ isBreak = callback.call(this, child, charIndex, children);
+ } else {
+ isBreak = callback(child, charIndex, children);
+ }
+ charIndex++;
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+
+ return this;
+}
+
+export default ForEachCharChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachChild.js
new file mode 100644
index 000000000..281025f11
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachChild.js
@@ -0,0 +1,31 @@
+var ForEachChild = function (callback, scope, activeOnly) {
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ var childIndex = 0;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ var isBreak;
+ if (scope) {
+ isBreak = callback.call(this, child, childIndex, children);
+ } else {
+ isBreak = callback(child, childIndex, children);
+ }
+ childIndex++;
+
+ if (isBreak) {
+ break;
+ }
+ }
+
+ return this;
+}
+
+export default ForEachChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachRenderableChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachRenderableChild.js
new file mode 100644
index 000000000..d29072ac5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ForEachRenderableChild.js
@@ -0,0 +1,33 @@
+var ForEachRenderableChild = function (callback, scope, activeOnly) {
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ var childIndex = 0;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ if (child.renderable && !child.removed) {
+ var isBreak;
+ if (scope) {
+ isBreak = callback.call(this, child, childIndex, children);
+ } else {
+ isBreak = callback(child, childIndex, children);
+ }
+ childIndex++;
+
+ if (isBreak) {
+ break;
+ }
+ }
+ }
+
+ return this;
+}
+
+export default ForEachRenderableChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetActiveChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetActiveChildren.js
new file mode 100644
index 000000000..ca55cd390
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetActiveChildren.js
@@ -0,0 +1,7 @@
+const GetAll = Phaser.Utils.Array.GetAll;
+
+var GetActiveChildren = function () {
+ return GetAll(this.children, 'active', true);
+}
+
+export default GetActiveChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChild.js
new file mode 100644
index 000000000..d0ef25b68
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChild.js
@@ -0,0 +1,27 @@
+import { IsChar } from '../bob/Types.js';
+
+var GetCharChild = function (charIndex, activeOnly) {
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ if (IsChar(child) && !child.removed) {
+ if (charIndex === 0) {
+ return child;
+ } else {
+ charIndex--;
+ }
+ }
+ }
+
+ return undefined;
+}
+
+export default GetCharChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildIndex.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildIndex.js
new file mode 100644
index 000000000..d58bb48fe
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildIndex.js
@@ -0,0 +1,27 @@
+import { IsChar } from '../bob/Types.js';
+
+var GetCharChildIndex = function (charIndex, activeOnly) {
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ if (IsChar(child) && !child.removed) {
+ if (charIndex === 0) {
+ return i;
+ } else {
+ charIndex--;
+ }
+ }
+ }
+
+ return undefined;
+}
+
+export default GetCharChildIndex;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildren.js
new file mode 100644
index 000000000..6c88a9159
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharChildren.js
@@ -0,0 +1,13 @@
+var GetCharChildren = function (activeOnly, out) {
+ if (out === undefined) {
+ out = [];
+ }
+
+ this.forEachCharChild(function (child) {
+ out.push(child);
+ }, undefined, activeOnly);
+
+ return out;
+}
+
+export default GetCharChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharIndex.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharIndex.js
new file mode 100644
index 000000000..52ebc9ca9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharIndex.js
@@ -0,0 +1,34 @@
+import { IsChar } from '../bob/Types.js';
+
+var GetCharIndex = function (childIndex, activeOnly) {
+ if (typeof (childIndex) !== 'number') {
+ childIndex = this.children.indexOf(childIndex);
+ if (childIndex < 0) {
+ return null;
+ }
+ }
+
+ if (activeOnly === undefined) {
+ activeOnly = true;
+ }
+
+ var children = this.children;
+ if (childIndex >= children.length) {
+ childIndex = children.length;
+ }
+ var charIndex = 0;
+ for (var i = 0; i < childIndex; i++) {
+ var child = children[i];
+ if (activeOnly && !child.active) {
+ continue;
+ }
+
+ if (IsChar(child) && !child.removed) {
+ charIndex++;
+ }
+ }
+
+ return charIndex;
+}
+
+export default GetCharIndex;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharWorldPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharWorldPosition.js
new file mode 100644
index 000000000..030b5b634
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetCharWorldPosition.js
@@ -0,0 +1,11 @@
+import GetBobWorldPosition from './utils/transform/GetBobWorldPosition.js';
+
+var GetCharWorldPosition = function (child, offsetX, offsetY, out) {
+ if (typeof (child) === 'number') {
+ child = this.getCharChild(child, true);
+ }
+
+ return GetBobWorldPosition(this, child, offsetX, offsetY, out);
+}
+
+export default GetCharWorldPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetChildren.js
new file mode 100644
index 000000000..2c47837f1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetChildren.js
@@ -0,0 +1,5 @@
+var GetChildren = function () {
+ return this.children;
+}
+
+export default GetChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetLastAppendedChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetLastAppendedChildren.js
new file mode 100644
index 000000000..d09335ca2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetLastAppendedChildren.js
@@ -0,0 +1,5 @@
+var GetLastAppendedChildren = function () {
+ return this.lastAppendedChildren;
+}
+
+export default GetLastAppendedChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetNearestChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetNearestChild.js
new file mode 100644
index 000000000..47e31e88f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetNearestChild.js
@@ -0,0 +1,21 @@
+import GetBobCenterPosition from './utils/transform/GetBobCenterPosition';
+
+const GetDistance = Phaser.Math.Distance.BetweenPointsSquared;
+
+var GetNearestChild = function (canvasX, canvasY) {
+ var pointA = { x: canvasX, y: canvasY };
+
+ var minDistance = Infinity;
+ var nearestChild = null;
+ this.forEachRenderableChild(function (child) {
+ var distance = GetDistance(pointA, GetBobCenterPosition(child, true));
+ if (minDistance > distance) {
+ minDistance = distance;
+ nearestChild = child;
+ }
+ })
+
+ return nearestChild;
+}
+
+export default GetNearestChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetPadding.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetPadding.js
new file mode 100644
index 000000000..cbc3cb2d7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetPadding.js
@@ -0,0 +1,7 @@
+import { GetPadding as GetPaddingBase } from '../../../../utils/padding/PaddingMethods.js';
+
+var GetPadding = function (key) {
+ return GetPaddingBase(this.padding, key);
+}
+
+export default GetPadding;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetText.js
new file mode 100644
index 000000000..4d0bea4f7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/GetText.js
@@ -0,0 +1,9 @@
+var GetText = function (activeOnly) {
+ var text = ''
+ this.forEachCharChild(function (child) {
+ text += child.text;
+ }, undefined, activeOnly);
+ return text;
+}
+
+export default GetText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InnerBoundsMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InnerBoundsMethods.js
new file mode 100644
index 000000000..70b3f0e0d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InnerBoundsMethods.js
@@ -0,0 +1,11 @@
+export default {
+ setInnerBoundsColor(color, color2, isHorizontalGradient) {
+ this.innerBounds.setColor(color, color2, isHorizontalGradient);
+ return this;
+ },
+
+ setInnerBoundsStroke(color, lineWidth) {
+ this.innerBounds.setStroke(color, lineWidth);
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InsertText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InsertText.js
new file mode 100644
index 000000000..7ced5840b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/InsertText.js
@@ -0,0 +1,9 @@
+var InsertText = function (index, text, style) {
+ var children = this.createCharChildren(text, style);
+ index = this.getCharChildIndex(index, true);
+ this.addChild(children, index);
+
+ return this;
+};
+
+export default InsertText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/Methods.js
new file mode 100644
index 000000000..6030fd918
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/Methods.js
@@ -0,0 +1,124 @@
+import SetFixedSize from './SetFixedSize.js';
+import SetPadding from './SetPadding.js';
+import GetPadding from './GetPadding.js';
+import ModifyTextStyle from './ModifyTextStyle.js';
+import ModifyDefaultTextStyle from './ModifyDefaultTextStyle.js';
+import ResetTextStyle from './ResetTextStyle.js';
+import SetTestString from './SetTestString.js';
+
+import RemoveChild from './RemoveChild.js';
+import RemoveChildren from './RemoveChildren.js';
+import PopChild from './PopChild.js';
+import ClearContent from './ClearContent.js';
+import AddChild from './AddChild.js';
+import CreateCharChild from './CreateCharChild.js';
+import CreateCharChildren from './CreateCharChildren.js';
+import SetText from './SetText.js';
+import AppendText from './AppendText.js';
+import InsertText from './InsertText.js';
+import RemoveText from './RemoveText.js';
+import GetText from './GetText.js';
+import CreateImageChild from './CreateImageChild.js';
+import AppendImage from './AppendImage.js';
+import CreateDrawerChild from './CreateDrawerChild.js';
+import AppendDrawer from './AppendDrawer.js';
+import CreateSpaceChild from './CreateSpaceChild.js';
+import AppendSpace from './AppendSpace.js';
+import CreateCommandChild from './CreateCommandChild.js';
+import AppendCommand from './AppendCommand.js';
+import SetWrapConfig from './SetWrapConfig.js';
+import RunWordWrap from './RunWordWrap.js';
+import RunVerticalWrap from './RunVerticalWrap.js';
+import RunWrap from './RunWrap.js';
+import SetAlignMethods from './SetAlignMethods.js';
+import RenderContent from './RenderContent.js';
+
+import ForEachChild from './ForEachChild.js';
+import ForEachRenderableChild from './ForEachRenderableChild.js';
+import ForEachCharChild from './ForEachCharChild.js';
+import GetChildren from './GetChildren.js';
+import GetActiveChildren from './GetActiveChildren.js';
+import GetCharChildren from './GetCharChildren.js';
+import GetLastAppendedChildren from './GetLastAppendedChildren.js';
+import GetNearestChild from './GetNearestChild.js';
+import GetCharWorldPosition from './GetCharWorldPosition.js';
+import SetToMinSize from './SetToMinSize.js';
+import GetCharChildIndex from './GetCharChildIndex.js';
+import GetCharChild from './GetCharChild.js';
+import GetCharIndex from './GetCharIndex.js';
+
+import SetChildrenInteractiveEnable from './input/SetChildrenInteractiveEnable.js';
+import SetInteractive from './input/SetInteractive.js';
+
+import MoveChildMethods from './MoveChildMethods.js';
+import BackgroundMethods from './BackgroundMethods.js';
+import InnerBoundsMethods from './InnerBoundsMethods.js';
+
+var Methods = {
+ setFixedSize: SetFixedSize,
+ setPadding: SetPadding,
+ getPadding: GetPadding,
+ modifyTextStyle: ModifyTextStyle,
+ modifyDefaultTextStyle: ModifyDefaultTextStyle,
+ resetTextStyle: ResetTextStyle,
+ setTestString: SetTestString,
+
+ removeChild: RemoveChild,
+ removeChildren: RemoveChildren,
+ popChild: PopChild,
+ clearContent: ClearContent,
+ addChild: AddChild,
+ createCharChild: CreateCharChild,
+ createCharChildren: CreateCharChildren,
+ setText: SetText,
+ appendText: AppendText,
+ insertText: InsertText,
+ removeText: RemoveText,
+ getText: GetText,
+ createImageChild: CreateImageChild,
+ appendImage: AppendImage,
+ createDrawerChild: CreateDrawerChild,
+ appendDrawer: AppendDrawer,
+ createSpaceChild: CreateSpaceChild,
+ appendSpace: AppendSpace,
+ createCommandChild: CreateCommandChild,
+ appendCommand: AppendCommand,
+
+ setWrapConfig: SetWrapConfig,
+ runWordWrap: RunWordWrap,
+ runVerticalWrap: RunVerticalWrap,
+ runWrap: RunWrap,
+ renderContent: RenderContent,
+
+ forEachChild: ForEachChild,
+ forEachRenderableChild: ForEachRenderableChild,
+ forEachCharChild: ForEachCharChild,
+ getChildren: GetChildren,
+ getActiveChildren: GetActiveChildren,
+ getCharChildren: GetCharChildren,
+ getLastAppendedChildren: GetLastAppendedChildren,
+ getNearestChild: GetNearestChild,
+ getCharWorldPosition: GetCharWorldPosition,
+
+ setToMinSize: SetToMinSize,
+
+ getCharChildIndex: GetCharChildIndex,
+ getCharChild: GetCharChild,
+ getCharIndex: GetCharIndex,
+
+
+ setChildrenInteractiveEnable: SetChildrenInteractiveEnable,
+ setInteractive: SetInteractive,
+}
+
+Object.assign(
+ Methods,
+
+ MoveChildMethods,
+ BackgroundMethods,
+ InnerBoundsMethods,
+ SetAlignMethods,
+
+)
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyDefaultTextStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyDefaultTextStyle.js
new file mode 100644
index 000000000..bafb464aa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyDefaultTextStyle.js
@@ -0,0 +1,6 @@
+var ModifyDefaultTextStyle = function (style) {
+ this.defaultTextStyle.modify(style);
+ return this;
+};
+
+export default ModifyDefaultTextStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyTextStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyTextStyle.js
new file mode 100644
index 000000000..3492fefed
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ModifyTextStyle.js
@@ -0,0 +1,6 @@
+var ModifyTextStyle = function (style) {
+ this.textStyle.modify(style);
+ return this;
+};
+
+export default ModifyTextStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/MoveChildMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/MoveChildMethods.js
new file mode 100644
index 000000000..68b98754f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/MoveChildMethods.js
@@ -0,0 +1,38 @@
+const BringToTop = Phaser.Utils.Array.BringToTop;
+const SendToBack = Phaser.Utils.Array.SendToBack;
+const MoveUp = Phaser.Utils.Array.MoveUp;
+const MoveDown = Phaser.Utils.Array.MoveDown;
+const MoveAbove = Phaser.Utils.Array.MoveAbove;
+const MoveBelow = Phaser.Utils.Array.MoveBelow;
+
+export default {
+ moveChildToFist(child) {
+ SendToBack(this.children, child);
+ return this;
+ },
+
+ moveChildToLast(child) {
+ BringToTop(this.children, child);
+ return this;
+ },
+ movechildUp(child) {
+ MoveUp(this.children, child);
+ return this;
+ },
+
+ movechildDown(child) {
+ MoveDown(this.children, child);
+ return this;
+ },
+
+ movechildAbove(child, baseChild) {
+ MoveAbove(this.children, child, baseChild);
+ return this;
+ },
+
+ movechildBelow(child, baseChild) {
+ MoveBelow(this.children, child, baseChild);
+ return this;
+ },
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/PopChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/PopChild.js
new file mode 100644
index 000000000..3caf55423
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/PopChild.js
@@ -0,0 +1,11 @@
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+var PopChild = function (child) {
+ RemoveItem(this.children, child);
+ this.lastAppendedChildren.length = 0;
+ this.lastOverChild = null;
+ this.dirty = true;
+ return this;
+}
+
+export default PopChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChild.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChild.js
new file mode 100644
index 000000000..b5354ba3a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChild.js
@@ -0,0 +1,12 @@
+const RemoveItem = Phaser.Utils.Array.Remove;
+
+var RemoveChild = function (child) {
+ this.poolManager.free(child);
+ RemoveItem(this.children, child);
+ this.lastAppendedChildren.length = 0;
+ this.lastOverChild = null;
+ this.dirty = true;
+ return this;
+}
+
+export default RemoveChild;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChildren.js
new file mode 100644
index 000000000..9b50166ec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveChildren.js
@@ -0,0 +1,10 @@
+var RemoveChildren = function () {
+ this.poolManager.freeMultiple(this.children);
+ this.children.length = 0;
+ this.lastAppendedChildren.length = 0;
+ this.lastOverChild = null;
+ this.dirty = true;
+ return this;
+}
+
+export default RemoveChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveText.js
new file mode 100644
index 000000000..5f6f41a01
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RemoveText.js
@@ -0,0 +1,16 @@
+var RemoveText = function (index, length) {
+ if (length === undefined) {
+ length = 1;
+ }
+
+ for (var i = 0; i < length; i++) {
+ var childIndex = this.getCharChildIndex(index, true);
+ if (childIndex === undefined) {
+ break;
+ }
+ this.removeChild(this.children[childIndex]);
+ }
+ return this;
+}
+
+export default RemoveText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RenderContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RenderContent.js
new file mode 100644
index 000000000..66df4dbb7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RenderContent.js
@@ -0,0 +1,23 @@
+var RenderContent = function () {
+ this.clear();
+
+ this.setCanvasSize(this.width, this.height);
+
+ if (this.background.active) {
+ this.background.render();
+ }
+
+ var child;
+ for (var i = 0, cnt = this.children.length; i < cnt; i++) {
+ child = this.children[i];
+ if (child.active) {
+ child.render();
+ }
+ }
+
+ if (this.innerBounds.active) {
+ this.innerBounds.render();
+ }
+}
+
+export default RenderContent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ResetTextStyle.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ResetTextStyle.js
new file mode 100644
index 000000000..a60e96476
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/ResetTextStyle.js
@@ -0,0 +1,6 @@
+var ResetTextStyle = function () {
+ this.textStyle.copyFrom(this.defaultTextStyle);
+ return this;
+};
+
+export default ResetTextStyle;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunVerticalWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunVerticalWrap.js
new file mode 100644
index 000000000..5aa108cb7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunVerticalWrap.js
@@ -0,0 +1,13 @@
+import RunVerticalWrapBase from './wrap/runverticalwrap/RunVerticalWrap.js';
+
+const Merge = Phaser.Utils.Objects.Merge;
+
+var RunVerticalWrap = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ return RunVerticalWrapBase.call(this, Merge(config, this.wrapConfig));
+};
+
+export default RunVerticalWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWordWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWordWrap.js
new file mode 100644
index 000000000..0c843412d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWordWrap.js
@@ -0,0 +1,13 @@
+import RunWordWrapBase from './wrap/runwordwrap/RunWordWrap.js';
+
+const Merge = Phaser.Utils.Objects.Merge;
+
+var RunWordWrap = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+
+ return RunWordWrapBase.call(this, Merge(config, this.wrapConfig));
+};
+
+export default RunWordWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWrap.js
new file mode 100644
index 000000000..c7316e84d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/RunWrap.js
@@ -0,0 +1,15 @@
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var RunWrap = function (config) {
+ var wrapCallback = GetValue(this.wrapConfig, 'callback');
+ if (!wrapCallback) {
+ wrapCallback = GetValue(config, 'callback', this.runWordWrap);
+ }
+ if (typeof (wrapCallback) === 'string') {
+ wrapCallback = this[wrapCallback];
+ }
+
+ return wrapCallback.call(this, config);
+}
+
+export default RunWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetAlignMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetAlignMethods.js
new file mode 100644
index 000000000..5caacc4d9
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetAlignMethods.js
@@ -0,0 +1,11 @@
+export default {
+ setVAlign(align) {
+ this.wrapConfig.vAlign = align;
+ return this;
+ },
+
+ setHAlign(align) {
+ this.wrapConfig.hAlign = align;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetFixedSize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetFixedSize.js
new file mode 100644
index 000000000..bd5f2f84a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetFixedSize.js
@@ -0,0 +1,26 @@
+var SetFixedSize = function (width, height) {
+ if (width === undefined) {
+ width = 0;
+ }
+ if (height === undefined) {
+ height = 0;
+ }
+
+ var dirty = (this.fixedWidth !== width) || (this.fixedHeight !== height);
+ if (!dirty) {
+ return this;
+ }
+
+ this.fixedWidth = width;
+ this.fixedHeight = height;
+ this.dirty = true;
+
+ this.setCanvasSize(
+ (width > 0) ? width : this.width,
+ (height > 0) ? height : this.height
+ );
+
+ return this;
+}
+
+export default SetFixedSize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetPadding.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetPadding.js
new file mode 100644
index 000000000..696116466
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetPadding.js
@@ -0,0 +1,21 @@
+import { SetPadding as SetPaddingBase } from '../../../../utils/padding/PaddingMethods.js';
+
+var SetPadding = function (key, value) {
+ var padding = this.padding;
+ var paddingLeft = padding.left,
+ paddingRight = padding.right,
+ paddingTop = padding.top,
+ paddingBottom = padding.bottom;
+
+ SetPaddingBase(padding, key, value);
+
+ this.dirty = this.dirty ||
+ (paddingLeft != padding.left) ||
+ (paddingRight != padding.right) ||
+ (paddingTop != padding.top) ||
+ (paddingBottom != padding.bottom)
+ ;
+ return this;
+};
+
+export default SetPadding;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetTestString.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetTestString.js
new file mode 100644
index 000000000..10727c783
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetTestString.js
@@ -0,0 +1,6 @@
+var SetTestString = function (testString) {
+ this.testString = testString;
+ return this;
+}
+
+export default SetTestString;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetText.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetText.js
new file mode 100644
index 000000000..2908db8a7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetText.js
@@ -0,0 +1,15 @@
+import AppendText from './AppendText.js';
+
+var SetText = function (text, style) {
+ if (text === undefined) {
+ text = '';
+ }
+
+ this.removeChildren();
+ AppendText.call(this, text, style); // this.appendText might be override
+
+ this.dirty = true;
+ return this;
+};
+
+export default SetText;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetToMinSize.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetToMinSize.js
new file mode 100644
index 000000000..d273add7b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetToMinSize.js
@@ -0,0 +1,28 @@
+var SetToMinSize = function () {
+ var children = this.children;
+ var maxX = 0,
+ maxY = 0;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (!child.renderable || !child.active || !child.visible) {
+ continue;
+ }
+
+ var x0 = (child.x0 !== undefined) ? child.x0 : child.x;
+ var y0 = (child.y0 !== undefined) ? child.y0 : child.y;
+ maxX = Math.max(maxX, x0);
+ maxY = Math.max(maxY, y0);
+ }
+
+ var width = maxX + this.padding.left + this.padding.right + this.wrapPadding.left + this.wrapPadding.right;
+ var height = maxY + this.padding.top + this.padding.bottom + this.wrapPadding.top + this.wrapPadding.bottom;
+
+ // Ignore fixedWidth, and fixedHeight
+ if ((this.width !== width) || (this.height !== height)) {
+ this.dirty = true;
+ this.setCanvasSize(width, height);
+ }
+ return this;
+}
+
+export default SetToMinSize;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetWrapConfig.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetWrapConfig.js
new file mode 100644
index 000000000..a0ebc21e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/SetWrapConfig.js
@@ -0,0 +1,14 @@
+import DeepClone from '../../../../utils/object/DeepClone.js';
+
+var SetWrapConfig = function (config) {
+ if (config === undefined) {
+ config = {};
+ } else if (typeof (config) === 'object') {
+ config = DeepClone(config);
+ }
+
+ this.wrapConfig = config;
+ return this;
+}
+
+export default SetWrapConfig;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/GetFirstChildContains.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/GetFirstChildContains.js
new file mode 100644
index 000000000..8ab0ca739
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/GetFirstChildContains.js
@@ -0,0 +1,15 @@
+var GetFirstChildContains = function (children, x, y) {
+ var children = children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (!child.active || !child.renderable) {
+ continue;
+ }
+ if (child.contains(x, y)) {
+ return child;
+ }
+ }
+ return null;
+}
+
+export default GetFirstChildContains;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractive.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractive.js
new file mode 100644
index 000000000..883d8f938
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractive.js
@@ -0,0 +1,73 @@
+import GetFirstChildContains from './GetFirstChildContains.js';
+
+var SetChildrenInteractive = function () {
+ this
+ .on('pointerdown', OnPointerDown, this)
+
+ .on('pointerdown', OnPointerUp, this)
+
+ .on('pointermove', OnPointOverOut, this)
+ .on('pointerover', OnPointOverOut, this)
+ .on('pointerout', function (pointer, event) {
+ OnPointOverOut.call(this, pointer, null, null, event);
+ }, this)
+
+ return this;
+}
+
+var OnPointerDown = function (pointer, localX, localY, event) {
+ if (!this.childrenInteractiveEnable) {
+ return;
+ }
+
+ var child = GetFirstChildContains(this.children, localX, localY);
+ if (!child) {
+ return;
+ }
+
+ this.emit('child.pointerdown', child, pointer, localX, localY, event);
+}
+
+var OnPointerUp = function (pointer, localX, localY, event) {
+ if (!this.childrenInteractiveEnable) {
+ return;
+ }
+
+ var child = GetFirstChildContains(this.children, localX, localY);
+ if (!child) {
+ return;
+ }
+
+ this.emit('child.pointerup', child, pointer, localX, localY, event);
+}
+
+var OnPointOverOut = function (pointer, localX, localY, event) {
+ if (!this.childrenInteractiveEnable) {
+ return;
+ }
+
+ if (localX === null) { // Case of pointerout
+ if (this.lastOverChild !== null) {
+ this.emit('child.pointerout', this.lastOverChild, pointer, localX, localY, event);
+ this.lastOverChild = null;
+ }
+ return;
+ }
+
+ var child = GetFirstChildContains(this.children, localX, localY);
+ if (child === this.lastOverChild) {
+ return;
+ }
+
+ if (this.lastOverChild !== null) {
+ this.emit('child.pointerout', this.lastOverChild, pointer, localX, localY, event);
+ }
+
+ if (child !== null) {
+ this.emit('child.pointerover', child, pointer, localX, localY, event);
+ }
+
+ this.lastOverChild = child;
+}
+
+export default SetChildrenInteractive;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractiveEnable.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractiveEnable.js
new file mode 100644
index 000000000..592b2a166
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetChildrenInteractiveEnable.js
@@ -0,0 +1,15 @@
+var SetChildrenInteractiveEnable = function (enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ if (this.childrenInteractiveEnable !== enable) {
+ this.lastOverChild = null;
+ }
+
+ this.childrenInteractiveEnable = enable;
+
+ return this;
+}
+
+export default SetChildrenInteractiveEnable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetInteractive.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetInteractive.js
new file mode 100644
index 000000000..c248732c7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/input/SetInteractive.js
@@ -0,0 +1,17 @@
+import SetChildrenInteractive from './SetChildrenInteractive.js';
+
+const GameObject = Phaser.GameObjects.GameObject;
+
+var SetInteractive = function (hitArea, hitAreaCallback, dropZone) {
+ var isInteractived = !!this.input;
+
+ GameObject.prototype.setInteractive.call(this, hitArea, hitAreaCallback, dropZone);
+
+ if (!isInteractived) {
+ SetChildrenInteractive.call(this);
+ }
+
+ return this;
+}
+
+export default SetInteractive;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToCanvasPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToCanvasPosition.js
new file mode 100644
index 000000000..c4766c5dc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToCanvasPosition.js
@@ -0,0 +1,28 @@
+const RotateAround = Phaser.Math.RotateAround;
+
+var BobPositionToCanvasPosition = function (bob, bobX, bobY, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ if (globPoint === undefined) {
+ globPoint = {};
+ }
+ out = globPoint;
+ }
+
+ out.x = bobX;
+ out.y = bobY;
+
+ if (bob.rotation !== 0) {
+ RotateAround(out, 0, 0, bob.rotation);
+ }
+
+ out.x = (out.x * bob.scaleX) + bob.drawX;
+ out.y = (out.y * bob.scaleY) + bob.drawY;
+
+ return out;
+}
+
+var globPoint;
+
+export default BobPositionToCanvasPosition
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToWorldPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToWorldPosition.js
new file mode 100644
index 000000000..0899ae984
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/BobPositionToWorldPosition.js
@@ -0,0 +1,10 @@
+import BobPositionToCanvasPosition from './BobPositionToCanvasPosition.js';
+import GameObjectLocalXYToWorldXY from '../../../../../../utils/position/GameObjectLocalXYToWorldXY.js';
+
+var BobPositionToWorldPosition = function (dynamicText, bob, bobX, bobY, out) {
+ var localXY = BobPositionToCanvasPosition(bob, bobX, bobY, true);
+ var worldXY = GameObjectLocalXYToWorldXY(dynamicText, localXY.x, localXY.y, out);
+ return worldXY;
+}
+
+export default BobPositionToWorldPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/CanvasPositionToBobPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/CanvasPositionToBobPosition.js
new file mode 100644
index 000000000..0592e66d8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/CanvasPositionToBobPosition.js
@@ -0,0 +1,24 @@
+const RotateAround = Phaser.Math.RotateAround;
+
+var CanvasPositionToBobPosition = function (canvasX, canvasY, bob, out) {
+ if (out === undefined) {
+ out = {};
+ } else if (out === true) {
+ if (globPoint === undefined) {
+ globPoint = {};
+ }
+ out = globPoint;
+ }
+
+ out.x = (canvasX - bob.drawX) / bob.scaleX;
+ out.y = (canvasY - bob.drawY) / bob.scaleY;
+
+ if (bob.rotation !== 0) {
+ RotateAround(out, 0, 0, -bob.rotation);
+ }
+ return out;
+}
+
+var globPoint;
+
+export default CanvasPositionToBobPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobCenterPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobCenterPosition.js
new file mode 100644
index 000000000..331d443cf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobCenterPosition.js
@@ -0,0 +1,14 @@
+import BobPositionToCanvasPosition from './BobPositionToCanvasPosition.js';
+
+var GetBobCenterPosition = function (bob, offsetX, offsetY, out) {
+ if (typeof (offsetX) !== 'number') {
+ out = offsetX;
+ offsetX = 0;
+ offsetY = 0;
+ }
+ var bobX = bob.drawCenterX + offsetX;
+ var bobY = bob.drawCenterY + offsetY;
+ return BobPositionToCanvasPosition(bob, bobX, bobY, out);
+}
+
+export default GetBobCenterPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobWorldPosition.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobWorldPosition.js
new file mode 100644
index 000000000..0e4610bbd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/utils/transform/GetBobWorldPosition.js
@@ -0,0 +1,14 @@
+import BobPositionToWorldPosition from './BobPositionToWorldPosition.js';
+
+var GetBobWorldPosition = function (dynamicText, bob, offsetX, offsetY, out) {
+ if (typeof (offsetX) !== 'number') {
+ out = offsetX;
+ offsetX = 0;
+ offsetY = 0;
+ }
+ var bobX = bob.drawCenterX + offsetX;
+ var bobY = bob.drawCenterY + offsetY;
+ return BobPositionToWorldPosition(dynamicText, bob, bobX, bobY, out);
+}
+
+export default GetBobWorldPosition;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/GetChildrenAlign.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/GetChildrenAlign.js
new file mode 100644
index 000000000..5c68c2898
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/GetChildrenAlign.js
@@ -0,0 +1,12 @@
+var GetChildrenAlign = function (children) {
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (child.align !== undefined) {
+ return child.align;
+ }
+ }
+
+ return undefined;
+}
+
+export default GetChildrenAlign;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/OffsetChildren.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/OffsetChildren.js
new file mode 100644
index 000000000..cdc5fb822
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/OffsetChildren.js
@@ -0,0 +1,17 @@
+var OffsetChildren = function (children, offsetX, offsetY) {
+ if ((offsetX === 0) && (offsetY === 0)) {
+ return;
+ }
+
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (!child.renderable) {
+ continue;
+ }
+
+ child.x += offsetX;
+ child.y += offsetY;
+ }
+}
+
+export default OffsetChildren;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/AlignLines.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/AlignLines.js
new file mode 100644
index 000000000..f4c9e685c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/AlignLines.js
@@ -0,0 +1,65 @@
+import GetChildrenAlign from '../GetChildrenAlign.js';
+import OffsetChildren from '../OffsetChildren.js';
+
+var AlignLines = function (result, width, height) {
+ var hAlign = result.hAlign,
+ vAlign = result.vAlign;
+
+ var offsetX, offsetY;
+
+ var rtl = result.rtl;
+ var lines = result.lines,
+ lineWidth = result.lineWidth,
+ linesWidth = result.linesWidth;
+ switch (hAlign) {
+ case 1: // center
+ case 'center':
+ offsetX = (width - linesWidth) / 2
+ break;
+
+ case 2: // right
+ case 'right':
+ offsetX = width - linesWidth;
+ break;
+
+ default: // left
+ offsetX = 0;
+ break;
+ }
+ if (rtl) {
+ offsetX += lineWidth;
+ }
+
+ for (var li = 0, lcnt = lines.length; li < lcnt; li++) {
+ var line = lines[(rtl) ? (lcnt - li - 1) : li];
+ var children = line.children;
+ var lineHeight = line.height;
+
+ var lineVAlign = GetChildrenAlign(children);
+ if (lineVAlign === undefined) {
+ lineVAlign = vAlign;
+ }
+
+ switch (lineVAlign) {
+ case 1: // center
+ case 'center':
+ offsetY = (height - lineHeight) / 2;
+ break;
+
+ case 2: // bottom
+ case 'bottom':
+ offsetY = height - lineHeight;
+ break;
+
+ default: // top
+ offsetY = 0;
+ break;
+ }
+
+ OffsetChildren(children, offsetX, offsetY);
+
+ offsetX += lineWidth;
+ }
+}
+
+export default AlignLines;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/RunVerticalWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/RunVerticalWrap.js
new file mode 100644
index 000000000..b7cba1a97
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runverticalwrap/RunVerticalWrap.js
@@ -0,0 +1,192 @@
+import { SetPadding } from '../../../../../../utils/padding/PaddingMethods.js';
+import AlignLines from './AlignLines.js';
+import { IsNewLineChar, IsPageBreakChar } from '../../../bob/Types.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var RunVerticalWrap = function (config) {
+ // Parse parameters
+ var startIndex = GetValue(config, 'start', 0);
+
+ SetPadding(this.wrapPadding, GetValue(config, 'padding', 0));
+ var paddingVertical = this.padding.top + this.padding.bottom + this.wrapPadding.top + this.wrapPadding.bottom;
+ var paddingHorizontal = this.padding.left + this.padding.right + this.wrapPadding.left + this.wrapPadding.right;
+
+ var lineWidth = GetValue(config, 'lineWidth', undefined);
+ var maxLines;
+ if (lineWidth === undefined) {
+ // Calculate lineWidth via maxLines, in fixedWidth mode
+ maxLines = GetValue(config, 'maxLines', 0);
+ if (this.fixedWidth > 0) {
+ var innerWidth = this.fixedWidth - paddingHorizontal;
+ lineWidth = innerWidth / maxLines;
+ } else {
+ lineWidth = 0;
+ }
+ } else {
+ if (this.fixedWidth > 0) {
+ // Calculate maxLines via lineWidth, in fixedWidth mode
+ maxLines = GetValue(config, 'maxLines', undefined);
+ if (maxLines === undefined) {
+ var innerWidth = this.fixedWidth - paddingHorizontal;
+ maxLines = Math.floor(innerWidth / lineWidth) + 1;
+ }
+ } else {
+ maxLines = GetValue(config, 'maxLines', 0); // Default is show all lines
+ }
+
+ }
+ var showAllLines = (maxLines === 0);
+
+ // Get fixedChildHeight
+ var fixedChildHeight = GetValue(config, 'fixedChildHeight', undefined);
+ if (fixedChildHeight === undefined) {
+ var charPerLine = GetValue(config, 'charPerLine', undefined);
+ if (charPerLine !== undefined) {
+ var innerHeight = this.fixedHeight - paddingVertical;
+ fixedChildHeight = Math.floor(innerHeight / charPerLine);
+ } else {
+ // Use child.heigh as fixedChildHeight
+ }
+ }
+
+ // Get wrapHeight
+ var wrapHeight = GetValue(config, 'wrapHeight', undefined);
+ if (wrapHeight === undefined) {
+ if (this.fixedHeight > 0) {
+ wrapHeight = this.fixedHeight - paddingVertical;
+ } else {
+ wrapHeight = Infinity; // No word-wrap
+ }
+ }
+
+ var letterSpacing = GetValue(config, 'letterSpacing', 0);
+
+ var rtl = GetValue(config, 'rtl', true);
+ var hAlign = GetValue(config, 'hAlign', rtl ? 2 : 0);
+ var vAlign = GetValue(config, 'vAlign', 0);
+
+ var result = {
+ callback: 'runVerticalWrap',
+ start: startIndex, // Next start index
+ isLastPage: false, // Is last page
+ padding: this.wrapPadding,
+ lineWidth: lineWidth,
+ maxLines: maxLines,
+ fixedChildHeight: fixedChildHeight,
+ wrapHeight: wrapHeight,
+ letterSpacing: letterSpacing,
+ hAlign: hAlign,
+ vAlign: vAlign,
+ rtl: rtl,
+ children: [], // Word-wrap result
+ lines: [], // Word-wrap result in lines
+ maxLineHeight: 0,
+ linesWidth: 0
+ }
+
+ // Set all children to active
+ var children = this.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ children[i].setActive(false);
+ }
+
+ // Layout children
+ wrapHeight += letterSpacing;
+ var startX = this.padding.left + this.wrapPadding.left, // Reset x of each character in AlignLines method
+ startY = this.padding.top + this.wrapPadding.top,
+ x = startX,
+ y = startY;
+ var remainderHeight = wrapHeight,
+ childIndex = startIndex,
+ lastChildIndex = children.length;
+ var resultChildren = result.children;
+ var resultLines = result.lines,
+ lastLine = [], lastLineHeight = 0, maxLineHeight = 0;
+ while (childIndex < lastChildIndex) {
+ // Append non-typeable child directly
+ var child = children[childIndex];
+ childIndex++;
+ if (!child.renderable) {
+ child.setActive();
+ resultChildren.push(child);
+ lastLine.push(child);
+ continue;
+ }
+
+ var childHeight = ((fixedChildHeight !== undefined) ? fixedChildHeight : child.height) + letterSpacing;
+ // Next line
+ var isNewLineChar = IsNewLineChar(child);
+ var isPageBreakChar = IsPageBreakChar(child);
+ var isControlChar = isNewLineChar || isPageBreakChar;
+ if ((remainderHeight < childHeight) || isControlChar) {
+ // Add to result
+ if (isNewLineChar) {
+ child.setActive().setPosition(x, y).setOrigin(0.5);
+ resultChildren.push(child);
+ lastLine.push(child);
+ }
+
+ // Move cursor
+ x = startX;
+ y = startY;
+ remainderHeight = wrapHeight;
+ resultLines.push({ children: lastLine, height: lastLineHeight });
+ maxLineHeight = Math.max(maxLineHeight, lastLineHeight);
+
+ lastLineHeight = 0;
+ lastLine = [];
+
+ var isPageEnd = isPageBreakChar ||
+ (!showAllLines && (resultLines.length === maxLines)); // Exceed maxLines
+ if (isPageEnd) {
+ break;
+ } else if (isControlChar) { // Already add to result
+ continue;
+ }
+ }
+ remainderHeight -= childHeight;
+ lastLineHeight += childHeight;
+
+ child.setActive().setPosition(x, y).setOrigin(0.5);
+ resultChildren.push(child);
+ lastLine.push(child);
+ y += childHeight;
+ }
+
+ if (lastLine.length > 0) {
+ resultLines.push({ children: lastLine, height: lastLineHeight });
+ maxLineHeight = Math.max(maxLineHeight, lastLineHeight);
+ }
+
+ result.start += resultChildren.length;
+ result.isLastPage = (result.start === lastChildIndex);
+ result.maxLineHeight = maxLineHeight;
+ result.linesWidth = (resultLines.length * lineWidth);
+
+ // Calculate size of game object
+ var width = (this.fixedWidth > 0) ? this.fixedWidth : (result.linesWidth + paddingHorizontal);
+ var height = (this.fixedHeight > 0) ? this.fixedHeight : (result.maxLineHeight + paddingVertical);
+
+ // Size might be changed after wrapping
+ var innerWidth = width - paddingHorizontal;
+ var innerHeight = height - paddingVertical;
+ AlignLines(result, innerWidth, innerHeight);
+
+ // Resize
+ this.setCanvasSize(width, height);
+
+ // Set initial position
+ for (var i = 0, cnt = resultChildren.length; i < cnt; i++) {
+ var child = resultChildren[i];
+ if (!child.renderable) {
+ continue;
+ }
+ child.x0 = child.x;
+ child.y0 = child.y;
+ }
+
+ return result;
+}
+
+export default RunVerticalWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/AlignLines.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/AlignLines.js
new file mode 100644
index 000000000..4095a719c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/AlignLines.js
@@ -0,0 +1,60 @@
+import GetChildrenAlign from '../GetChildrenAlign.js';
+import OffsetChildren from '../OffsetChildren.js';
+
+var AlignLines = function (result, width, height) {
+ var hAlign = result.hAlign,
+ vAlign = result.vAlign;
+
+ var offsetX, offsetY;
+
+ var linesHeight = result.linesHeight;
+ switch (vAlign) {
+ case 1: // center
+ case 'center':
+ offsetY = (height - linesHeight) / 2;
+ break;
+
+ case 2: // bottom
+ case 'bottom':
+ offsetY = height - linesHeight;
+ break;
+
+ default:
+ offsetY = 0;
+ break;
+ }
+
+ var lines = result.lines;
+ for (var li = 0, lcnt = lines.length; li < lcnt; li++) {
+ var line = lines[li];
+ var lineWidth = line.width,
+ children = line.children;
+
+ var lineHAlign = GetChildrenAlign(children);
+ if (lineHAlign === undefined) {
+ lineHAlign = hAlign;
+ }
+
+ switch (lineHAlign) {
+ case 1: // center
+ case 'center':
+ offsetX = (width - lineWidth) / 2
+ break;
+
+ case 2: // right
+ case 'right':
+ offsetX = width - lineWidth;
+ break;
+
+ default:
+ offsetX = 0;
+ break;
+ }
+
+ OffsetChildren(children, offsetX, offsetY);
+
+ }
+
+}
+
+export default AlignLines;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetDefaultTextHeight.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetDefaultTextHeight.js
new file mode 100644
index 000000000..1f02fed36
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetDefaultTextHeight.js
@@ -0,0 +1,21 @@
+var GetDefaultTextHeight = function () {
+ var metrics = this.defaultTextStyle.getTextMetrics(this.context, this.testString);
+ var ascent, descent;
+ if ('actualBoundingBoxAscent' in metrics) {
+ ascent = metrics.actualBoundingBoxAscent;
+ descent = metrics.actualBoundingBoxDescent;
+ } else {
+ ascent = 0;
+ descent = 0;
+ }
+
+ Result.ascent = ascent;
+ Result.descent = descent;
+ Result.height = ascent + descent;
+
+ return Result;
+}
+
+var Result = {};
+
+export default GetDefaultTextHeight;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetWord.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetWord.js
new file mode 100644
index 000000000..8414fc2cf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/GetWord.js
@@ -0,0 +1,47 @@
+import { CharTypeName } from '../../../bob/Types.js';
+
+var GetWord = function (children, startIndex, charMode, result) {
+ if (result === undefined) {
+ result = { word: [], width: 0 };
+ }
+
+ result.word.length = 0;
+
+ var endIndex = children.length;
+ var currentIndex = startIndex;
+ var word = result.word, wordWidth = 0;
+ while (currentIndex < endIndex) {
+ var child = children[currentIndex];
+ // Can't render (command child), put into output directly
+ if (!child.renderable) {
+ word.push(child);
+ currentIndex++;
+ continue;
+ }
+
+ var text = (child.type === CharTypeName) ? child.text : null;
+ if ((text !== null) &&
+ (text !== ' ') && (text !== '\n') && (text !== '\f')
+ ) {
+ word.push(child);
+ wordWidth += child.outerWidth;
+ currentIndex++;
+ // Continue
+ } else { // Get image child, a space, a new-line, or page-break
+ if (currentIndex === startIndex) { // Single child
+ word.push(child);
+ wordWidth += child.outerWidth;
+ }
+ break;
+ }
+
+ if (charMode) { // Word only contains 1 character
+ break;
+ }
+ }
+
+ result.width = wordWidth;
+ return result;
+}
+
+export default GetWord;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/RunWordWrap.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/RunWordWrap.js
new file mode 100644
index 000000000..c678098a2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/methods/wrap/runwordwrap/RunWordWrap.js
@@ -0,0 +1,209 @@
+import { SetPadding } from '../../../../../../utils/padding/PaddingMethods.js';
+import GetWord from './GetWord.js';
+import AlignLines from './AlignLines.js';
+import { IsNewLineChar, IsPageBreakChar } from '../../../bob/Types.js';
+import GetDefaultTextHeight from './GetDefaultTextHeight.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var RunWordWrap = function (config) {
+ // Parse parameters
+ var startIndex = GetValue(config, 'start', 0);
+
+ SetPadding(this.wrapPadding, GetValue(config, 'padding', 0));
+ var paddingVertical = this.padding.top + this.padding.bottom + this.wrapPadding.top + this.wrapPadding.bottom;
+ var paddingHorizontal = this.padding.left + this.padding.right + this.wrapPadding.left + this.wrapPadding.right;
+
+ // Get lineHeight, maxLines
+ var lineHeight = GetValue(config, 'lineHeight');
+ var ascent = GetValue(config, 'ascent', lineHeight);
+ var maxLines;
+ if (lineHeight === undefined) {
+ // Calculate lineHeight
+ var useDefaultTextHeight = GetValue(config, 'useDefaultTextHeight', false);
+ maxLines = GetValue(config, 'maxLines', 0);
+ if ((this.fixedHeight > 0) && (!useDefaultTextHeight)) {
+ var innerHeight = this.fixedHeight - paddingVertical;
+ if (maxLines > 0) {
+ // Calculate lineHeight via maxLines, in fixedHeight mode
+ lineHeight = innerHeight / maxLines;
+ } else {
+ var textHeightResult = GetDefaultTextHeight.call(this);
+ lineHeight = textHeightResult.height;
+ ascent = textHeightResult.ascent;
+ // Calculate maxLines via (ascent, lineHeight), in fixedHeight mode
+ maxLines = Math.floor((innerHeight - ascent) / lineHeight);
+ }
+ } else {
+ var textHeightResult = GetDefaultTextHeight.call(this);
+ lineHeight = textHeightResult.height;
+ ascent = textHeightResult.ascent;
+ }
+
+ } else {
+ // Calculate maxLines
+ if (this.fixedHeight > 0) {
+ // Calculate maxLines via lineHeight, in fixedHeight mode
+ maxLines = GetValue(config, 'maxLines');
+ if (maxLines === undefined) {
+ var innerHeight = this.fixedHeight - paddingVertical;
+ maxLines = Math.floor(innerHeight / lineHeight);
+ }
+ } else {
+ maxLines = GetValue(config, 'maxLines', 0); // Default is show all lines
+ }
+
+ }
+
+ // If ascent is undefined, assign to lineHeight
+ if (ascent === undefined) {
+ ascent = lineHeight;
+ }
+
+ var showAllLines = (maxLines === 0);
+
+ // Get wrapWidth
+ var wrapWidth = GetValue(config, 'wrapWidth', undefined);
+ if (wrapWidth === undefined) {
+ if (this.fixedWidth > 0) {
+ wrapWidth = this.fixedWidth - paddingHorizontal;
+ } else {
+ wrapWidth = Infinity; // No word-wrap
+ }
+ }
+
+ var letterSpacing = GetValue(config, 'letterSpacing', 0);
+
+ var hAlign = GetValue(config, 'hAlign', 0);
+ var vAlign = GetValue(config, 'vAlign', 0);
+
+ var charWrap = GetValue(config, 'charWrap', false);
+
+ var result = {
+ callback: 'runWordWrap',
+ start: startIndex, // Next start index
+ isLastPage: false, // Is last page
+ padding: this.wrapPadding,
+ ascent: ascent,
+ lineHeight: lineHeight,
+ maxLines: maxLines,
+ wrapWidth: wrapWidth,
+ letterSpacing: letterSpacing,
+ hAlign: hAlign,
+ vAlign: vAlign,
+ charWrap: charWrap,
+ children: [], // Word-wrap result
+ lines: [], // Word-wrap result in lines
+ maxLineWidth: 0,
+ linesHeight: 0
+ }
+
+ // Set all children to inactive
+ var children = this.children;
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ children[i].setActive(false);
+ }
+
+ // Layout children
+ wrapWidth += letterSpacing;
+ var startX = this.padding.left + this.wrapPadding.left,
+ startY = this.padding.top + this.wrapPadding.top + ascent, // Start(baseline) from ascent, not 0
+ x = startX,
+ y = startY;
+ var remainderWidth = wrapWidth,
+ childIndex = startIndex,
+ lastChildIndex = children.length;
+ var resultChildren = result.children;
+ var resultLines = result.lines,
+ lastLine = [], lastLineWidth = 0, maxLineWidth = 0;
+ var wordResult;
+ while (childIndex < lastChildIndex) {
+ wordResult = GetWord(children, childIndex, charWrap, wordResult);
+ var word = wordResult.word;
+ var charCnt = word.length;
+ var wordWidth = wordResult.width + (charCnt * letterSpacing);
+
+ childIndex += charCnt;
+ // Next line
+ var isNewLineChar = IsNewLineChar(word[0]);
+ var isPageBreakChar = IsPageBreakChar(word[0]);
+ var isControlChar = isNewLineChar || isPageBreakChar;
+ if ((remainderWidth < wordWidth) || isControlChar) {
+ // Add to result
+ if (isControlChar) {
+ var char = word[0];
+ char.setActive().setPosition(x, y);
+ resultChildren.push(char);
+ lastLine.push(char);
+ }
+
+ // Move cursor
+ x = startX;
+ y += lineHeight;
+ remainderWidth = wrapWidth;
+ resultLines.push({ children: lastLine, width: lastLineWidth });
+ maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
+
+ lastLineWidth = 0;
+ lastLine = [];
+
+ var isPageEnd = isPageBreakChar ||
+ (!showAllLines && (resultLines.length === maxLines)); // Exceed maxLines
+ if (isPageEnd) {
+ break;
+ } else if (isControlChar) { // Already add to result
+ continue;
+ }
+ }
+ remainderWidth -= wordWidth;
+ lastLineWidth += wordWidth;
+
+ for (var i = 0, cnt = word.length; i < cnt; i++) {
+ var child = word[i];
+ child.setActive();
+ resultChildren.push(child);
+ lastLine.push(child);
+
+ if (child.renderable) {
+ child.setPosition(x, y);
+ x += (child.outerWidth + letterSpacing);
+ }
+ }
+ }
+
+ if (lastLine.length > 0) {
+ resultLines.push({ children: lastLine, width: lastLineWidth });
+ maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
+ }
+
+ result.start += resultChildren.length;
+ result.isLastPage = (result.start === lastChildIndex);
+ result.maxLineWidth = maxLineWidth;
+ result.linesHeight = (resultLines.length * lineHeight);
+
+ // Calculate size of game object
+ var width = (this.fixedWidth > 0) ? this.fixedWidth : (result.maxLineWidth + paddingHorizontal);
+ var height = (this.fixedHeight > 0) ? this.fixedHeight : (result.linesHeight + paddingVertical);
+
+ // Size might be changed after wrapping
+ var innerWidth = width - paddingHorizontal;
+ var innerHeight = height - paddingVertical;
+ AlignLines(result, innerWidth, innerHeight);
+
+ // Resize
+ this.setCanvasSize(width, height);
+
+ // Set initial position
+ for (var i = 0, cnt = resultChildren.length; i < cnt; i++) {
+ var child = resultChildren[i];
+ if (!child.renderable) {
+ continue;
+ }
+ child.x0 = child.x;
+ child.y0 = child.y;
+ }
+
+ return result;
+};
+
+export default RunWordWrap;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/poolmanager/PoolManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/poolmanager/PoolManager.js
new file mode 100644
index 000000000..e09974ccb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/dynamictext/poolmanager/PoolManager.js
@@ -0,0 +1,44 @@
+import Pool from '../../../../pool.js';
+
+const GetFastValue = Phaser.Utils.Objects.GetFastValue;
+
+var Pools = {};
+class PoolManager {
+ constructor(config) {
+ this.pools = GetFastValue(config, 'pools', Pools);
+ }
+
+ free(bob) {
+ if (!this.pools) {
+ return this;
+ }
+
+ var bobType = bob.type;
+ if (!this.pools.hasOwnProperty(bobType)) {
+ this.pools[bobType] = new Pool();
+ }
+ this.pools[bobType].push(bob);
+ bob.onFree();
+ return this;
+ }
+
+ freeMultiple(arr) {
+ if (!this.pools) {
+ return this;
+ }
+
+ for (var i = 0, cnt = arr.length; i < cnt; i++) {
+ this.free(arr[i]);
+ }
+ return this;
+ }
+
+ allocate(bobType) {
+ if (!this.pools || !this.pools.hasOwnProperty(bobType)) {
+ return null;
+ }
+ return this.pools[bobType].pop();
+ }
+}
+
+export default PoolManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Creator.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Creator.js
new file mode 100644
index 000000000..05c054924
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Creator.js
@@ -0,0 +1,16 @@
+import TextPlayer from './TextPlayer.js'
+
+const GetAdvancedValue = Phaser.Utils.Objects.GetAdvancedValue;
+const BuildGameObject = Phaser.GameObjects.BuildGameObject;
+
+export default function (config, addToScene) {
+ if (config === undefined) { config = {}; }
+ if (addToScene !== undefined) {
+ config.add = addToScene;
+ }
+ var width = GetAdvancedValue(config, 'width', undefined);
+ var height = GetAdvancedValue(config, 'height', undefined);
+ var gameObject = new TextPlayer(this.scene, 0, 0, width, height, config);
+ BuildGameObject(this.scene, gameObject, config);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Factory.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Factory.js
new file mode 100644
index 000000000..f4604e70c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/Factory.js
@@ -0,0 +1,7 @@
+import TextPlayer from './TextPlayer.js'
+
+export default function (x, y, width, height, config) {
+ var gameObject = new TextPlayer(this.scene, x, y, width, height, config);
+ this.scene.add.existing(gameObject);
+ return gameObject;
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.d.ts
new file mode 100644
index 000000000..36d82a638
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.d.ts
@@ -0,0 +1,161 @@
+// import * as Phaser from 'phaser';
+import DynamicText from '../dynamictext/DynamicText';
+import Parser from '../../../bracketparser';
+import Managers from '../../../logic/runcommands/managers/Managers';
+
+export default TextPlayer;
+
+declare namespace TextPlayer {
+
+ interface IConfigParser {
+ delimiters?: string,
+ comment?: string,
+ translateTagNameCallback?: (s: string) => string,
+ }
+
+ interface IConfigTyping {
+ speed?: number,
+ onTypingStart?: (children: DynamicText.RenderChildTypes[]) => void,
+ animation?: {
+ duration?: number,
+ yoyo?: boolean,
+ onStart?: (child: DynamicText.RenderChildTypes) => void,
+ onProgress: (child: DynamicText.RenderChildTypes, t: number) => void,
+ onComplete: (child: DynamicText.RenderChildTypes) => void
+ },
+ skipSpace?: boolean,
+ minSizeEnable?: boolean,
+
+ fadeOutPage?: (children: DynamicText.RenderChildTypes[])
+ => void | Phaser.Events.EventEmitter | Promise;
+ }
+
+ interface IConfigImages {
+ [name: string]: {
+ width?: number,
+ height?: number,
+ key?: string,
+ frame?: string
+ }
+ }
+
+ interface ISpriteGameObjectConfig {
+ createGameObject?: 'sprite' | 'image' | Managers.CreateGameObjectCallbackType,
+
+ fade?: number | {
+ mode?: 0 | 1 | 'tint' | 'alpha',
+ time?: number
+ },
+
+ viewportCoordinate?: boolean | {
+ enable?: boolean,
+ viewport?: Phaser.Geom.Rectangle
+ }
+ }
+
+ type NextPageInputTypes = string | ((callback: Function) => void) | null;
+
+ type ClickTrgetTypes = Phaser.GameObjects.GameObject | Phaser.Scene;
+
+ interface IConfig extends DynamicText.IConfig {
+ parser?: IConfigParser,
+
+ typing?: IConfigTyping,
+
+ images?: IConfigImages,
+
+ sounds?: Managers.IConfigSounds,
+
+ sprites?: ISpriteGameObjectConfig | false,
+
+ nextPageInput?: NextPageInputTypes,
+
+ clickTarget?: ClickTrgetTypes,
+
+ text?: string
+ }
+
+ namespace Events {
+ type TypingCompleteCallbackType = () => void;
+
+ type TypingChildCallbackType = (
+ child: DynamicText.RenderChildTypes
+ ) => void
+
+ type PageStartCallbackType = () => void;
+
+ type PageCompleteCallbackType = () => void;
+
+ type WaitClickCallbackType = () => void;
+
+ type WaitKeyDownCallbackType = (keyName: string) => void;
+
+ type WaitTimeCallbackType = (time: number) => void;
+
+ type WaitMusicCompleteCallbackType = (
+ music: Phaser.Sound.BaseSound
+ ) => void;
+
+ type WaitCameraEffectCompleteCallbackType = (effectName: string) => void;
+
+ type WaitSpriteActionCompleteCallbackType = (name?: string, prop?: string) => void;
+
+ type WaitCallbackType = (
+ callback: () => void
+ ) => void;
+
+ type ParseCustomTagOnCallbackType = (parser: Parser, ...values: any) => void;
+ type ExecuteCustomTagOnCallbackType = (...values: any) => void;
+ type ParseCustomTagOffCallbackType = (parser: Parser) => void;
+ type ExecuteCustomTagOffCallbackType = () => void;
+ }
+}
+
+declare class TextPlayer extends DynamicText {
+ constructor(
+ scene: Phaser.Scene,
+ config?: TextPlayer.IConfig
+ );
+
+ addGameObjectManager(config: Managers.IGameObjectConfig): this;
+
+ play(content: string): this;
+ playPromise(content: string): Promise;
+
+ showPage(): this;
+ typingNextPage(): this;
+
+ pause(): this;
+ pauseTyping(): this;
+ resume(): this;
+
+ setTypingSpeed(speed: number): this;
+ typingSpeed: number;
+ setTimeScale(timeScale: number): this;
+ timeScale: number;
+
+ readonly isPlaying: boolean;
+ readonly isPageTyping: boolean;
+
+ addImage(config: TextPlayer.IConfigImages): this;
+
+ ignoreNextPageInput(enable?: boolean): this;
+ setClickTarget(clickTarget: TextPlayer.ClickTrgetTypes): this;
+ readonly clickTarget: TextPlayer.ClickTrgetTypes;
+
+ setTargetCamera(camera: Phaser.Cameras.Scene2D.BaseCamera): this;
+ readonly targetCamera: Phaser.Cameras.Scene2D.BaseCamera;
+
+ getGameObject(
+ goType: string,
+ name: string
+ ): Phaser.GameObjects.GameObject;
+ getGameObject(
+ goType: string,
+ ): { [name: string]: Phaser.GameObjects.GameObject }
+ addGameObject(
+ goType: string,
+ name: string,
+ gameObject: Phaser.GameObjects.GameObject
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.js
new file mode 100644
index 000000000..25e77e84a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/TextPlayer.js
@@ -0,0 +1,134 @@
+import Extend from '../../../utils/managers/Extend.js';
+import DynamicText from '../dynamictext/DynamicText.js';
+import Parser from './parser/Parser.js';
+import TypeWriter from './typewriter/TypeWriter.js';
+import ImageManager from '../../../utils/texture/imagemanager/ImageManager.js';
+import AddSpriteManager from './methods/spritemanager/AddSpriteManager.js';
+import Methods from './methods/Methods.js';
+import ClearEvents from './methods/utils/ClearEvents.js';
+
+const IsPlainObject = Phaser.Utils.Objects.IsPlainObject;
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TextPlayer extends Extend(DynamicText) {
+ constructor(scene, x, y, fixedWidth, fixedHeight, config) {
+ if (IsPlainObject(x)) {
+ config = x;
+ } else if (IsPlainObject(fixedWidth)) {
+ config = fixedWidth;
+ }
+ if (config === undefined) {
+ config = {};
+ }
+
+ // Don't set text in DynamicText's constructor
+ var content = config.text;
+ delete config.text;
+
+ super(scene, x, y, fixedWidth, fixedHeight, config);
+ this.type = 'rexTextPlayer';
+
+ this.parser = new Parser(this, GetValue(config, 'parser', undefined));
+
+ this.typeWriter = new TypeWriter(this, GetValue(config, 'typing', undefined));
+
+ this._imageManager = undefined;
+ var imageData = GetValue(config, 'images', undefined);
+ if (imageData) {
+ this.addImage(imageData);
+ }
+
+ this.setTargetCamera(GetValue(config, 'camera', this.scene.sys.cameras.main));
+
+ this.initManagers(scene, config);
+
+ var spriteManagerConfig = GetValue(config, 'sprites');
+ if ((spriteManagerConfig !== false) && (spriteManagerConfig !== null)) {
+ AddSpriteManager.call(this, spriteManagerConfig);
+ }
+
+ this.setIgnoreNextPageInput(GetValue(config, 'ignoreNextPageInput', false));
+ this.setClickTarget(GetValue(config, 'clickTarget', this)); // this.clickEE
+ this.setNextPageInput(GetValue(config, 'nextPageInput', null));
+
+ this.isPlaying = false;
+
+ if (content) {
+ this.play(content);
+ }
+ }
+
+ get imageManager() {
+ if (this._imageManager === undefined) {
+ this._imageManager = new ImageManager(this.scene);
+ }
+ return this._imageManager;
+ }
+
+ get spriteManager() {
+ return this.getGameObjectManager('sprite');
+ }
+
+ destroy(fromScene) {
+ // This Game Object has already been destroyed
+ if (!this.scene || this.ignoreDestroy) {
+ return;
+ }
+
+ ClearEvents(this);
+
+ this.parser.destroy();
+ this.parser = undefined;
+
+ this.typeWriter.destroy(fromScene);
+ this.typeWriter = undefined;
+
+ if (this._imageManager) {
+ this._imageManager.destroy(fromScene);
+ }
+ this._imageManager = undefined;
+
+ this.targetCamera = undefined;
+
+ this.clickEE = undefined;
+
+ this.destroyManagers(fromScene);
+
+ super.destroy(fromScene);
+ }
+
+ get isPageTyping() {
+ return this.typeWriter.isPageTyping;
+ }
+
+ set defaultTypingSpeed(speed) {
+ this.typeWriter.setDefaultTypingSpeed(speed);
+ }
+
+ get defaultTypingSpeed() {
+ return this.typeWriter.defaultTypingSpeed;
+ }
+
+ set typingSpeed(speed) {
+ this.typeWriter.setTypingSpeed(speed);
+ }
+
+ get typingSpeed() {
+ return this.typeWriter.speed;
+ }
+
+ set timeScale(value) {
+ this.setTimeScale(value);
+ }
+
+ get timeScale() {
+ return this.getTimeScale();
+ }
+}
+
+Object.assign(
+ TextPlayer.prototype,
+ Methods
+);
+
+export default TextPlayer;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/AddImage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/AddImage.js
new file mode 100644
index 000000000..94743ce85
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/AddImage.js
@@ -0,0 +1,6 @@
+var AddImage = function (key, config) {
+ this.imageManager.add(key, config);
+ return this;
+}
+
+export default AddImage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ContentMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ContentMethods.js
new file mode 100644
index 000000000..a0e6e0c05
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ContentMethods.js
@@ -0,0 +1,12 @@
+export default {
+ setContentOutputEnable(enable) {
+ this.parser.setContentOutputEnable(enable);
+ return this;
+ },
+
+ setContentCallback(callback, scope) {
+ this.contentCallback = callback;
+ this.contentCallbackScope = scope;
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Methods.js
new file mode 100644
index 000000000..7647e8052
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Methods.js
@@ -0,0 +1,41 @@
+import GameObjectManagerMethods from './gameobjectmanager/GameObjectManagerMethods.js';
+import SetClickTarget from './SetClickTarget.js';
+import SetTargetCamera from './SetTargetCamera.js';
+import SetNextPageInput from './SetNextPageInput.js';
+import AddImage from './AddImage.js';
+import PlayMethods from './PlayMethods.js';
+import TypingNextPage from './TypingNextPage.js';
+import PauseMethods from './PauseMethods.js';
+import ResumeMethods from './ResumeMethods.js';
+import Wait from './Wait.js';
+import TypingSpeedMethods from './TypingSpeedMethods.js';
+import SetIgnoreWait from './SetIgnoreWait.js';
+import SetIgnoreNextPageInput from './SetIgnoreNextPageInput.js';
+import ShowPage from './ShowPage.js';
+import SpriteMethods from './spritemanager/SpriteMethods.js';
+import ContentMethods from './ContentMethods.js';
+
+var Methods = {
+ setClickTarget: SetClickTarget,
+ setTargetCamera: SetTargetCamera,
+ setNextPageInput: SetNextPageInput,
+ addImage: AddImage,
+ typingNextPage: TypingNextPage,
+ wait: Wait,
+ setIgnoreWait: SetIgnoreWait,
+ setIgnoreNextPageInput: SetIgnoreNextPageInput,
+ showPage: ShowPage,
+}
+
+Object.assign(
+ Methods,
+ GameObjectManagerMethods,
+ PlayMethods,
+ PauseMethods,
+ ResumeMethods,
+ TypingSpeedMethods,
+ SpriteMethods,
+ ContentMethods,
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PauseMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PauseMethods.js
new file mode 100644
index 000000000..eb0543111
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PauseMethods.js
@@ -0,0 +1,15 @@
+export default {
+ pause() {
+ // Pause typing, typing timer and animation progresses
+ this.timeline.pause();
+
+ return this;
+ },
+
+ pauseTyping() {
+ // Pause typing
+ this.typeWriter.pauseTyping();
+
+ return this;
+ }
+};
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PlayMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PlayMethods.js
new file mode 100644
index 000000000..34f2123ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/PlayMethods.js
@@ -0,0 +1,27 @@
+import { WaitComplete } from '../../../../utils/promise/WaitEvent.js';
+
+export default {
+ play(content) {
+ if (this.isPlaying) {
+ return this;
+ }
+
+ this.removeChildren();
+ this.parser.start(content); // Parse bbcode-content
+
+ this.isPlaying = true;
+ this.once('complete', function () {
+ this.isPlaying = false;
+ }, this);
+
+ this.lastWrapResult = undefined;
+ this.typingNextPage();
+ return this;
+ },
+
+ playPromise(content) {
+ var promise = WaitComplete(this);
+ this.play(content);
+ return promise;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ResumeMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ResumeMethods.js
new file mode 100644
index 000000000..d9c761018
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ResumeMethods.js
@@ -0,0 +1,15 @@
+export default {
+ resume() {
+ // Resume typing timer, animation progresses and typing
+ this.timeline.resume();
+
+ return this;
+ },
+
+ resumeTyping(offsetTime) {
+ // Resume typing
+ this.typeWriter.resumeTyping(offsetTime);
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetClickTarget.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetClickTarget.js
new file mode 100644
index 000000000..e99d46976
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetClickTarget.js
@@ -0,0 +1,17 @@
+import IsSceneObject from '../../../../utils/system/IsSceneObject.js';
+
+var SetClickTarget = function (target) {
+ this.clickTarget = target;
+
+ if (!target) {
+ this.clickEE = null;
+ } else if (IsSceneObject(target)) {
+ this.clickEE = target.input;
+ } else { // Assume that target is a gameObject
+ this.clickEE = target.setInteractive();
+ }
+
+ return this;
+}
+
+export default SetClickTarget;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreNextPageInput.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreNextPageInput.js
new file mode 100644
index 000000000..61b0fc7e3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreNextPageInput.js
@@ -0,0 +1,9 @@
+var SetIgnoreNextPageInput = function (enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.ignoreNextPageInput = enable;
+ return this;
+}
+
+export default SetIgnoreNextPageInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreWait.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreWait.js
new file mode 100644
index 000000000..3dbf521b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetIgnoreWait.js
@@ -0,0 +1,6 @@
+var SetIgnoreWait = function (value) {
+ this.typeWriter.setIgnoreWait(value);
+ return this;
+}
+
+export default SetIgnoreWait;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetNextPageInput.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetNextPageInput.js
new file mode 100644
index 000000000..8d782f3aa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetNextPageInput.js
@@ -0,0 +1,22 @@
+import GetWrapCallback from './utils/wait/GetWrapCallback.js';
+import WaitMultiple from './utils/wait/WaitMultiple.js';
+
+var SetNextPageInput = function (input) {
+ var textPlayer = this;
+ if (!input) {
+ this.nextPageInput = null;
+
+ } else if (typeof (input) === 'function') {
+ this.nextPageInput = function (callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope);
+ input.call(textPlayer, wrapCallback);
+ }
+
+ } else {
+ this.nextPageInput = function (callback, args, scope) {
+ WaitMultiple(textPlayer, input, callback, args, scope);
+ }
+ }
+}
+
+export default SetNextPageInput;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetTargetCamera.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetTargetCamera.js
new file mode 100644
index 000000000..b56ad47f2
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SetTargetCamera.js
@@ -0,0 +1,6 @@
+var SetTargetCamera = function (camera) {
+ this.targetCamera = camera;
+ return this;
+}
+
+export default SetTargetCamera;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ShowPage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ShowPage.js
new file mode 100644
index 000000000..9ff2d2e7f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/ShowPage.js
@@ -0,0 +1,33 @@
+var ShowPage = function () {
+ // Only can work after playing, and before processing last child
+ if (!this.isPlaying || !this.isPageTyping) {
+ return this;
+ }
+
+ // Save parameters
+ var typingSpeedSave = this.typeWriter.speed;
+ var ignoreWaitSave = this.typeWriter.ignoreWait;
+ var skipTypingAnimationSave = this.typeWriter.skipTypingAnimation;
+ var skipSoundEffectSave = this.typeWriter.skipSoundEffect;
+
+ this.typeWriter
+ .once('complete', function () {
+ // Recover parameters
+ this.typeWriter
+ .setTypingSpeed(typingSpeedSave)
+ .setIgnoreWait(ignoreWaitSave)
+ .setSkipTypingAnimation(skipTypingAnimationSave)
+ .setSkipSoundEffect(skipSoundEffectSave)
+
+ }, this)
+
+ .setTypingSpeed(0)
+ .skipCurrentTypingDelay()
+ .setIgnoreWait(true)
+ .setSkipTypingAnimation(true)
+ .setSkipSoundEffect(true)
+
+ return this;
+}
+
+export default ShowPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SpriteMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SpriteMethods.js
new file mode 100644
index 000000000..bdd6f4f2e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/SpriteMethods.js
@@ -0,0 +1,11 @@
+export default {
+ getSprite(name) {
+ return this.spriteManager.getGO(name);
+ },
+
+ addSprite(name, gameObject) {
+ this.spriteManager.addGO(name, gameObject);
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingNextPage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingNextPage.js
new file mode 100644
index 000000000..8d5bec84b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingNextPage.js
@@ -0,0 +1,50 @@
+import { StopPlayEvent } from './utils/Events.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+var TypingNextPage = function () {
+ if (!this.isPlaying || this.isPageTyping) {
+ return this;
+ }
+
+ this.typeWriter
+ .once('page.fadeout', _TypingNextPage, this)
+ .fadeOutPage();
+ return this;
+}
+
+var _TypingNextPage = function () {
+ var result = this.runWrap(this.lastWrapResult);
+ this.lastWrapResult = result;
+
+ this.emit('page.start');
+
+ var OnTypingPageComplete = function () {
+ this.emit(StopPlayEvent); // Clear registed StopPlayEvent
+ if (result.isLastPage) {
+ this.emit('complete');
+ } else {
+ this.emit('page.complete');
+
+ if (this.ignoreNextPageInput) {
+ TypingNextPage.call(this);
+ } else if (this.nextPageInput) {
+ this.nextPageInput(TypingNextPage, [], this);
+ } else {
+ // Stop here, don't typing next page.
+ }
+
+ }
+ }
+
+ // Remove event when typing pages has been canceled
+ this.once(StopPlayEvent, function () {
+ this.typeWriter.off('complete', OnTypingPageComplete, this);
+ })
+
+ this.typeWriter
+ .once('complete', OnTypingPageComplete, this)
+ .start(result.children);
+}
+
+export default TypingNextPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingSpeedMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingSpeedMethods.js
new file mode 100644
index 000000000..fb90d6f49
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/TypingSpeedMethods.js
@@ -0,0 +1,11 @@
+export default {
+ setDefaultTypingSpeed(speed) {
+ this.defaultTypingSpeed = speed;
+ return this;
+ },
+
+ setTypingSpeed(speed) {
+ this.typingSpeed = speed;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Wait.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Wait.js
new file mode 100644
index 000000000..38d10895a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/Wait.js
@@ -0,0 +1,6 @@
+var Wait = function (name) {
+ this.typeWriter.wait(name);
+ return this;
+}
+
+export default Wait;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/GameObjectManagerMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/GameObjectManagerMethods.js
new file mode 100644
index 000000000..2824dcea3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/GameObjectManagerMethods.js
@@ -0,0 +1,43 @@
+import GameObjectManagerMethods from '../../../../../utils/managers/GameObjectManagerMethods.js';
+import OnParseAddGameObjectTag from './OnParseAddGameObjectTag.js';
+import OnParseRemoveAllGameObjectsTag from './OnParseRemoveAllGameObjectsTag.js';
+import OnParseCallGameObjectMethodTag from './OnParseCallGameObjectMethodTag.js';
+import OnParseEaseGameObjectPropertyTag from './OnParseEaseGameObjectPropertyTag.js';
+
+const ParseCallbacks = [
+ OnParseAddGameObjectTag, OnParseRemoveAllGameObjectsTag,
+ OnParseCallGameObjectMethodTag,
+ OnParseEaseGameObjectPropertyTag
+];
+
+const AddGameObjectManager = GameObjectManagerMethods.addGameObjectManager;
+
+export default {
+ addGameObjectManager(config, GameObjectManagerClass) {
+ if (config === undefined) {
+ config = {};
+ }
+ var name = config.name;
+ if (!name) {
+ console.warn(`Parameter 'name' is required in TextPlayer.addGameObjectManager(config) method`);
+ }
+
+ AddGameObjectManager.call(this, config, GameObjectManagerClass);
+
+ // Register parse callbacks
+ var customParseCallbacks = config.parseCallbacks;
+ if (!customParseCallbacks) {
+ customParseCallbacks = ParseCallbacks;
+ } else {
+ customParseCallbacks = [
+ ...customParseCallbacks, // customParseCallbacks have higher priority
+ ...ParseCallbacks
+ ];
+ }
+ for (var i = 0, cnt = customParseCallbacks.length; i < cnt; i++) {
+ customParseCallbacks[i](this, this.parser, config);
+ }
+
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseAddGameObjectTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseAddGameObjectTag.js
new file mode 100644
index 000000000..5ac444597
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseAddGameObjectTag.js
@@ -0,0 +1,75 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var IsAddGameObjectTag = function (tags, goType) {
+ // goType.name
+ return (tags.length === 2) && (tags[0] === goType)
+}
+
+var OnParseAddGameObjectTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on('+', function (tag, ...args) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name=key,frame], or [goType.name]
+ var tags = tag.split('.');
+ var name;
+ if (IsAddGameObjectTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.add`, // name
+ AddGameObject, // callback
+ [goType, name, ...args], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+ .on('-', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [/goType.name]
+ var tags = tag.split('.');
+ var name;
+ if (IsAddGameObjectTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.remove`, // name
+ RemoveGameObject, // callback
+ [goType, name], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var AddGameObject = function (params) {
+ var goType, args;
+ [goType, ...args] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.add(...args);
+}
+
+var RemoveGameObject = function (params) {
+ var goType, args;
+ [goType, ...args] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.remove(...args);
+}
+
+export default OnParseAddGameObjectTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseCallGameObjectMethodTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseCallGameObjectMethodTag.js
new file mode 100644
index 000000000..291674861
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseCallGameObjectMethodTag.js
@@ -0,0 +1,63 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var IsPropTag = function (tags, goType) {
+ // goType.name.prop
+ return (tags.length === 3) && (tags[0] === goType);
+}
+
+var OnParseCallGameObjectMethodTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on(`+`, function (tag, ...parameters) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.methodName=value0,value1,value2...]
+ // [goType.name.prop=value]
+ var tags = tag.split('.');
+ var name, prop;
+ if (IsPropTag(tags, goType)) {
+ name = tags[1];
+ prop = tags[2];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.call`, // name
+ CallMethod, // callback
+ [goType, name, prop, ...parameters], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var CallMethod = function (params) {
+ var goType, name, prop, args;
+ [goType, name, prop, ...args] = params;
+ // this: textPlayer
+
+ var eventName = `${goType}.${prop}`;
+ this.emit(
+ eventName,
+ name, ...args
+ );
+ if (this.listenerCount(eventName) > 0) {
+ return;
+ }
+
+ var gameObjectManager = this.getGameObjectManager(goType);
+ if (gameObjectManager.hasMethod(name, prop)) {
+ // Is method
+ gameObjectManager.call(name, prop, ...args);
+ } else {
+ // Is property
+ gameObjectManager.setProperty(name, prop, args[0]);
+ }
+
+}
+
+export default OnParseCallGameObjectMethodTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseEaseGameObjectPropertyTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseEaseGameObjectPropertyTag.js
new file mode 100644
index 000000000..1ac5183a1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseEaseGameObjectPropertyTag.js
@@ -0,0 +1,99 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var EaseMode = {
+ to: true, yoyo: true, from: true,
+ toLeft: true, toRight: true, toUp: true, toDown: true,
+ yoyoLeft: true, yoyoRight: true, yoyoUp: true, yoyoDown: true,
+ fromLeft: true, fromRight: true, fromUp: true, fromDown: true,
+}
+var IsEasePropertyTag = function (tags, goType) {
+ // goType.name.prop.to
+ return (tags.length === 4) && (tags[0] === goType) && EaseMode[tags[3]];
+}
+
+var OnParseEaseGameObjectPropertyTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ var gameObjectManager = textPlayer.getGameObjectManager(goType);
+ parser
+ .on(`+`, function (tag, value, duration, ease, repeat) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.prop.to=value,duration]
+ // [goType.name.prop.to=value,duration,ease,repeat]
+ // [goType.name.prop.to=value,duration,repeat]
+ var tags = tag.split('.');
+ var name, property, easeMode;
+ if (IsEasePropertyTag(tags, goType)) {
+ name = tags[1];
+ property = tags[2];
+ easeMode = tags[3];
+ } else {
+ return;
+ }
+
+ if (typeof (ease) === 'number') {
+ repeat = ease;
+ ease = undefined;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.ease`, // name
+ EaseProperty, // callback
+ [
+ goType,
+ name, property, value,
+ duration, ease, repeat, easeMode
+ ], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var EaseProperty = function (params) {
+ var goType, name, property, value, duration, ease, repeat, easeMode;
+ [
+ goType,
+ name, property, value,
+ duration, ease, repeat, easeMode
+ ] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+
+ var currentValue = gameObjectManager.getProperty(name, property);
+ // Only can tween number property
+ if (typeof (currentValue) !== 'number') {
+ return;
+ }
+
+ if (easeMode.endsWith('Left') || easeMode.endsWith('Up')) {
+ if (easeMode.startsWith('to') || easeMode.startsWith('yoyo')) {
+ value = currentValue - value;
+ } else if (easeMode.startsWith('from')) {
+ gameObjectManager.setProperty(name, property, (currentValue - value));
+ value = currentValue;
+ }
+ } else if (easeMode.endsWith('Right') || easeMode.endsWith('Down')) {
+ if (easeMode.startsWith('to') || easeMode.startsWith('yoyo')) {
+ value = currentValue + value;
+ } else if (easeMode.startsWith('from')) {
+ gameObjectManager.setProperty(name, property, (currentValue + value));
+ value = currentValue;
+ }
+ } else if (easeMode === 'from') {
+ gameObjectManager.setProperty(name, property, value);
+ value = currentValue;
+ }
+
+ var isYoyo = easeMode.startsWith('yoyo');
+
+ gameObjectManager.easeProperty(
+ name, property, value,
+ duration, ease, repeat, isYoyo
+ );
+}
+
+export default OnParseEaseGameObjectPropertyTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseRemoveAllGameObjectsTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseRemoveAllGameObjectsTag.js
new file mode 100644
index 000000000..5c074ebba
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/gameobjectmanager/OnParseRemoveAllGameObjectsTag.js
@@ -0,0 +1,33 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseRemoveAllGameObjectsTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on('-', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [/goType]
+ if (tag === goType) {
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.removeall`, // name
+ RemoveAllSprites, // callback
+ goType, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var RemoveAllSprites = function (goType) {
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.removeAll();
+}
+
+export default OnParseRemoveAllGameObjectsTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/AddSpriteManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/AddSpriteManager.js
new file mode 100644
index 000000000..9ab7a4371
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/AddSpriteManager.js
@@ -0,0 +1,21 @@
+import SpriteManager from '../../../../../utils/sprite/spritemanager/SpriteManager.js';
+import OnParsePlayAnimationTag from './OnParsePlayAnimationTag.js';
+import OnParsePauseAnimationTag from './OnParsePauseAnimationTag.js';
+import OnParseChainAnimationTag from './OnParseChainAnimationTag.js';
+
+const ParseCallbacks = [
+ OnParsePlayAnimationTag,
+ OnParsePauseAnimationTag,
+ OnParseChainAnimationTag,
+];
+
+var AddSpriteManager = function (config) {
+ if (config === undefined) {
+ config = {};
+ }
+ config.name = 'sprite';
+ config.parseCallbacks = ParseCallbacks;
+ this.addGameObjectManager(config, SpriteManager);
+}
+
+export default AddSpriteManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParseChainAnimationTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParseChainAnimationTag.js
new file mode 100644
index 000000000..2ca6ae3a8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParseChainAnimationTag.js
@@ -0,0 +1,45 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var IsChainAnimationTag = function (tags, goType) {
+ // goType.name.chain
+ return (tags.length === 3) && (tags[0] === goType) && (tags[2] === 'chain');
+}
+
+var OnParseChainAnimationTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on('+', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.chain=key]
+ var tags = tag.split('.');
+ var name;
+ if (IsChainAnimationTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ var keys = Array.prototype.slice.call(arguments, 1);
+ AppendCommandBase.call(textPlayer,
+ `${goType}.chain`, // name
+ ChainAnimation, // callback
+ [goType, name, keys], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var ChainAnimation = function (params) {
+ var goType, args;
+ [goType, ...args] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.chainAnimation(...args);
+}
+
+export default OnParseChainAnimationTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePauseAnimationTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePauseAnimationTag.js
new file mode 100644
index 000000000..1cdddbdb4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePauseAnimationTag.js
@@ -0,0 +1,44 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var IsPauseAnimationTag = function (tags, goType) {
+ // goType.name.pause
+ return (tags.length === 3) && (tags[0] === goType) && (tags[2] === 'pause');
+}
+
+var OnParsePauseAnimationTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on('+', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.pause=key]
+ var tags = tag.split('.');
+ var name;
+ if (IsPauseAnimationTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.pause`, // name
+ PauseAnimation, // callback
+ [goType, name], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var PauseAnimation = function (params) {
+ var goType, args;
+ [goType, ...args] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.pauseAnimation(...args);
+}
+
+export default OnParsePauseAnimationTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePlayAnimationTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePlayAnimationTag.js
new file mode 100644
index 000000000..0b94a4317
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/OnParsePlayAnimationTag.js
@@ -0,0 +1,108 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var IsPlayAnimationTag = function (tags, goType) {
+ // goType.name.play
+ return (tags.length === 3) && (tags[0] === goType) && (tags[2] === 'play');
+}
+
+var IsStopAnimationTag = function (tags, goType) {
+ // goType.name.stop
+ return (tags.length === 3) && (tags[0] === goType) && (tags[2] === 'stop');
+}
+
+var OnParsePlayAnimationTag = function (textPlayer, parser, config) {
+ var goType = config.name;
+ parser
+ .on('+', function (tag, ...keys) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.play=key], or [goType.name.play=key0,key1,...]
+ var tags = tag.split('.');
+ var name;
+ if (IsPlayAnimationTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.play`, // name
+ PlayAnimation, // callback
+ [goType, name, keys], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+ .on('+', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [goType.name.stop]
+ var tags = tag.split('.');
+ var name;
+ if (IsStopAnimationTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.stop`, // name
+ StopAnimation, // callback
+ [goType, name], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+ .on('-', function (tag) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ // [/goType.name.play]
+ var tags = tag.split('.');
+ var name;
+ if (IsPlayAnimationTag(tags, goType)) {
+ name = tags[1];
+ } else {
+ return;
+ }
+
+ AppendCommandBase.call(textPlayer,
+ `${goType}.stop`, // name
+ StopAnimation, // callback
+ [goType, name], // params
+ textPlayer, // scope
+ );
+
+ parser.skipEvent();
+ })
+}
+
+var PlayAnimation = function (params) {
+ var goType, name, keys;
+ [goType, name, keys] = params;
+ var key = keys.shift();
+
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.playAnimation(name, key);
+ if (keys.length > 0) {
+ gameObjectManager.chainAnimation(name, keys);
+ }
+}
+
+var StopAnimation = function (params) {
+ var goType, args;
+ [goType, ...args] = params;
+ // this: textPlayer
+ var gameObjectManager = this.getGameObjectManager(goType);
+ gameObjectManager.stopAnimation(...args);
+}
+
+export default OnParsePlayAnimationTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/SpriteMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/SpriteMethods.js
new file mode 100644
index 000000000..57cdbb186
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/spritemanager/SpriteMethods.js
@@ -0,0 +1,11 @@
+export default {
+ getSprite(name) {
+ return this.getGameObject('sprite', name);
+ },
+
+ addSprite(name, gameObject) {
+ this.addGameObject('sprite', name, gameObject);
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/ClearEvents.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/ClearEvents.js
new file mode 100644
index 000000000..6e5736f79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/ClearEvents.js
@@ -0,0 +1,9 @@
+import { ClearEvents as Events } from './Events.js';
+
+var ClearEvents = function (textPlayer) {
+ for (var i = 0, cnt = Events.length; i < cnt; i++) {
+ textPlayer.emit(Events[i]);
+ }
+}
+
+export default ClearEvents;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Events.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Events.js
new file mode 100644
index 000000000..4c37982e1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Events.js
@@ -0,0 +1,15 @@
+// Internal events
+
+const RemoveWaitEvents = '_remove.wait';
+const StopPlayEvent = '_remove.play';
+
+const ClearEvents = [
+ RemoveWaitEvents,
+ StopPlayEvent
+]
+
+export {
+ RemoveWaitEvents,
+ StopPlayEvent,
+ ClearEvents
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Progress.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Progress.js
new file mode 100644
index 000000000..4723b40a7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/Progress.js
@@ -0,0 +1,5 @@
+var Progress = function (textPlayer, config) {
+ return textPlayer.typeWriter.timeline.addTimer(config);
+}
+
+export default Progress;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/GetWrapCallback.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/GetWrapCallback.js
new file mode 100644
index 000000000..6709d3a65
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/GetWrapCallback.js
@@ -0,0 +1,9 @@
+import { RemoveWaitEvents } from '../Events.js';
+
+var GetWrapCallback = function (textPlayer, callback, args, scope, removeFrom) {
+ return function () {
+ textPlayer.emit(RemoveWaitEvents, removeFrom); // Remove all wait events
+ callback.apply(scope, args);
+ }
+}
+export default GetWrapCallback;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCallback.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCallback.js
new file mode 100644
index 000000000..91a7fa130
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCallback.js
@@ -0,0 +1,10 @@
+import GetWrapCallback from './GetWrapCallback.js';
+
+var WaitCallback = function (textPlayer, postfixName, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, 'custom');
+
+ var eventName = (postfixName) ? `wait.${postfixName}` : 'wait';
+ textPlayer.emit(eventName, wrapCallback);
+}
+
+export default WaitCallback;
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCameraEffect.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCameraEffect.js
new file mode 100644
index 000000000..53a8d2ceb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitCameraEffect.js
@@ -0,0 +1,77 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var IsWaitCameraEffect = function (name) {
+ switch (name) {
+ case 'camera.fadein':
+ case 'camera.fadeout':
+ case 'camera.flash':
+ case 'camera.shake':
+ case 'camera.zoom':
+ case 'camera.rotate':
+ case 'camera.scroll':
+ return true;
+ default:
+ return false;
+ }
+}
+
+var WaitCameraEffect = function (textPlayer, effectName, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, `camera.${effectName}`);
+
+ var camera = textPlayer.targetCamera;
+
+ var effect, completeEventName;
+ switch (effectName) {
+ case 'camera.fadein':
+ effect = camera.fadeEffect;
+ completeEventName = 'camerafadeincomplete';
+ break;
+
+ case 'camera.fadeout':
+ effect = camera.fadeEffect;
+ completeEventName = 'camerafadeoutcomplete';
+ break;
+
+ case 'camera.flash':
+ effect = camera.flashEffect;
+ completeEventName = 'cameraflashcomplete';
+ break;
+
+ case 'camera.shake':
+ effect = camera.shakeEffect;
+ completeEventName = 'camerashakecomplete';
+ break;
+
+ case 'camera.zoom':
+ effect = camera.zoomEffect;
+ completeEventName = 'camerazoomcomplete';
+ break;
+
+ case 'camera.rotate':
+ effect = camera.rotateToEffect;
+ completeEventName = 'camerarotatecomplete';
+ break;
+
+ case 'camera.scroll':
+ effect = camera.panEffect;
+ completeEventName = 'camerapancomplete';
+ break;
+ }
+
+ if (!effect.isRunning) {
+ textPlayer.emit('wait.camera', effectName);
+ wrapCallback();
+
+ } else {
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function (removeFrom) {
+ camera.off(completeEventName, wrapCallback, textPlayer);
+ });
+ camera.once(completeEventName, wrapCallback, textPlayer);
+ textPlayer.emit('wait.camera', effectName);
+ }
+
+}
+
+export { IsWaitCameraEffect, WaitCameraEffect };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitClick.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitClick.js
new file mode 100644
index 000000000..05b85df99
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitClick.js
@@ -0,0 +1,23 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var WaitClick = function (textPlayer, callback, args, scope) {
+ var clickEE = textPlayer.clickEE;
+
+ if (!clickEE) {
+ return;
+ }
+
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, 'click');
+
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ clickEE.off('pointerdown', wrapCallback, textPlayer);
+ });
+
+ clickEE.once('pointerdown', wrapCallback, textPlayer);
+
+ textPlayer.emit('wait.click');
+}
+
+export default WaitClick;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitGameObject.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitGameObject.js
new file mode 100644
index 000000000..82111a40b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitGameObject.js
@@ -0,0 +1,108 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var IsWaitGameObject = function (textPlayer, name) {
+ var names = name.split('.');
+ return textPlayer.gameObjectManagers.hasOwnProperty(names[0]);
+}
+
+var WaitGameObject = function (textPlayer, tag, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope);
+ var tags = tag.split('.');
+ var goType = tags[0];
+ var gameObjectManager = textPlayer.getGameObjectManager(goType);
+ var waitEventName = `wait.${goType}`
+ switch (tags.length) {
+ case 1: // 'goType' : wait all sprites has beeen destroyed
+ if (gameObjectManager.isEmpty) {
+ textPlayer.emit(waitEventName);
+ wrapCallback();
+ } else {
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function (removeFrom) {
+ gameObjectManager.off('empty', wrapCallback, textPlayer);
+ });
+ gameObjectManager.once('empty', wrapCallback, textPlayer);
+ textPlayer.emit(waitEventName);
+ }
+ return;
+
+ case 2: // 'goType.name' : wait goType.name has been destroyed
+ var name = tags[1];
+ if (!gameObjectManager.has(name)) {
+ textPlayer.emit(waitEventName, name);
+ wrapCallback();
+ } else {
+ var spriteData = gameObjectManager.get(name);
+ var gameObject = spriteData.gameObject;
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ gameObject.off('destroy', wrapCallback, textPlayer);
+ });
+
+ gameObject.once('destroy', wrapCallback, textPlayer);
+ textPlayer.emit(waitEventName, name);
+ }
+ return;
+
+ case 3: // 'goType.name.prop' : wait ease goType.name.prop has been completed
+ var name = tags[1],
+ prop = tags[2];
+
+ var value = gameObjectManager.getProperty(name, prop);
+ // Can start tween task for a number property
+ if (typeof (value) === 'number') {
+ var task = gameObjectManager.getTweenTask(name, prop);
+ if (!task) {
+ textPlayer.emit(waitEventName, name, prop);
+ wrapCallback();
+ } else {
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ task.off('complete', wrapCallback, textPlayer);
+ });
+
+ task.once('complete', wrapCallback, textPlayer);
+ textPlayer.emit(waitEventName, name, prop);
+ }
+ return;
+ }
+
+ var dataKey = prop;
+ var matchFalseFlag = dataKey.startsWith('!');
+ if (matchFalseFlag) {
+ dataKey = dataKey.substring(1);
+ }
+ // Wait until flag is true/false
+ if (gameObjectManager.hasData(name, dataKey)) {
+ var gameObject = gameObjectManager.getGO(name);
+ var flag = gameObject.getData(dataKey);
+ var matchTrueFlag = !matchFalseFlag;
+ if (flag === matchTrueFlag) {
+ textPlayer.emit(waitEventName, name, prop);
+ wrapCallback();
+ } else {
+ // Remove all wait events
+ var eventName = `changedata-${dataKey}`;
+ var callback = function (gameObject, value, previousValue) {
+ value = !!value;
+ if (value === matchTrueFlag) {
+ wrapCallback.call(textPlayer);
+ }
+ }
+ textPlayer.once(RemoveWaitEvents, function () {
+ gameObject.off(eventName, callback);
+ });
+
+ gameObject.on(eventName, callback);
+ textPlayer.emit(waitEventName, name, prop);
+ }
+ return;
+ }
+
+ }
+
+}
+
+
+export { IsWaitGameObject, WaitGameObject };
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitKeyDown.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitKeyDown.js
new file mode 100644
index 000000000..7f5c9619e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitKeyDown.js
@@ -0,0 +1,20 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var WaitKeyDown = function (textPlayer, keyName, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, 'keydown');
+
+ var eventName = `keydown-${keyName.toUpperCase()}`;
+ var keyboard = textPlayer.scene.input.keyboard;
+
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ keyboard.off(eventName, wrapCallback, textPlayer);
+ });
+
+ keyboard.once(eventName, wrapCallback, textPlayer);
+
+ textPlayer.emit('wait.keydown', keyName);
+}
+
+export default WaitKeyDown;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMultiple.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMultiple.js
new file mode 100644
index 000000000..3299f1e8e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMultiple.js
@@ -0,0 +1,62 @@
+import WaitCallback from './WaitCallback.js';
+import WaitTime from './WaitTime.js';
+import WaitClick from './WaitClick.js';
+import WaitMusic from './WaitMusic.js';
+import { IsWaitCameraEffect, WaitCameraEffect } from './WaitCameraEffect.js';
+import WaitKeyDown from './WaitKeyDown.js';
+import { IsWaitGameObject, WaitGameObject } from './WaitGameObject.js'
+
+const KeyCodes = Phaser.Input.Keyboard.KeyCodes;
+
+var WaitMultiple = function (textPlayer, names, callback, args, scope) {
+ if ((typeof (names) === 'string') && (names.length > 1) && (names.indexOf('|') !== -1)) {
+ names = names.split('|');
+ } else {
+ names = [names];
+ }
+
+ for (var i = 0, cnt = names.length; i < cnt; i++) {
+ var name = names[i];
+
+ if ((name == null) || (name === 'wait')) { // Wait event
+ WaitCallback(textPlayer, undefined, callback, args, scope);
+
+ } else if ((typeof (name) === 'number') || !isNaN(name)) { // A number, or a number string
+ WaitTime(textPlayer, parseFloat(name), callback, args, scope);
+
+ } else if (name === 'click') { // 'click'
+ WaitClick(textPlayer, callback, args, scope);
+
+ } else if (name === 'se') {
+ var music = textPlayer.soundManager.getLastSoundEffect();
+ WaitMusic(textPlayer, music, callback, args, scope);
+
+ } else if (name === 'se2') {
+ var music = textPlayer.soundManager.getLastSoundEffect2();
+ WaitMusic(textPlayer, music, callback, args, scope);
+
+ } else if (name === 'bgm') {
+ var music = textPlayer.soundManager.getBackgroundMusic();
+ WaitMusic(textPlayer, music, callback, args, scope);
+
+ } else if (name === 'bgm2') {
+ var music = textPlayer.soundManager.getBackgroundMusic2();
+ WaitMusic(textPlayer, music, callback, args, scope);
+
+ } else if (KeyCodes.hasOwnProperty(name.toUpperCase())) {
+ WaitKeyDown(textPlayer, name, callback, args, scope);
+
+ } else if (IsWaitCameraEffect(name)) {
+ WaitCameraEffect(textPlayer, name, callback, args, scope);
+
+ } else if (IsWaitGameObject(textPlayer, name)) {
+ WaitGameObject(textPlayer, name, callback, args, scope);
+
+ } else {
+ WaitCallback(textPlayer, name, callback, args, scope);
+
+ }
+ }
+}
+
+export default WaitMultiple;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMusic.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMusic.js
new file mode 100644
index 000000000..ae63df344
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitMusic.js
@@ -0,0 +1,23 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var WaitMusic = function (textPlayer, music, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, 'music');
+
+ if (music) {
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ music.off('complete', wrapCallback, textPlayer);
+ });
+
+ music.once('complete', wrapCallback, textPlayer);
+ }
+
+ textPlayer.emit('wait.music', music);
+
+ if (!music) {
+ wrapCallback();
+ }
+}
+
+export default WaitMusic;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitTime.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitTime.js
new file mode 100644
index 000000000..9fecbe360
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/methods/utils/wait/WaitTime.js
@@ -0,0 +1,22 @@
+import GetWrapCallback from './GetWrapCallback.js';
+import { RemoveWaitEvents } from '../Events.js';
+
+var WaitTime = function (textPlayer, time, callback, args, scope) {
+ var wrapCallback = GetWrapCallback(textPlayer, callback, args, scope, 'time');
+
+ var timer;
+
+ // Remove all wait events
+ textPlayer.once(RemoveWaitEvents, function () {
+ if (timer) {
+ timer.remove();
+ timer = undefined;
+ }
+ });
+
+ timer = textPlayer.timeline.delayCall(time, wrapCallback);
+
+ textPlayer.emit('wait.time', time);
+}
+
+export default WaitTime;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/AddParseCallbacks.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/AddParseCallbacks.js
new file mode 100644
index 000000000..e6e2a8af7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/AddParseCallbacks.js
@@ -0,0 +1,69 @@
+import ParseColorTag from './textstyle/OnParseColorTag.js';
+import ParseStrokeColorTag from './textstyle/OnParseStrokeColorTag.js';
+import ParseBoldTag from './textstyle/OnParseBoldTag.js';
+import ParseItalicTag from './textstyle/OnParseItalicTag.js';
+import ParseFontSizeTag from './textstyle/OnParseFontSizeTag.js';
+import ParseOffsetYTag from './textstyle/OnParseOffsetYTag.js';
+import ParseOffsetXTag from './textstyle/OnParseOffsetXTag.js';
+import ParseLeftSpaceTag from './textstyle/OnParseLeftSpaceTag.js';
+import ParseRightSpaceTag from './textstyle/OnParseRightSpaceTag.js';
+import ParseShadowColorTag from './textstyle/OnParseShadowColorTag.js';
+import ParseAlignTag from './textstyle/OnParseAlignTag.js'
+import ParseImageTag from './image/OnParseImageTag.js';
+import ParseSpaceTag from './space/OnParseSpaceTag.js';
+import ParseTypingSpeedTag from './typing/OnParseTypingSpeedTag.js';
+import ParsePlaySoundEffectTag from './soundeffect/OnParsePlaySoundEffectTag.js';
+import ParseFadeInSoundEffectTag from './soundeffect/OnParseFadeInSoundEffectTag.js';
+import ParseFadeOutSoundEffectTag from './soundeffect/OnParseFadeOutSoundEffectTag.js';
+import ParseSetSoundEffectVolumeTag from './soundeffect/OnParseSetSoundEffectVolumeTag.js';
+import ParsePlayBackgroundMusicTag from './backgroundmusic/OnParsePlayBackgroundMusicTag.js';
+import ParseFadeInBackgroundMusicTag from './backgroundmusic/OnParseFadeInBackgroundMusicTag.js';
+import ParseFadeOutBackgroundMusicTag from './backgroundmusic/OnParseFadeOutBackgroundMusicTag.js';
+import ParseCrossFadeBackgroundMusicTag from './backgroundmusic/OnParseCrossFadeBackgroundMusicTag.js';
+import ParsePauseBackgroundMusicTag from './backgroundmusic/OnParsePauseBackgroundMusicTag.js';
+import ParseFadeInCameraTag from './camera/OnParseFadeInCameraTag.js';
+import ParseFadeOutCameraTag from './camera/OnParseFadeOutCameraTag.js';
+import ParseShakeCameraTag from './camera/OnParseShakeCameraTag.js';
+import ParseFlashCameraTag from './camera/OnParseFlashCameraTag.js';
+import ParseZoomCameraTag from './camera/OnParseZoomCameraTag.js';
+import ParseRotateCameraTag from './camera/OnParseRotateCameraTag.js';
+import ParseScrollCameraTag from './camera/OnParseScrollCameraTag.js';
+import ParseWaitTag from './wait/OnParseWaitTag.js';
+import ParseNewLineTag from './content/OnParseNewLineTag.js';
+import ParsePageBreakTag from './content/OnParsePageBreakTag.js';
+import ParseContentOff from './content/OnParseContentOff.js';
+import ParseContentOn from './content/OnParseContentOn.js';
+import ParseContent from './content/OnParseContent.js';
+import ParseCustomTag from './custom/OnParseCustomTag.js';
+
+const ParseCallbacks = [
+ ParseColorTag, ParseStrokeColorTag,
+ ParseBoldTag, ParseItalicTag,
+ ParseFontSizeTag, ParseShadowColorTag, ParseAlignTag,
+ ParseOffsetYTag, ParseOffsetXTag, ParseLeftSpaceTag, ParseRightSpaceTag,
+ ParseImageTag,
+ ParseSpaceTag,
+
+ ParseTypingSpeedTag,
+
+ ParsePlaySoundEffectTag, ParseFadeInSoundEffectTag, ParseFadeOutSoundEffectTag, ParseSetSoundEffectVolumeTag,
+ ParsePlayBackgroundMusicTag, ParseFadeInBackgroundMusicTag, ParseFadeOutBackgroundMusicTag, ParseCrossFadeBackgroundMusicTag, ParsePauseBackgroundMusicTag,
+
+ ParseFadeInCameraTag, ParseFadeOutCameraTag, ParseShakeCameraTag, ParseFlashCameraTag, ParseZoomCameraTag, ParseRotateCameraTag, ParseScrollCameraTag,
+
+ ParseWaitTag,
+
+ ParseNewLineTag, ParsePageBreakTag,
+ ParseContentOff, ParseContentOn,
+ ParseContent,
+
+ ParseCustomTag,
+];
+
+var AddParseCallbacks = function (textPlayer, parser, config) {
+ for (var i = 0, cnt = ParseCallbacks.length; i < cnt; i++) {
+ ParseCallbacks[i](textPlayer, parser, config);
+ }
+}
+
+export default AddParseCallbacks;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/Parser.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/Parser.js
new file mode 100644
index 000000000..8e0f4f635
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/Parser.js
@@ -0,0 +1,42 @@
+import BracketParser from '../../../../bracketparser.js';
+import AddParseCallbacks from './AddParseCallbacks.js';
+import PreProcessSource from './PreProcessSource.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class Parser extends BracketParser {
+ constructor(textPlayer, config) {
+ if (config === undefined) {
+ config = {};
+ }
+ if (!config.hasOwnProperty('delimiters')) {
+ config.delimiters = '[]';
+ }
+ super(config);
+
+ AddParseCallbacks(textPlayer, this, config);
+
+ this.setCommentLineStartSymbol(GetValue(config, 'comment', '//'));
+ this.setContentOutputEnable();
+ }
+
+ setCommentLineStartSymbol(symbol) {
+ this.commentLineStart = symbol;
+ return this;
+ }
+
+ setContentOutputEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.contentOutputEnable = enable;
+ return this;
+ }
+
+ start(source) {
+ super.start(PreProcessSource(this, source));
+ return this;
+ }
+}
+
+export default Parser;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/PreProcessSource.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/PreProcessSource.js
new file mode 100644
index 000000000..fb3341436
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/PreProcessSource.js
@@ -0,0 +1,28 @@
+/*
+Skip line
+- An empty line, only has space
+- A comment line, start with commentLineStart ('//')
+*/
+
+var PreProcess = function (parser, source) {
+ var comentLineStart = parser.commentLineStart;
+ var lines = source.split('\n');
+ for (var i = 0, cnt = lines.length; i < cnt; i++) {
+ var line = lines[i];
+ if (line === '') {
+ // Do nothing
+
+ } else if (line.trim().length === 0) {
+ // An empty line, only has space
+ lines[i] = '';
+
+ } else if (comentLineStart && line.startsWith(comentLineStart)) {
+ // A comment line, start with commentLineStart ('//')
+ lines[i] = '';
+ }
+ }
+ // Use [r] to put \n
+ return lines.join('');
+}
+
+export default PreProcess;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseCrossFadeBackgroundMusicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseCrossFadeBackgroundMusicTag.js
new file mode 100644
index 000000000..4b35fe891
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseCrossFadeBackgroundMusicTag.js
@@ -0,0 +1,46 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseCrossFadeBackgroundMusicTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm.cross';
+ parser
+ .on(`+${tagName}`, function (name, fadeTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ CrossFadeBackgroundMusic, // callback
+ [name, fadeTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2.cross';
+ parser
+ .on(`+${tagName}`, function (name, fadeTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ CrossFadeBackgroundMusic2, // callback
+ [name, fadeTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var CrossFadeBackgroundMusic = function (params) {
+ // this: textPlayer
+ this.soundManager.crossFadeBackgroundMusic(...params);
+}
+
+var CrossFadeBackgroundMusic2 = function (params) {
+ // this: textPlayer
+ this.soundManager.crossFadeBackgroundMusic2(...params);
+}
+
+export default OnParseCrossFadeBackgroundMusicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeInBackgroundMusicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeInBackgroundMusicTag.js
new file mode 100644
index 000000000..292523daa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeInBackgroundMusicTag.js
@@ -0,0 +1,46 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeInBackgroundMusicTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm.fadein';
+ parser
+ .on(`+${tagName}`, function (time) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeInBackgroundMusic, // callback
+ time, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2.fadein';
+ parser
+ .on(`+${tagName}`, function (time) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeInBackgroundMusic2, // callback
+ time, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var FadeInBackgroundMusic = function (time) {
+ // this: textPlayer
+ this.soundManager.fadeInBackgroundMusic(time);
+}
+
+var FadeInBackgroundMusic2 = function (time) {
+ // this: textPlayer
+ this.soundManager.fadeInBackgroundMusic2(time);
+}
+
+export default OnParseFadeInBackgroundMusicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeOutBackgroundMusicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeOutBackgroundMusicTag.js
new file mode 100644
index 000000000..40030b59a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseFadeOutBackgroundMusicTag.js
@@ -0,0 +1,48 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeOutBackgroundMusicTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm.fadeout';
+ parser
+ .on(`+${tagName}`, function (time, isStopped) {
+ isStopped = (isStopped === 'stop');
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeOutBackgroundMusic, // callback
+ [time, isStopped], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2.fadeout';
+ parser
+ .on(`+${tagName}`, function (time, isStopped) {
+ isStopped = (isStopped === 'stop');
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeOutBackgroundMusic2, // callback
+ [time, isStopped], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var FadeOutBackgroundMusic = function (params) {
+ // this: textPlayer
+ this.soundManager.fadeOutBackgroundMusic(...params);
+}
+
+var FadeOutBackgroundMusic2 = function (params) {
+ // this: textPlayer
+ this.soundManager.fadeOutBackgroundMusic2(...params);
+}
+
+export default OnParseFadeOutBackgroundMusicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePauseBackgroundMusicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePauseBackgroundMusicTag.js
new file mode 100644
index 000000000..eb9e12411
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePauseBackgroundMusicTag.js
@@ -0,0 +1,68 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParsePauseBackgroundMusicTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm.pause';
+ parser
+ .on(`+${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PauseBackgroundMusic, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ 'bgm.resume', // name
+ ResumeBackgroundMusic, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2.pause';
+ parser
+ .on(`+${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PauseBackgroundMusic2, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ 'bgm2.resume', // name
+ ResumeBackgroundMusic2, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PauseBackgroundMusic = function () {
+ // this: textPlayer
+ this.soundManager.pauseBackgroundMusic();
+}
+
+var ResumeBackgroundMusic = function () {
+ // this: textPlayer
+ this.soundManager.resumeBackgroundMusic();
+}
+
+var PauseBackgroundMusic2 = function () {
+ // this: textPlayer
+ this.soundManager.pauseBackgroundMusic2();
+}
+
+var ResumeBackgroundMusic2 = function () {
+ // this: textPlayer
+ this.soundManager.resumeBackgroundMusic2();
+}
+
+export default OnParsePauseBackgroundMusicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePlayBackgroundMusicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePlayBackgroundMusicTag.js
new file mode 100644
index 000000000..edc7c019e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParsePlayBackgroundMusicTag.js
@@ -0,0 +1,80 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParsePlayBackgroundMusicTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm';
+ parser
+ .on(`+${tagName}`, function (name, fadeInTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayBackgroundMusic, // callback
+ [name, fadeInTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ 'bgm.stop', // name
+ StopBackgroundMusic, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2';
+ parser
+ .on(`+${tagName}`, function (name, fadeInTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayBackgroundMusic2, // callback
+ [name, fadeInTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ AppendCommandBase.call(textPlayer,
+ 'bgm2.stop', // name
+ StopBackgroundMusic2, // callback
+ undefined, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PlayBackgroundMusic = function (params) {
+ var name = params[0];
+ var fadeInTime = params[1];
+
+ // this: textPlayer
+ this.soundManager.playBackgroundMusic(name);
+ if (fadeInTime) {
+ this.soundManager.fadeInBackgroundMusic(fadeInTime);
+ }
+}
+
+var StopBackgroundMusic = function () {
+ // this: textPlayer
+ this.soundManager.stopBackgroundMusic();
+}
+
+var PlayBackgroundMusic2 = function (params) {
+ var name = params[0];
+ var fadeInTime = params[1];
+
+ // this: textPlayer
+ this.soundManager.playBackgroundMusic2(name);
+ if (fadeInTime) {
+ this.soundManager.fadeInBackgroundMusic2(fadeInTime);
+ }
+}
+
+var StopBackgroundMusic2 = function () {
+ // this: textPlayer
+ this.soundManager.stopBackgroundMusic2();
+}
+
+export default OnParsePlayBackgroundMusicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseSetBackgroundMusicVolumeTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseSetBackgroundMusicVolumeTag.js
new file mode 100644
index 000000000..d2607e5cf
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/backgroundmusic/OnParseSetBackgroundMusicVolumeTag.js
@@ -0,0 +1,45 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseSetBackgroundMusicVolumeTag = function (textPlayer, parser, config) {
+ var tagName = 'bgm.volume';
+ parser
+ .on(`+${tagName}`, function (volume) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ SetBackgroundMusicVolume, // callback
+ volume, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'bgm2.volume';
+ parser
+ .on(`+${tagName}`, function (volume) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ SetBackgroundMusicVolume2, // callback
+ volume, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var SetBackgroundMusicVolume = function (volume) {
+ // this: textPlayer
+ this.soundManager.setBackgroundMusicVolume(volume);
+}
+
+var SetBackgroundMusicVolume2 = function (volume) {
+ // this: textPlayer
+ this.soundManager.setBackgroundMusicVolume2(volume);
+}
+export default OnParseSetBackgroundMusicVolumeTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeInCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeInCameraTag.js
new file mode 100644
index 000000000..20d97243e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeInCameraTag.js
@@ -0,0 +1,22 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeInCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.fadein';
+ parser
+ .on(`+${tagName}`, function (duration, red, green, blue) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayFadeInEffect, // callback
+ [duration, red, green, blue], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PlayFadeInEffect = function (params) {
+ // this: textPlayer
+ this.targetCamera.fadeIn(...params);
+}
+
+export default OnParseFadeInCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeOutCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeOutCameraTag.js
new file mode 100644
index 000000000..29a395949
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFadeOutCameraTag.js
@@ -0,0 +1,22 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeOutCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.fadeout';
+ parser
+ .on(`+${tagName}`, function (duration, red, green, blue) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayFadeOutEffect, // callback
+ [duration, red, green, blue], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PlayFadeOutEffect = function (params) {
+ // this: textPlayer
+ this.targetCamera.fadeOut(...params);
+}
+
+export default OnParseFadeOutCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFlashCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFlashCameraTag.js
new file mode 100644
index 000000000..fd2685491
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseFlashCameraTag.js
@@ -0,0 +1,22 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFlashCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.flash';
+ parser
+ .on(`+${tagName}`, function (duration, red, green, blue) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayFlashEffect, // callback
+ [duration, red, green, blue], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PlayFlashEffect = function (params) {
+ // this: textPlayer
+ this.targetCamera.flash(...params);
+}
+
+export default OnParseFlashCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseRotateCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseRotateCameraTag.js
new file mode 100644
index 000000000..02e45c816
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseRotateCameraTag.js
@@ -0,0 +1,44 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+const DegToRad = Phaser.Math.DegToRad;
+
+var OnParseRotateCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.rotate';
+ parser
+ .on(`+${tagName}`, function (value) {
+ value = DegToRad(value);
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ Rotate, // callback
+ value, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`+${tagName}.to`, function (value, duration, ease) {
+ value = DegToRad(value);
+ AppendCommandBase.call(textPlayer,
+ 'camera.rotate.to', // name
+ RotateTo, // callback
+ [value, duration, ease], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var Rotate = function (value) {
+ // this: textPlayer
+ this.targetCamera.setRotation(value);
+}
+
+var RotateTo = function (params) {
+ var value = params[0];
+ var duration = params[1];
+ var ease = params[2];
+
+ // this: textPlayer
+ this.targetCamera.rotateTo(value, false, duration, ease);
+}
+
+export default OnParseRotateCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseScrollCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseScrollCameraTag.js
new file mode 100644
index 000000000..e1f2f6d9a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseScrollCameraTag.js
@@ -0,0 +1,50 @@
+import AppendCommand from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseScrollCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.scroll';
+ parser
+ .on(`+${tagName}`, function (x, y) {
+ AppendCommand.call(textPlayer,
+ tagName, // name
+ Scroll, // callback
+ [x, y], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`+${tagName}.to`, function (x, y, duration, ease) {
+ AppendCommand.call(textPlayer,
+ 'camera.scroll.to', // name
+ ScrollTo, // callback
+ [x, y, duration, ease], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var Scroll = function (params) {
+ // this: textPlayer
+ this.targetCamera.setScroll(...params);
+}
+
+var ScrollTo = function (params) {
+ var x = params[0];
+ var y = params[1];
+ var duration = params[2];
+ var ease = params[3];
+
+ // this: textPlayer
+ var camera = this.targetCamera;
+ var xSave = camera.scrollX;
+ var ySave = camera.scrollY;
+ camera.setScroll(x, y);
+ x += camera.centerX;
+ y += camera.centerY;
+ camera.setScroll(xSave, ySave);
+
+ // x,y in pan() is the centerX, centerY
+ camera.pan(x, y, duration, ease);
+}
+
+export default OnParseScrollCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseShakeCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseShakeCameraTag.js
new file mode 100644
index 000000000..d4133de6c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseShakeCameraTag.js
@@ -0,0 +1,22 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseShakeCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.shake';
+ parser
+ .on(`+${tagName}`, function (duration, intensity) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlayShakeEffect, // callback
+ [duration, intensity], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var PlayShakeEffect = function (params) {
+ // this: textPlayer
+ this.targetCamera.shake(...params);
+}
+
+export default OnParseShakeCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseZoomCameraTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseZoomCameraTag.js
new file mode 100644
index 000000000..bfc10a933
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/camera/OnParseZoomCameraTag.js
@@ -0,0 +1,36 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseZoomCameraTag = function (textPlayer, parser, config) {
+ var tagName = 'camera.zoom';
+ parser
+ .on(`+${tagName}`, function (value) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ Zoom, // callback
+ value, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`+${tagName}.to`, function (value, duration, ease) {
+ AppendCommandBase.call(textPlayer,
+ 'camera.zoom.to', // name
+ ZoomTo, // callback
+ [value, duration, ease], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+}
+
+var Zoom = function (value) {
+ // this: textPlayer
+ this.targetCamera.setZoom(value);
+}
+
+var ZoomTo = function (params) {
+ // this: textPlayer
+ this.targetCamera.zoomTo(...params);
+}
+
+export default OnParseZoomCameraTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContent.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContent.js
new file mode 100644
index 000000000..79ef6acab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContent.js
@@ -0,0 +1,15 @@
+import AppendTextBase from '../../../dynamictext/methods/AppendText.js';
+
+var OnParseContent = function (textPlayer, parser, config) {
+ parser
+ .on('content', function (content) {
+ if (parser.contentOutputEnable) {
+ AppendTextBase.call(textPlayer, content);
+ } else {
+ var startTag = `+${parser.lastTagStart}`;
+ textPlayer.emit(`parser.${startTag}#content`, parser, content);
+ }
+ })
+}
+
+export default OnParseContent;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOff.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOff.js
new file mode 100644
index 000000000..549579ad4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOff.js
@@ -0,0 +1,10 @@
+var OnParseContentOff = function (textPlayer, parser, config) {
+ var tagName = 'content.off';
+ parser
+ .on(`+${tagName}`, function () {
+ parser.setContentOutputEnable(false);
+ parser.skipEvent();
+ })
+}
+
+export default OnParseContentOff;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOn.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOn.js
new file mode 100644
index 000000000..4b3d3d5d0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseContentOn.js
@@ -0,0 +1,10 @@
+var OnParseContentOn = function (textPlayer, parser, config) {
+ var tagName = 'content.on';
+ parser
+ .on(`+${tagName}`, function () {
+ parser.setContentOutputEnable();
+ parser.skipEvent();
+ })
+}
+
+export default OnParseContentOn;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseNewLineTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseNewLineTag.js
new file mode 100644
index 000000000..66ec2c1a4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParseNewLineTag.js
@@ -0,0 +1,15 @@
+import AppendTextBase from '../../../dynamictext/methods/AppendText.js';
+
+var OnParseNewLineTag = function (textPlayer, parser, config) {
+ var tagName = 'r';
+ parser
+ .on(`+${tagName}`, function () {
+ AppendTextBase.call(textPlayer, '\n');
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+export default OnParseNewLineTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParsePageBreakTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParsePageBreakTag.js
new file mode 100644
index 000000000..488746252
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/content/OnParsePageBreakTag.js
@@ -0,0 +1,19 @@
+import AppendTextBase from '../../../dynamictext/methods/AppendText.js';
+
+var OnParsePageBreakTag = function (textPlayer, parser, config) {
+ var tagNames = ['pagebreak', 'pb'];
+ for (var i = 0, cnt = tagNames.length; i < cnt; i++) {
+ var tagName = tagNames[i];
+ parser
+ .on(`+${tagName}`, function () {
+ AppendTextBase.call(textPlayer, '\f');
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+ }
+
+}
+
+export default OnParsePageBreakTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/custom/OnParseCustomTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/custom/OnParseCustomTag.js
new file mode 100644
index 000000000..bdccbeeec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/custom/OnParseCustomTag.js
@@ -0,0 +1,53 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseCustomTag = function (textPlayer, parser, config) {
+ parser
+ .on('start', function () {
+ textPlayer.emit('parser.start', parser);
+ })
+ .on('+', function (tagName, ...value) {
+ if (parser.skipEventFlag) { // Has been processed before
+ return;
+ }
+
+ var startTag = `+${tagName}`;
+ var param = value;
+ textPlayer.emit(`parser.${startTag}`, parser, ...value, param);
+ AppendCommand(textPlayer, startTag, param);
+ })
+ .on('-', function (tagName) {
+ if (parser.skipEventFlag) {
+ return;
+ }
+
+ var endTag = `-${tagName}`;
+ var param = [];
+ textPlayer.emit(`parser.${endTag}`, parser, param);
+ AppendCommand(textPlayer, endTag, param);
+ })
+ .on('complete', function () {
+ textPlayer.emit('parser.complete', parser);
+ })
+}
+
+var FireEvent = function (param, tagName) {
+ var eventName = `tag.${tagName}`;
+ // this: textPlayer
+ if (param == null) {
+ this.emit(eventName);
+ } else {
+ this.emit(eventName, ...param);
+ }
+
+}
+
+var AppendCommand = function (textPlayer, name, param) {
+ AppendCommandBase.call(textPlayer,
+ name, // name
+ FireEvent, // callback
+ param, // params
+ textPlayer, // scope
+ );
+}
+
+export default OnParseCustomTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/image/OnParseImageTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/image/OnParseImageTag.js
new file mode 100644
index 000000000..0f47065c5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/image/OnParseImageTag.js
@@ -0,0 +1,24 @@
+import AppendImageBase from '../../../dynamictext/methods/AppendImage.js';
+
+var OnParseImageTag = function (textPlayer, parser, config) {
+ var tagName = 'img';
+ parser
+ .on(`+${tagName}`, function (name) {
+ var imgData = textPlayer.imageManager.get(name);
+ AppendImageBase.call(textPlayer,
+ imgData.key, imgData.frame,
+ {
+ width: imgData.width,
+ hieght: imgData.height,
+ leftSpace: imgData.left,
+ rightSpace: imgData.right
+ }
+ )
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+export default OnParseImageTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeInSoundEffectTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeInSoundEffectTag.js
new file mode 100644
index 000000000..c486d300d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeInSoundEffectTag.js
@@ -0,0 +1,46 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeInSoundEffectTag = function (textPlayer, parser, config) {
+ var tagName = 'se.fadein'
+ parser
+ .on(`+${tagName}`, function (time) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeInSoundEffect, // callback
+ time, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'se2.fadein'
+ parser
+ .on(`+${tagName}`, function (time) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeInSoundEffect2, // callback
+ time, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var FadeInSoundEffect = function (time) {
+ // this: textPlayer
+ this.soundManager.fadeInSoundEffect(time);
+}
+
+var FadeInSoundEffect2 = function (time) {
+ // this: textPlayer
+ this.soundManager.fadeInSoundEffect2(time);
+}
+
+export default OnParseFadeInSoundEffectTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeOutSoundEffectTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeOutSoundEffectTag.js
new file mode 100644
index 000000000..620c11e44
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseFadeOutSoundEffectTag.js
@@ -0,0 +1,47 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseFadeOutSoundEffectTag = function (textPlayer, parser, config) {
+ var tagName = 'se.fadeout';
+ parser
+ .on(`+${tagName}`, function (time, isStopped) {
+ isStopped = (isStopped === 'stop');
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeOutSoundEffect, // callback
+ [time, isStopped], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'se2.fadeout';
+ parser
+ .on(`+${tagName}`, function (time, isStopped) {
+ isStopped = (isStopped === 'stop');
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ FadeOutSoundEffect2, // callback
+ [time, isStopped], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var FadeOutSoundEffect = function (params) {
+ // this: textPlayer
+ this.soundManager.fadeOutSoundEffect(...params);
+}
+
+var FadeOutSoundEffect2 = function (params) {
+ // this: textPlayer
+ this.soundManager.fadeOutSoundEffect2(...params);
+}
+export default OnParseFadeOutSoundEffectTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParsePlaySoundEffectTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParsePlaySoundEffectTag.js
new file mode 100644
index 000000000..982502e84
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParsePlaySoundEffectTag.js
@@ -0,0 +1,64 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParsePlaySoundEffectTag = function (textPlayer, parser, config) {
+ var tagName = 'se';
+ parser
+ .on(`+${tagName}`, function (name, fadeInTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlaySoundEffect, // callback
+ [name, fadeInTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'se2';
+ parser
+ .on(`+${tagName}`, function (name, fadeInTime) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ PlaySoundEffect2, // callback
+ [name, fadeInTime], // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var PlaySoundEffect = function (params) {
+ if (this.skipSoundEffect) {
+ return;
+ }
+
+ var name = params[0];
+ var fadeInTime = params[1];
+
+ this.soundManager.playSoundEffect(name); // this: textPlayer
+ if (fadeInTime) {
+ this.soundManager.fadeInSoundEffect(fadeInTime);
+ }
+}
+
+var PlaySoundEffect2 = function (params) {
+ if (this.skipSoundEffect) {
+ return;
+ }
+
+ var name = params[0];
+ var fadeInTime = params[1];
+
+ this.soundManager.playSoundEffect2(name); // this: textPlayer
+ if (fadeInTime) {
+ this.soundManager.fadeInSoundEffect2(fadeInTime);
+ }
+}
+
+export default OnParsePlaySoundEffectTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseSetSoundEffectVolumeTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseSetSoundEffectVolumeTag.js
new file mode 100644
index 000000000..9f5496a7a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/soundeffect/OnParseSetSoundEffectVolumeTag.js
@@ -0,0 +1,45 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseSetSoundEffectVolumeTag = function (textPlayer, parser, config) {
+ var tagName = 'se.volume';
+ parser
+ .on(`+${tagName}`, function (volume) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ SetSoundEffectVolume, // callback
+ volume, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+
+
+ var tagName = 'se2.volume';
+ parser
+ .on(`+${tagName}`, function (volume) {
+ AppendCommandBase.call(textPlayer,
+ tagName, // name
+ SetSoundEffectVolume2, // callback
+ volume, // params
+ textPlayer, // scope
+ );
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+var SetSoundEffectVolume = function (volume) {
+ // this: textPlayer
+ this.soundManager.setSoundEffectVolume(volume, true);
+}
+
+var SetSoundEffectVolume2 = function (volume) {
+ // this: textPlayer
+ this.soundManager.setSoundEffectVolume2(volume, true);
+}
+export default OnParseSetSoundEffectVolumeTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/space/OnParseSpaceTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/space/OnParseSpaceTag.js
new file mode 100644
index 000000000..26854135d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/space/OnParseSpaceTag.js
@@ -0,0 +1,17 @@
+import AppendSpaceBase from '../../../dynamictext/methods/AppendSpace.js';
+
+var OnParseImageTag = function (textPlayer, parser, config) {
+ var tagName = 'space';
+ parser
+ .on(`+${tagName}`, function (width) {
+ AppendSpaceBase.call(textPlayer,
+ width
+ )
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ parser.skipEvent();
+ })
+}
+
+export default OnParseImageTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseAlignTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseAlignTag.js
new file mode 100644
index 000000000..17d976684
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseAlignTag.js
@@ -0,0 +1,17 @@
+var OnParseAlignTag = function (textPlayer, parser, config) {
+ var tagName = 'align';
+ parser
+ .on(`+${tagName}`, function (align) {
+ textPlayer.textStyle.setAlign(align);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setAlign();
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setAlign();
+ })
+}
+
+export default OnParseAlignTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseBoldTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseBoldTag.js
new file mode 100644
index 000000000..97cd696d5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseBoldTag.js
@@ -0,0 +1,17 @@
+var OnParseBoldTag = function (textPlayer, parser, config) {
+ var tagName = 'b';
+ parser
+ .on('start', function () {
+ textPlayer.textStyle.setBold(false);
+ })
+ .on(`+${tagName}`, function () {
+ textPlayer.textStyle.setBold(true);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setBold(false);
+ parser.skipEvent();
+ })
+}
+
+export default OnParseBoldTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseColorTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseColorTag.js
new file mode 100644
index 000000000..a4f9998ab
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseColorTag.js
@@ -0,0 +1,21 @@
+var OnParseColorTag = function (textPlayer, parser, config) {
+ var tagName = 'color';
+ var defaultColor;
+ parser
+ .on('start', function () {
+ defaultColor = textPlayer.textStyle.color;
+ })
+ .on(`+${tagName}`, function (color) {
+ textPlayer.textStyle.setColor(color);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setColor(defaultColor);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setColor(defaultColor);
+ })
+}
+
+export default OnParseColorTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseFontSizeTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseFontSizeTag.js
new file mode 100644
index 000000000..5aa12198d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseFontSizeTag.js
@@ -0,0 +1,21 @@
+var OnParseFontSizeTag = function (textPlayer, parser, config) {
+ var tagName = 'size';
+ var defaultFontSize;
+ parser
+ .on('start', function () {
+ defaultFontSize = textPlayer.textStyle.fontSize;
+ })
+ .on(`+${tagName}`, function (fontSize) {
+ textPlayer.textStyle.setFontSize(fontSize);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setFontSize(defaultFontSize);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setFontSize(defaultFontSize);
+ })
+}
+
+export default OnParseFontSizeTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseItalicTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseItalicTag.js
new file mode 100644
index 000000000..2209e9668
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseItalicTag.js
@@ -0,0 +1,17 @@
+var OnParseItalicTag = function (textPlayer, parser, config) {
+ var tagName = 'i';
+ parser
+ .on('start', function () {
+ textPlayer.textStyle.setItalic(false);
+ })
+ .on(`+${tagName}`, function () {
+ textPlayer.textStyle.setItalic(true);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setItalic(false);
+ parser.skipEvent();
+ })
+}
+
+export default OnParseItalicTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseLeftSpaceTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseLeftSpaceTag.js
new file mode 100644
index 000000000..4fb8b2062
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseLeftSpaceTag.js
@@ -0,0 +1,25 @@
+var OnParseLeftSpaceTag = function (textPlayer, parser, config) {
+ var tagName = 'left';
+ var defaultLeftSpace;
+ parser
+ .on('start', function () {
+ defaultLeftSpace = textPlayer.textStyle.leftSpace;
+ textPlayer.textStyle.setLeftSpace(0);
+ })
+ .on(`+${tagName}`, function (space) {
+ if (space === undefined) {
+ space = defaultLeftSpace;
+ }
+ textPlayer.textStyle.setLeftSpace(space);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setLeftSpace(0);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setLeftSpace(0);
+ })
+}
+
+export default OnParseLeftSpaceTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetXTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetXTag.js
new file mode 100644
index 000000000..7960f39a6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetXTag.js
@@ -0,0 +1,25 @@
+var OnParseOffsetXTag = function (textPlayer, parser, config) {
+ var tagName = 'x';
+ var defaultOffsetX;
+ parser
+ .on('start', function () {
+ defaultOffsetX = textPlayer.textStyle.offsetY;
+ textPlayer.textStyle.setOffsetX(0);
+ })
+ .on(`+${tagName}`, function (y) {
+ if (y === undefined) {
+ y = defaultOffsetX;
+ }
+ textPlayer.textStyle.setOffsetX(y);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setOffsetX(0);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setOffsetX(0);
+ })
+}
+
+export default OnParseOffsetXTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetYTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetYTag.js
new file mode 100644
index 000000000..142ceb745
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseOffsetYTag.js
@@ -0,0 +1,25 @@
+var OnParseOffsetYTag = function (textPlayer, parser, config) {
+ var tagName = 'y';
+ var defaultOffsetY;
+ parser
+ .on('start', function () {
+ defaultOffsetY = textPlayer.textStyle.offsetY;
+ textPlayer.textStyle.setOffsetY(0);
+ })
+ .on(`+${tagName}`, function (y) {
+ if (y === undefined) {
+ y = defaultOffsetY;
+ }
+ textPlayer.textStyle.setOffsetY(y);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setOffsetY(0);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setOffsetY(0);
+ })
+}
+
+export default OnParseOffsetYTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseRightSpaceTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseRightSpaceTag.js
new file mode 100644
index 000000000..1db7d8c10
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseRightSpaceTag.js
@@ -0,0 +1,25 @@
+var OnParseRightSpaceTag = function (textPlayer, parser, config) {
+ var tagName = 'right';
+ var defaultRightSpace;
+ parser
+ .on('start', function () {
+ defaultRightSpace = textPlayer.textStyle.rightSpace;
+ textPlayer.textStyle.setRightSpace(0);
+ })
+ .on(`+${tagName}`, function (space) {
+ if (space === undefined) {
+ space = defaultRightSpace;
+ }
+ textPlayer.textStyle.setRightSpace(space);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setRightSpace(0);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setRightSpace(0);
+ })
+}
+
+export default OnParseRightSpaceTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseShadowColorTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseShadowColorTag.js
new file mode 100644
index 000000000..0a544502b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseShadowColorTag.js
@@ -0,0 +1,25 @@
+var OnParseShadowColorTag = function (textPlayer, parser, config) {
+ var tagName = 'shadow';
+ var defaultShadowColor;
+ parser
+ .on('start', function () {
+ defaultShadowColor = textPlayer.textStyle.shadowColor;
+ textPlayer.textStyle.setShadowColor(null);
+ })
+ .on(`+${tagName}`, function (color) {
+ if (color === undefined) {
+ color = defaultShadowColor;
+ }
+ textPlayer.textStyle.setShadowColor(color);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setShadowColor(null);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setShadowColor(defaultShadowColor);
+ })
+}
+
+export default OnParseShadowColorTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseStrokeColorTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseStrokeColorTag.js
new file mode 100644
index 000000000..ffae78b79
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/textstyle/OnParseStrokeColorTag.js
@@ -0,0 +1,25 @@
+var OnParseStrokeColorTag = function (textPlayer, parser, config) {
+ var tagName = 'stroke';
+ var defaultStroke;
+ parser
+ .on('start', function () {
+ defaultStroke = textPlayer.textStyle.stroke;
+ textPlayer.textStyle.setStrokeStyle(null);
+ })
+ .on(`+${tagName}`, function (color) {
+ if (color === undefined) {
+ color = defaultStroke;
+ }
+ textPlayer.textStyle.setStrokeStyle(color);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ textPlayer.textStyle.setStrokeStyle(null);
+ parser.skipEvent();
+ })
+ .on('complete', function () {
+ textPlayer.textStyle.setStrokeStyle(defaultStroke);
+ })
+}
+
+export default OnParseStrokeColorTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/typing/OnParseTypingSpeedTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/typing/OnParseTypingSpeedTag.js
new file mode 100644
index 000000000..13566c8dc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/typing/OnParseTypingSpeedTag.js
@@ -0,0 +1,29 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseTypingSpeedTag = function (textPlayer, parser, config) {
+ var tagName = 'speed';
+ parser
+ .on(`+${tagName}`, function (speed) {
+ AppendCommand(textPlayer, speed);
+ parser.skipEvent();
+ })
+ .on(`-${tagName}`, function () {
+ AppendCommand(textPlayer, undefined);
+ parser.skipEvent();
+ })
+}
+
+var SetTypingSpeed = function (speed) {
+ this.typeWriter.setTypingSpeed(speed); // this: textPlayer
+}
+
+var AppendCommand = function (textPlayer, speed) {
+ AppendCommandBase.call(textPlayer,
+ 'speed', // name
+ SetTypingSpeed, // callback
+ speed, // params
+ textPlayer, // scope
+ );
+}
+
+export default OnParseTypingSpeedTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/wait/OnParseWaitTag.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/wait/OnParseWaitTag.js
new file mode 100644
index 000000000..d2c6c6d56
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/parser/wait/OnParseWaitTag.js
@@ -0,0 +1,36 @@
+import AppendCommandBase from '../../../dynamictext/methods/AppendCommand.js';
+
+var OnParseWaitTag = function (textPlayer, parser, config) {
+ var tagWait = 'wait';
+ var tagClick = 'click';
+ parser
+ .on(`+${tagWait}`, function (name) {
+ AppendCommand(textPlayer, name);
+ parser.skipEvent();
+ })
+ .on(`-${tagWait}`, function () {
+ parser.skipEvent();
+ })
+ .on(`+${tagClick}`, function () { // Equal to [wait=click]
+ AppendCommand(textPlayer, 'click');
+ parser.skipEvent();
+ })
+ .on(`-${tagClick}`, function () { // Equal to [/wait]
+ parser.skipEvent();
+ })
+}
+
+var Wait = function (name) {
+ this.typeWriter.wait(name); // this: textPlayer
+}
+
+var AppendCommand = function (textPlayer, name) {
+ AppendCommandBase.call(textPlayer,
+ 'wait', // name
+ Wait, // callback
+ name, // params
+ textPlayer, // scope
+ );
+}
+
+export default OnParseWaitTag;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/FadeOutPage.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/FadeOutPage.js
new file mode 100644
index 000000000..00e834e04
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/FadeOutPage.js
@@ -0,0 +1,29 @@
+const PageFadeOutCompleteEvent = 'page.fadeout';
+
+var FadeOutPage = function () {
+ if (!this.fadeOutPageCallback || !this.children) {
+ this.emit(PageFadeOutCompleteEvent);
+ return this;
+ }
+
+ var renderableChildren = this.children.filter(function (child) { return child.renderable });
+ var waitObject = this.fadeOutPageCallback(renderableChildren, this.fadeOutPageDuration);
+ if (!waitObject) {
+ this.emit(PageFadeOutCompleteEvent);
+ } else if (waitObject.once) {
+ waitObject.once('complete', function () {
+ this.emit(PageFadeOutCompleteEvent);
+ }, this);
+ } else if (waitObject.then) {
+ var self = this;
+ waitObject.then(function () {
+ self.emit(PageFadeOutCompleteEvent);
+ })
+ } else {
+ this.emit(PageFadeOutCompleteEvent);
+ }
+
+ return this;
+}
+
+export default FadeOutPage;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Methods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Methods.js
new file mode 100644
index 000000000..82a652b34
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Methods.js
@@ -0,0 +1,37 @@
+import TypingSpeedMethods from './TypingSpeedMethods.js';
+import FadeOutPage from './FadeOutPage.js';
+import Start from './Start.js';
+import Typing from './Typing.js';
+import Pause from './Pause.js';
+import Resume from './Resume.js';
+import PauseTyping from './PauseTyping.js';
+import ResumeTyping from './ResumeTyping.js';
+import Wait from './Wait.js';
+import SetIgnoreWait from './SetIgnoreWait.js';
+import SetSkipSpaceEnable from './SetSkipSpaceEnable.js';
+import SetSkipTypingAnimation from './SetSkipTypingAnimation.js';
+import SetSkipSoundEffect from './SetSkipSoundEffect.js';
+import SkipCurrentTypingDelay from './SkipCurrentTypingDelay.js';
+
+var Methods = {
+ fadeOutPage: FadeOutPage,
+ start: Start,
+ typing: Typing,
+ pause: Pause,
+ resume: Resume,
+ pauseTyping: PauseTyping,
+ resumeTyping: ResumeTyping,
+ wait: Wait,
+ setIgnoreWait: SetIgnoreWait,
+ setSkipSpaceEnable: SetSkipSpaceEnable,
+ setSkipTypingAnimation: SetSkipTypingAnimation,
+ setSkipSoundEffect: SetSkipSoundEffect,
+ skipCurrentTypingDelay: SkipCurrentTypingDelay,
+}
+
+Object.assign(
+ Methods,
+ TypingSpeedMethods
+);
+
+export default Methods;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Pause.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Pause.js
new file mode 100644
index 000000000..4b9cacaaa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Pause.js
@@ -0,0 +1,7 @@
+var Pause = function () {
+ // Pause typing timer and animation progresses
+ this.timeline.pause();
+ return this;
+}
+
+export default Pause;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/PauseTyping.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/PauseTyping.js
new file mode 100644
index 000000000..db8358e51
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/PauseTyping.js
@@ -0,0 +1,17 @@
+var PauseTyping = function () {
+ // Already in typingPaused state
+ if (this.isTypingPaused) {
+ return this;
+ }
+
+ if (this.typingTimer) { // Pause when typing timer is counting
+ this.typingTimer.pause();
+ this.isTypingPaused = true;
+ } else if (this.inTypingProcessLoop) { // Pause in loop of typing(), by tag
+ this.inTypingProcessLoop = false;
+ this.isTypingPaused = true;
+ }
+ return this;
+}
+
+export default PauseTyping;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Resume.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Resume.js
new file mode 100644
index 000000000..37fc417c1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Resume.js
@@ -0,0 +1,7 @@
+var Resume = function () {
+ // Resume typing timer and animation progresses
+ this.timeline.resume();
+ return this;
+}
+
+export default Resume;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/ResumeTyping.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/ResumeTyping.js
new file mode 100644
index 000000000..a13625393
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/ResumeTyping.js
@@ -0,0 +1,21 @@
+var ResumeTyping = function (offsetTime) {
+ // Already not in typingPaused state
+ if (!this.isTypingPaused) {
+ return this;
+ }
+ if (offsetTime === undefined) {
+ offsetTime = 0;
+ }
+
+ if (this.typingTimer) { // Pause when typing timer is paused
+ this.isTypingPaused = false;
+ this.typingTimer.resume();
+ this.typingTimer.remainder += offsetTime;
+ } else if (this.isTypingPaused) { // Resume paused by tag
+ this.isTypingPaused = false;
+ this.typing(offsetTime);
+ }
+ return this;
+}
+
+export default ResumeTyping;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetIgnoreWait.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetIgnoreWait.js
new file mode 100644
index 000000000..bc2ef6c21
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetIgnoreWait.js
@@ -0,0 +1,9 @@
+var SetIgnoreWait = function (value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.ignoreWait = value;
+ return this;
+}
+
+export default SetIgnoreWait;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSoundEffect.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSoundEffect.js
new file mode 100644
index 000000000..d4da2ebd3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSoundEffect.js
@@ -0,0 +1,16 @@
+var SetSkipSoundEffect = function (value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.skipSoundEffect = value;
+
+ if (value) {
+ var soundManager = this.textPlayer._soundManager;
+ if (soundManager) {
+ soundManager.fadeOutAllSoundEffects(100, true);
+ }
+ }
+ return this;
+}
+
+export default SetSkipSoundEffect;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSpaceEnable.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSpaceEnable.js
new file mode 100644
index 000000000..75a8a01cc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipSpaceEnable.js
@@ -0,0 +1,9 @@
+var SetSkipSpaceEnable = function (enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+ this.skipSpaceEnable = enable;
+ return this;
+}
+
+export default SetSkipSpaceEnable;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipTypingAnimation.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipTypingAnimation.js
new file mode 100644
index 000000000..981c55e5e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SetSkipTypingAnimation.js
@@ -0,0 +1,19 @@
+import { TypingAnimationTimerType, } from './TimerTypes.js';
+
+var SetSkipTypingAnimation = function (value) {
+ if (value === undefined) {
+ value = true;
+ }
+ this.skipTypingAnimation = value;
+
+ if (value) {
+ // Skip current playing typing-animation
+ var timers = this.timeline.getTimers(TypingAnimationTimerType);
+ for (var i = 0, cnt = timers.length; i < cnt; i++) {
+ timers[i].seek(1);
+ }
+ }
+ return this;
+}
+
+export default SetSkipTypingAnimation;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SkipCurrentTypingDelay.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SkipCurrentTypingDelay.js
new file mode 100644
index 000000000..81d8f5432
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/SkipCurrentTypingDelay.js
@@ -0,0 +1,8 @@
+var SkipCurrentTypingDelay = function () {
+ if (this.typingTimer) {
+ this.typingTimer.seek(1);
+ }
+ return this;
+}
+
+export default SkipCurrentTypingDelay;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Start.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Start.js
new file mode 100644
index 000000000..1033ed7ec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Start.js
@@ -0,0 +1,16 @@
+import { WaitComplete } from '../../../../utils/promise/WaitEvent.js';
+
+var Start = function (children) {
+ this.children = children;
+ this.index = 0;
+ this.isPageTyping = true;
+
+ if (this.onTypeStart) {
+ this.onTypeStart(children);
+ }
+ this.typing();
+
+ return WaitComplete(this); // Promise
+}
+
+export default Start;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TimerTypes.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TimerTypes.js
new file mode 100644
index 000000000..a1f6a536a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TimerTypes.js
@@ -0,0 +1,7 @@
+const TypingDelayTimerType = 'delay';
+const TypingAnimationTimerType = 'anim';
+
+export {
+ TypingAnimationTimerType,
+ TypingDelayTimerType
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypeWriter.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypeWriter.js
new file mode 100644
index 000000000..6017db03c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypeWriter.js
@@ -0,0 +1,120 @@
+import EventEmitterMethods from '../../../../utils/eventemitter/EventEmitterMethods.js';
+import Methods from './Methods.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class TypeWriter {
+ constructor(textPlayer, config) {
+ this.setEventEmitter();
+ this.textPlayer = textPlayer;
+ this.isPageTyping = false;
+ this.typingTimer = undefined; // Typing delay
+ this.pauseTypingTimer = undefined; // Wait time
+ this.inTypingProcessLoop = false; // Used in this.typing()
+ this.isTypingPaused = false; // Used in this.wait(), this.pauseTyping(), this.resumeTyping()
+ this.setIgnoreWait(false);
+ this.setSkipTypingAnimation(false);
+
+ this.setTypingStartCallback(GetValue(config, 'onTypingStart', SetChildrenInvisible));
+ this.setDefaultTypingSpeed(GetValue(config, 'speed', 250));
+ this.setTypingSpeed();
+ this.setSkipSpaceEnable(GetValue(config, 'skipSpace', false));
+ this.setAnimationConfig(GetValue(config, 'animation', undefined));
+ this.setMinSizeEnable(GetValue(config, 'minSizeEnable', false));
+
+ this.setFadeOutPageCallback(GetValue(config, 'fadeOutPage'));
+
+ }
+
+ destroy() {
+ this.destroyEventEmitter();
+
+ this.textPlayer = undefined;
+
+ this.typingTimer = undefined;
+
+ this.pauseTypingTimer = undefined;
+
+ this.onTypeStart = undefined;
+
+ this.animationConfig = undefined;
+ }
+
+ get timeline() {
+ return this.textPlayer.timeline;
+ }
+
+ setTypingStartCallback(callback) {
+ this.onTypeStart = callback;
+ return this;
+ }
+
+ setAnimationConfig(config) {
+ if (!config) {
+ config = {};
+ }
+
+ if (!config.hasOwnProperty('duration')) {
+ config.duration = 0;
+ }
+
+ if (!config.hasOwnProperty('onStart')) {
+ // Apply default onStart callback
+ config.onStart = SetChildVisible;
+ }
+
+ this.animationConfig = config;
+ return this;
+ }
+
+ setFadeOutPageCallback(callback) {
+ this.fadeOutPageCallback = callback;
+ return this;
+ }
+
+ setMinSizeEnable(enable) {
+ if (enable === undefined) {
+ enable = true;
+ }
+
+ this.minSizeEnable = enable;
+ return this;
+ }
+
+ getNextChild() {
+ var child = this.nextChild;
+ this.index = Math.min(this.index + 1, this.children.length); // Point to next child
+ this._nextChild = undefined;
+ return child;
+ }
+
+ get nextChild() {
+ if (!this._nextChild) {
+ this._nextChild = this.children[this.index];
+ }
+ return this._nextChild;
+ }
+}
+
+var SetChildVisible = function (child) {
+ if (child.setVisible) {
+ child.setVisible();
+ }
+}
+
+var SetChildrenInvisible = function (children) {
+ for (var i = 0, cnt = children.length; i < cnt; i++) {
+ var child = children[i];
+ if (child.setVisible) {
+ child.setVisible(false);
+ }
+ }
+}
+
+Object.assign(
+ TypeWriter.prototype,
+ EventEmitterMethods,
+ Methods,
+);
+
+export default TypeWriter;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Typing.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Typing.js
new file mode 100644
index 000000000..405be5f5d
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Typing.js
@@ -0,0 +1,94 @@
+import { IsCommand, IsSpaceChar } from '../../dynamictext/bob/Types.js';
+import { TypingDelayTimerType, TypingAnimationTimerType, } from './TimerTypes.js';
+
+var Typing = function (offsetTime) {
+ if (offsetTime === undefined) {
+ offsetTime = 0;
+ }
+
+ var delay = 0;
+ this.inTypingProcessLoop = true;
+ while (this.inTypingProcessLoop) {
+ var child = this.getNextChild();
+ if (!child) {
+ if (this.timeline.isRunning) {
+ // Wait until last animationConfig is end
+ this.timeline.once('complete', function () {
+ this.isPageTyping = false;
+ this.emit('complete');
+ }, this);
+ } else {
+ this.isPageTyping = false;
+ this.emit('complete');
+ }
+ break; // Leave this typing loop
+ }
+
+ if (child.renderable) {
+ // Typing this char
+ var animationConfig = this.animationConfig;
+ if (animationConfig.duration > 0) {
+ var animationTimer = this.timeline.addTimer({
+ name: TypingAnimationTimerType,
+ target: child,
+ duration: animationConfig.duration,
+ yoyo: animationConfig.yoyo,
+ onStart: animationConfig.onStart,
+ onProgress: animationConfig.onProgress,
+ onComplete: animationConfig.onComplete,
+ })
+ if (this.skipTypingAnimation) {
+ animationTimer.seek(1);
+ }
+ } else { // No animationConfig, only invoke onStart callback
+ if (animationConfig.onStart) {
+ animationConfig.onStart(child, 0);
+ }
+ }
+
+ // Set to min size
+ if (this.minSizeEnable) {
+ this.textPlayer.setToMinSize();
+ }
+
+ this.textPlayer.emit('typing', child);
+
+ var nextChild = this.nextChild;
+ if (nextChild) {
+ if (this.skipSpaceEnable && IsSpaceChar(nextChild)) {
+ // Don't have delay when typing a space character
+ } else {
+ delay += (this.speed + offsetTime);
+ offsetTime = 0;
+ if (delay > 0) {
+ // Process next character later
+ this.typingTimer = this.timeline.addTimer({
+ name: TypingDelayTimerType,
+ target: this,
+ duration: delay,
+ onComplete: function (target, t, timer) {
+ target.typingTimer = undefined;
+ Typing.call(target, timer.remainder);
+ }
+ })
+ break; // Leave this typing loop
+ }
+ }
+ }
+ // Process next child
+ } else if (IsCommand(child)) {
+ child.exec();
+ // Process next child
+ }
+
+ }
+
+ // Set to min size
+ if (this.minSizeEnable) {
+ this.textPlayer.setToMinSize();
+ }
+
+ this.inTypingProcessLoop = false;
+}
+
+export default Typing;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypingSpeedMethods.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypingSpeedMethods.js
new file mode 100644
index 000000000..41649cb86
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/TypingSpeedMethods.js
@@ -0,0 +1,14 @@
+export default {
+ setDefaultTypingSpeed(speed) {
+ this.defaultSpeed = speed;
+ return this;
+ },
+
+ setTypingSpeed(speed) {
+ if (speed === undefined) {
+ speed = this.defaultSpeed;
+ }
+ this.speed = speed;
+ return this;
+ },
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Wait.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Wait.js
new file mode 100644
index 000000000..9af94caf1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/dynamictext/textplayer/typewriter/Wait.js
@@ -0,0 +1,15 @@
+import WaitMultiple from '../methods/utils/wait/WaitMultiple.js';
+
+var Wait = function (name) {
+ // Already in typingPaused state, or ignore any wait
+ if (this.ignoreWait) {
+ return this;
+ }
+
+ this.pauseTyping();
+ WaitMultiple(this.textPlayer, name, this.resumeTyping, [], this);
+
+ return this;
+}
+
+export default Wait;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.d.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.d.ts
new file mode 100644
index 000000000..c4cccea02
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.d.ts
@@ -0,0 +1,31 @@
+import GOManager from '../../../utils/gameobject/gomanager/GOManager';
+
+export default LayerManager;
+
+declare namespace LayerManager {
+ interface IConfig {
+ layers?: string[];
+
+ createGameObject?: GOManager.CreateGameObjectCallbackType,
+ }
+}
+
+declare class LayerManager extends GOManager {
+ constructor(
+ scene: Phaser.Scene,
+ config?: LayerManager.IConfig
+ )
+ constructor(
+ scene: Phaser.Scene,
+ config?: string[]
+ )
+
+ getLayer(name: string): Phaser.GameObjects.Layer;
+
+ getLayers(out?: Phaser.GameObjects.GameObject[]): Phaser.GameObjects.Layer[];
+
+ addToLayer(
+ name: string,
+ gameObject: Phaser.GameObjects.GameObject
+ ): this;
+}
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.js b/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.js
new file mode 100644
index 000000000..7b2154b3f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/layer/layermanager/LayerManager.js
@@ -0,0 +1,90 @@
+import GOManager from '../../../utils/gameobject/gomanager/GOManager.js';
+import SortGameObjectsByDepth from '../../../utils/system/SortGameObjectsByDepth.js';
+
+const GetValue = Phaser.Utils.Objects.GetValue;
+
+class LayerManager extends GOManager {
+ constructor(scene, config) {
+ if (config === undefined) {
+ config = {};
+ } else if (Array.isArray(config)) {
+ config = {
+ layers: config
+ }
+ }
+
+ if (!config.hasOwnProperty('fade')) {
+ config.fade = 0;
+ }
+
+ config.viewportCoordinate = false;
+
+ super(scene, config);
+
+ var initLayers = GetValue(config, 'layers');
+ if (initLayers) {
+ for (var i = 0, cnt = initLayers.length; i < cnt; i++) {
+ this.add(initLayers[i]);
+ }
+ }
+ }
+
+ setCreateGameObjectCallback(callback, scope) {
+ if (!callback) {
+ callback = CreateLayer;
+ }
+ super.setCreateGameObjectCallback(callback, scope);
+ return this;
+ }
+
+ // Override
+ addGO(name, gameObject) {
+ super.addGO(name, gameObject);
+ gameObject.name = name;
+
+ return this;
+ }
+
+ // New methods
+ getLayer(name) {
+ return this.getGO(name);
+ }
+
+ getLayers(out) {
+ if (out === undefined) {
+ out = [];
+ }
+ this.forEachGO(function (gameObject) {
+ out.push(gameObject);
+ })
+ SortGameObjectsByDepth(out, false);
+ return out;
+ }
+
+ addToLayer(name, gameObject) {
+ var layer = this.getGO(name);
+ if (!layer) {
+ console.warn(`Can't get layer "${name}"`);
+ return;
+ }
+
+ if (gameObject.isRexContainerLite) {
+ gameObject.addToLayer(layer);
+ } else {
+ layer.add(gameObject);
+ }
+
+ return this;
+ }
+}
+
+var CreateLayer = function (scene, depth) {
+ var layer = scene.add.layer();
+ if (depth !== undefined) {
+ layer.setDepth(depth);
+ }
+ return layer;
+}
+
+
+export default LayerManager;
\ No newline at end of file
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/.eslintrc.yml b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/.eslintrc.yml
new file mode 100644
index 000000000..9d17f9ae5
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/.eslintrc.yml
@@ -0,0 +1,30 @@
+extends:
+ - eslint:recommended
+ - plugin:@typescript-eslint/eslint-recommended
+ - plugin:@typescript-eslint/recommended
+ - plugin:@typescript-eslint/recommended-requiring-type-checking
+ - plugin:prettier/recommended
+ - prettier/@typescript-eslint
+plugins:
+ - '@typescript-eslint'
+parser: '@typescript-eslint/parser'
+parserOptions:
+ sourceType: module
+ ecmaVersion: 2020
+ project: ./tsconfig.json
+rules:
+ prettier/prettier:
+ - error
+ - singleQuote: true
+ '@typescript-eslint/camelcase': warn
+ '@typescript-eslint/no-use-before-define': off
+ no-empty-function: off
+ '@typescript-eslint/no-empty-function':
+ - error
+ - allow:
+ - constructors
+ 'no-fallthrough': warn
+ '@typescript-eslint/unbound-method': off
+ 'no-inner-declarations': off
+ '@typescript-eslint/class-name-casing': warn
+ 'prefer-const': warn
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/CHANGELOG.md b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/CHANGELOG.md
new file mode 100644
index 000000000..be8d7042b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/CHANGELOG.md
@@ -0,0 +1,73 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+## [4-r.4] - 2021-12-09
+
+### Fixed
+
+* Fix useless void 0.
+* Fix a warning when `SegmentType` could not be obtained when loading motion.
+* Fix return correct error values for out-of-index arguments in cubismjson by [@cocor-au-lait](https://github.com/cocor-au-lait).
+* Fix a bug that motions currently played do not fade out when play a motion.
+
+
+## [4-r.3] - 2021-06-10
+
+### Fixed
+
+* Fix motion event time value from Int to Float.
+
+
+## [4-r.3-beta.1] - 2021-05-13
+
+### Added
+
+* Implement a function to get the correct value when the time axis of the Bezier handle cannot be linear.
+
+
+## [4-r.2] - 2021-03-09
+
+### Fixed
+
+* Fix implementation of `iterator#increment` in `csmmap` and `csmvector`.
+* Fix delay in starting fade-out for expressions.
+* Fix Physics input reflect flag on evaluate.
+* Fix reference size of model matrix.
+* Fix `Int` to `Float` when getting `PhysicsSettings.Vertices.Radius` in `physics3.json` parsing.
+ * **[INFO]** This fix may change the behavior of the physics operations.
+ The behavior changes if the value of `PhysicsSettings.Vertices.Radius` in `physics3.json` is less than `1.0`.
+ If you want to return to the behavior before Cubism SDK for Web R1,
+ change the value of the corresponding `PhysicsSettings.Vertices.Radius` to `0`.
+ * This fix is related to fix applied to `Cubism Editor 4.0.05 beta1` and later.
+ Please see [Cubism Editor Changelog](https://docs.live2d.com/cubism-editor-manual/updates4/).
+ * **Fix physics and scene blending settings where the length of the pendulum would be converted to an integer when displayed.**
+
+### Changed
+
+* Rename the function name that handles seconds from `Time` to `Seconds`.
+* Avoiding needless namespace syntax to simplify imports by [@cocor-au-lait](https://github.com/cocor-au-lait)
+
+
+## [4-r.1] - 2020-01-30
+
+### Added
+
+* Add `.editorconfig`, `.gitattributes` and `.gitignore`.
+* Add document `README.md` and `CHANGELOG.md`.
+* Add `package.json` for development and build.
+* Add Prettier and ESLint for format and check code quolity.
+
+### Changed
+
+* Move source files to `/src` directory.
+* Reformat code using Prettier and ESLint.
+
+
+[4-r.4]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3...4-r.4
+[4-r.3]: https://github.com/Live2D/CubismWebFramework/compare/4-r.3-beta.1...4-r.3
+[4-r.3-beta.1]: https://github.com/Live2D/CubismWebFramework/compare/4-r.2...4-r.3-beta.1
+[4-r.2]: https://github.com/Live2D/CubismWebFramework/compare/4-r.1...4-r.2
+[4-r.1]: https://github.com/Live2D/CubismWebFramework/compare/ce2585a919ac6e99f64dd468933772c6f1abbcc7...4-r.1
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/LICENSE.md b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/LICENSE.md
new file mode 100644
index 000000000..bd0be75fc
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/LICENSE.md
@@ -0,0 +1,45 @@
+## Definitions
+
+### Live2D Cubism Components
+
+Cubism Web Framework is included in Live2D Cubism Components.
+
+Cubism Web Framework は Live2D Cubism Components に含まれます。
+
+Cubism Web Framework 包括在 Live2D Cubism Components 中。
+
+## Cubism SDK Release License
+
+*All business* users must obtain a Cubism SDK Release License. "Business" means an entity with the annual gross revenue more than ten million (10,000,000) JPY for the most recent fiscal year.
+
+* [Cubism SDK Release License](https://www.live2d.com/en/download/cubism-sdk/release-license/)
+
+直近会計年度の売上高が 1000 万円以上の事業者様がご利用になる場合は、Cubism SDK リリースライセンス(出版許諾契約)に同意していただく必要がございます。
+
+* [Cubism SDK リリースライセンス](https://www.live2d.com/ja/download/cubism-sdk/release-license/)
+
+如果您的企业在最近一个会计年度的销售额达到或超过1000万日元,您必须得到Cubism SDK的出版授权许可(出版许可协议)。
+
+* [Cubism SDK发行许可证](https://www.live2d.com/zh-CHS/download/cubism-sdk/release-license/)
+
+## Live2D Open Software License
+
+Live2D Cubism Components is available under Live2D Open Software License.
+
+* [Live2D Open Software License](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html)
+* [Live2D Open Software 使用許諾契約書](https://www.live2d.com/eula/live2d-open-software-license-agreement_jp.html)
+* [Live2D Open Software 使用授权协议](https://www.live2d.com/eula/live2d-open-software-license-agreement_cn.html)
+
+
+## Live2D Proprietary Software License
+
+Live2D Cubism Core is available under Live2D Proprietary Software License.
+
+* [Live2D Proprietary Software License Agreement](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html)
+* [Live2D Proprietary Software 使用許諾契約書](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_jp.html)
+* [Live2D Proprietary Software 使用授权协议](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_cn.html)
+
+
+---
+
+Please contact us from [here](https://www.live2d.jp/contact/) for more license information.
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/README.md b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/README.md
new file mode 100644
index 000000000..5e1d4c4ff
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/README.md
@@ -0,0 +1,143 @@
+# Cubism Web Framework
+
+Live2D Cubism 4 Editor で出力したモデルをアプリケーションで利用するためのフレームワークです。
+
+モデルを表示、操作するための各種機能を提供します。
+モデルをロードするには Live2D Cubism Core ライブラリと組み合わせて使用します。
+
+ビルドを行うことで、ブラウザで利用可能な JavaScript ライブラリとして利用することができます。
+
+
+## ライセンス
+
+本 SDK を使用する前に、[ライセンス](LICENSE.md)をご確認ください。
+
+
+## 開発環境
+
+### Node.js
+
+* 17.2.0
+* 16.13.1
+* 14.18.2
+* 12.22.7
+
+### TypeScript
+
+4.5.2
+
+
+## 開発環境構築
+
+1. [Node.js] と [Visual Studio Code] をインストールします
+1. Visual Studio Code で本プロジェクトを開き、推奨拡張機能をインストールします
+ * 拡張機能タブから `@recommended` と入力することで確認できます
+1. コマンドパレット(*View > Command Palette...*)で `>Tasks: Run Task` を入力してタスク一覧を表示します
+1. `npm: install` を選択して依存パッケージのダウンロードを行います
+
+コマンドパレットのタスク一覧から各種コマンドを実行することができます。
+
+NOTE: デバック用の設定は、`.vscode/tasks.json` に記述しています。
+
+## タスク一覧
+
+### `npm: build`
+
+ソースファイルのビルドを行い、`dist` ディレクトリに出力します。
+
+`tsconfig.json` を編集することで設定内容を変更できます。
+
+### `npm: test`
+
+TypeScript の型チェックテストを行います。
+
+`tsconfig.json` を編集することで設定内容を変更できます。
+
+### `npm: lint`
+
+`src` ディレクトリ内の TypeScript ファイルの静的解析を行います。
+
+`.eslintrc.yml` を編集することで設定内容を変更できます。
+
+### `npm: lint:fix`
+
+`src` ディレクトリ内の TypeScript ファイルの静的解析及び自動修正を行います。
+
+`.eslintrc.yml` を編集することで設定内容を変更できます。
+
+### `npm: clean`
+
+ビルド成果物ディレクトリ(`dist`)を削除します。
+
+
+## コンポーネント
+
+### effect
+
+自動まばたきやリップシンクなど、モデルに対してモーション情報をエフェクト的に付加する機能を提供します。
+
+### id
+
+モデルに設定されたパラメータ名・パーツ名・Drawable名を独自の型で管理する機能を提供します。
+
+### math
+
+行列計算やベクトル計算など、モデルの操作や描画に必要な算術演算の機能を提供します。
+
+### model
+
+モデルを取り扱うための各種機能(生成、更新、破棄)を提供します。
+
+### motion
+
+モデルにモーションデータを適用するための各種機能(モーション再生、パラメータブレンド)を提供します。
+
+### physics
+
+モデルに物理演算による変形操作を適用するための機能を提供します。
+
+### rendering
+
+モデルを描画するためのグラフィックス命令を実装したレンダラを提供します。
+
+### type
+
+フレームワーク内で使用する型定義を提供します。
+
+### utils
+
+JSONパーサーやログ出力などのユーティリティ機能を提供します。
+
+
+## Live2D Cubism Core for Web
+
+当リポジトリには Cubism Core for Web は同梱されていません。
+
+[Cubism SDK for Web] からダウンロードしてください。
+
+[Cubism SDK for Web]: https://www.live2d.com/download/cubism-sdk/download-web/
+
+
+## サンプル
+
+標準的なアプリケーションの実装例は [CubismWebSamples] を参照ください。
+
+[CubismWebSamples]: https://github.com/Live2D/CubismWebSamples
+
+
+## マニュアル
+
+[Cubism SDK Manual](https://docs.live2d.com/cubism-sdk-manual/top/)
+
+
+## 変更履歴
+
+当リポジトリの変更履歴については [CHANGELOG.md](CHANGELOG.md) を参照ください。
+
+
+## コミュニティ
+
+ユーザー同士でCubism SDKの活用方法の提案や質問をしたい場合は、是非コミュニティをご活用ください。
+
+- [Live2D 公式コミュニティ](https://creatorsforum.live2d.com/)
+- [Live2D community(English)](http://community.live2d.com/)
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package-lock.json b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package-lock.json
new file mode 100644
index 000000000..898d14ef1
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package-lock.json
@@ -0,0 +1,1157 @@
+{
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
+ "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@types/eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
+ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
+ "dev": true
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.18.0.tgz",
+ "integrity": "sha512-kuO8WQjV+RCZvAXVRJfXWiJ8iYEtfHlKgcqqqXg9uUkIolEHuUaMmm8/lcO4xwCOtaw6mY0gStn2Lg4/eUXXYQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/experimental-utils": "2.18.0",
+ "eslint-utils": "^1.4.3",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^3.0.0",
+ "tsutils": "^3.17.1"
+ }
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.18.0.tgz",
+ "integrity": "sha512-J6MopKPHuJYmQUkANLip7g9I82ZLe1naCbxZZW3O2sIxTiq/9YYoOELEKY7oPg0hJ0V/AQ225h2z0Yp+RRMXhw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/typescript-estree": "2.18.0",
+ "eslint-scope": "^5.0.0"
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.18.0.tgz",
+ "integrity": "sha512-SJJPxFMEYEWkM6pGfcnjLU+NJIPo+Ko1QrCBL+i0+zV30ggLD90huEmMMhKLHBpESWy9lVEeWlQibweNQzyc+A==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-visitor-keys": "^1.0.0",
+ "@typescript-eslint/experimental-utils": "2.18.0",
+ "@typescript-eslint/typescript-estree": "2.18.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.18.0.tgz",
+ "integrity": "sha512-gVHylf7FDb8VSi2ypFuEL3hOtoC4HkZZ5dOjXvVjoyKdRrvXAOPSzpNRnKMfaUUEiSLP8UF9j9X9EDLxC0lfZg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "eslint-visitor-keys": "^1.1.0",
+ "glob": "^7.1.6",
+ "is-glob": "^4.0.1",
+ "lodash": "^4.17.15",
+ "semver": "^6.3.0",
+ "tsutils": "^3.17.1"
+ }
+ },
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
+ "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "dependencies": {
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-escapes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+ "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+ "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.3",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^7.0.0",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.3",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz",
+ "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^6.0.0"
+ }
+ },
+ "eslint-plugin-prettier": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz",
+ "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "dev": true
+ },
+ "espree": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
+ "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.0",
+ "acorn-jsx": "^5.1.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "figures": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
+ "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "flatted": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+ "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "12.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
+ "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.3.tgz",
+ "integrity": "sha512-+OiOVeVydu4hnCGLCSX+wedovR/Yzskv9BFqUNNKq9uU2qg7LCcCo3R86S2E7WLo0y/x2pnEZfZe1CoYnORUAw==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^2.4.2",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.15",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.5.3",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^5.1.0",
+ "through": "^2.3.6"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "prettier": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
+ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
+ "dev": true
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "regexpp": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz",
+ "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.1.tgz",
+ "integrity": "sha512-IQ4ikL8SjBiEDZfk+DFVwqRK8md24RWMEJkdSlgNLkyyAImcjf8SWvU1qFMDOb4igBClbTQ/ugPqXcRwdFTxZw==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "rxjs": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
+ "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ }
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true
+ }
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "tslib": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+ "dev": true
+ },
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.7.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
+ "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ }
+ }
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package.json b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package.json
new file mode 100644
index 000000000..0625a0089
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/package.json
@@ -0,0 +1,20 @@
+{
+ "private": true,
+ "scripts": {
+ "build": "tsc",
+ "test": "tsc --noEmit",
+ "lint": "eslint src --ext .ts",
+ "lint:fix": "eslint src --ext .ts --fix",
+ "clean": "rimraf dist"
+ },
+ "devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^2.18.0",
+ "@typescript-eslint/parser": "^2.18.0",
+ "eslint": "^6.8.0",
+ "eslint-config-prettier": "^6.10.0",
+ "eslint-plugin-prettier": "^3.1.2",
+ "prettier": "^1.19.1",
+ "rimraf": "^3.0.1",
+ "typescript": "^3.7.5"
+ }
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismdefaultparameterid.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismdefaultparameterid.ts
new file mode 100644
index 000000000..1d716d59b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismdefaultparameterid.ts
@@ -0,0 +1,118 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * @brief パラメータIDのデフォルト値を保持する定数
+ * デフォルト値の仕様は以下のマニュアルに基づく
+ * https://docs.live2d.com/cubism-editor-manual/standard-parametor-list/
+ */
+export const CubismDefaultParameterId = Object.freeze>({
+ // パーツID
+ HitAreaPrefix: 'HitArea',
+ HitAreaHead: 'Head',
+ HitAreaBody: 'Body',
+ PartsIdCore: 'Parts01Core',
+ PartsArmPrefix: 'Parts01Arm_',
+ PartsArmLPrefix: 'Parts01ArmL_',
+ PartsArmRPrefix: 'Parts01ArmR_',
+ // パラメータID
+ ParamAngleX: 'ParamAngleX',
+ ParamAngleY: 'ParamAngleY',
+ ParamAngleZ: 'ParamAngleZ',
+ ParamEyeLOpen: 'ParamEyeLOpen',
+ ParamEyeLSmile: 'ParamEyeLSmile',
+ ParamEyeROpen: 'ParamEyeROpen',
+ ParamEyeRSmile: 'ParamEyeRSmile',
+ ParamEyeBallX: 'ParamEyeBallX',
+ ParamEyeBallY: 'ParamEyeBallY',
+ ParamEyeBallForm: 'ParamEyeBallForm',
+ ParamBrowLY: 'ParamBrowLY',
+ ParamBrowRY: 'ParamBrowRY',
+ ParamBrowLX: 'ParamBrowLX',
+ ParamBrowRX: 'ParamBrowRX',
+ ParamBrowLAngle: 'ParamBrowLAngle',
+ ParamBrowRAngle: 'ParamBrowRAngle',
+ ParamBrowLForm: 'ParamBrowLForm',
+ ParamBrowRForm: 'ParamBrowRForm',
+ ParamMouthForm: 'ParamMouthForm',
+ ParamMouthOpenY: 'ParamMouthOpenY',
+ ParamCheek: 'ParamCheek',
+ ParamBodyAngleX: 'ParamBodyAngleX',
+ ParamBodyAngleY: 'ParamBodyAngleY',
+ ParamBodyAngleZ: 'ParamBodyAngleZ',
+ ParamBreath: 'ParamBreath',
+ ParamArmLA: 'ParamArmLA',
+ ParamArmRA: 'ParamArmRA',
+ ParamArmLB: 'ParamArmLB',
+ ParamArmRB: 'ParamArmRB',
+ ParamHandL: 'ParamHandL',
+ ParamHandR: 'ParamHandR',
+ ParamHairFront: 'ParamHairFront',
+ ParamHairSide: 'ParamHairSide',
+ ParamHairBack: 'ParamHairBack',
+ ParamHairFluffy: 'ParamHairFluffy',
+ ParamShoulderY: 'ParamShoulderY',
+ ParamBustX: 'ParamBustX',
+ ParamBustY: 'ParamBustY',
+ ParamBaseX: 'ParamBaseX',
+ ParamBaseY: 'ParamBaseY',
+ ParamNONE: 'NONE:'
+});
+
+// Namespace definition for compatibility.
+import * as $ from './cubismdefaultparameterid';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const HitAreaBody = $.CubismDefaultParameterId.HitAreaBody;
+ export const HitAreaHead = $.CubismDefaultParameterId.HitAreaHead;
+ export const HitAreaPrefix = $.CubismDefaultParameterId.HitAreaPrefix;
+ export const ParamAngleX = $.CubismDefaultParameterId.ParamAngleX;
+ export const ParamAngleY = $.CubismDefaultParameterId.ParamAngleY;
+ export const ParamAngleZ = $.CubismDefaultParameterId.ParamAngleZ;
+ export const ParamArmLA = $.CubismDefaultParameterId.ParamArmLA;
+ export const ParamArmLB = $.CubismDefaultParameterId.ParamArmLB;
+ export const ParamArmRA = $.CubismDefaultParameterId.ParamArmRA;
+ export const ParamArmRB = $.CubismDefaultParameterId.ParamArmRB;
+ export const ParamBaseX = $.CubismDefaultParameterId.ParamBaseX;
+ export const ParamBaseY = $.CubismDefaultParameterId.ParamBaseY;
+ export const ParamBodyAngleX = $.CubismDefaultParameterId.ParamBodyAngleX;
+ export const ParamBodyAngleY = $.CubismDefaultParameterId.ParamBodyAngleY;
+ export const ParamBodyAngleZ = $.CubismDefaultParameterId.ParamBodyAngleZ;
+ export const ParamBreath = $.CubismDefaultParameterId.ParamBreath;
+ export const ParamBrowLAngle = $.CubismDefaultParameterId.ParamBrowLAngle;
+ export const ParamBrowLForm = $.CubismDefaultParameterId.ParamBrowLForm;
+ export const ParamBrowLX = $.CubismDefaultParameterId.ParamBrowLX;
+ export const ParamBrowLY = $.CubismDefaultParameterId.ParamBrowLY;
+ export const ParamBrowRAngle = $.CubismDefaultParameterId.ParamBrowRAngle;
+ export const ParamBrowRForm = $.CubismDefaultParameterId.ParamBrowRForm;
+ export const ParamBrowRX = $.CubismDefaultParameterId.ParamBrowRX;
+ export const ParamBrowRY = $.CubismDefaultParameterId.ParamBrowRY;
+ export const ParamBustX = $.CubismDefaultParameterId.ParamBustX;
+ export const ParamBustY = $.CubismDefaultParameterId.ParamBustY;
+ export const ParamCheek = $.CubismDefaultParameterId.ParamCheek;
+ export const ParamEyeBallForm = $.CubismDefaultParameterId.ParamEyeBallForm;
+ export const ParamEyeBallX = $.CubismDefaultParameterId.ParamEyeBallX;
+ export const ParamEyeBallY = $.CubismDefaultParameterId.ParamEyeBallY;
+ export const ParamEyeLOpen = $.CubismDefaultParameterId.ParamEyeLOpen;
+ export const ParamEyeLSmile = $.CubismDefaultParameterId.ParamEyeLSmile;
+ export const ParamEyeROpen = $.CubismDefaultParameterId.ParamEyeROpen;
+ export const ParamEyeRSmile = $.CubismDefaultParameterId.ParamEyeRSmile;
+ export const ParamHairBack = $.CubismDefaultParameterId.ParamHairBack;
+ export const ParamHairFluffy = $.CubismDefaultParameterId.ParamHairFluffy;
+ export const ParamHairFront = $.CubismDefaultParameterId.ParamHairFront;
+ export const ParamHairSide = $.CubismDefaultParameterId.ParamHairSide;
+ export const ParamHandL = $.CubismDefaultParameterId.ParamHandL;
+ export const ParamHandR = $.CubismDefaultParameterId.ParamHandR;
+ export const ParamMouthForm = $.CubismDefaultParameterId.ParamMouthForm;
+ export const ParamMouthOpenY = $.CubismDefaultParameterId.ParamMouthOpenY;
+ export const ParamNONE = $.CubismDefaultParameterId.ParamNONE;
+ export const ParamShoulderY = $.CubismDefaultParameterId.ParamShoulderY;
+ export const PartsArmLPrefix = $.CubismDefaultParameterId.PartsArmLPrefix;
+ export const PartsArmPrefix = $.CubismDefaultParameterId.PartsArmPrefix;
+ export const PartsArmRPrefix = $.CubismDefaultParameterId.PartsArmRPrefix;
+ export const PartsIdCore = $.CubismDefaultParameterId.PartsIdCore;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismframeworkconfig.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismframeworkconfig.ts
new file mode 100644
index 000000000..3ff7d4607
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismframeworkconfig.ts
@@ -0,0 +1,32 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+//========================================================
+// ログ出力関数の設定
+//========================================================
+
+//---------- ログ出力レベル 選択項目 定義 ----------
+// 詳細ログ出力設定
+export const CSM_LOG_LEVEL_VERBOSE = 0;
+// デバッグログ出力設定
+export const CSM_LOG_LEVEL_DEBUG = 1;
+// Infoログ出力設定
+export const CSM_LOG_LEVEL_INFO = 2;
+// 警告ログ出力設定
+export const CSM_LOG_LEVEL_WARNING = 3;
+// エラーログ出力設定
+export const CSM_LOG_LEVEL_ERROR = 4;
+// ログ出力オフ設定
+export const CSM_LOG_LEVEL_OFF = 5;
+
+/**
+ * ログ出力レベル設定。
+ *
+ * 強制的にログ出力レベルを変える時に定義を有効にする。
+ * CSM_LOG_LEVEL_VERBOSE ~ CSM_LOG_LEVEL_OFF を選択する。
+ */
+export const CSM_LOG_LEVEL: number = CSM_LOG_LEVEL_VERBOSE;
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismmodelsettingjson.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismmodelsettingjson.ts
new file mode 100644
index 000000000..5e635c611
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/cubismmodelsettingjson.ts
@@ -0,0 +1,830 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { ICubismModelSetting } from './icubismmodelsetting';
+import { CubismIdHandle } from './id/cubismid';
+import { CubismFramework } from './live2dcubismframework';
+import { csmMap, iterator } from './type/csmmap';
+import { csmVector } from './type/csmvector';
+import { CubismJson, Value } from './utils/cubismjson';
+
+/**
+ * Model3Jsonのキー文字列
+ */
+
+// JSON Keys
+const Version = 'Version';
+const FileReferences = 'FileReferences';
+const Groups = 'Groups';
+const Layout = 'Layout';
+const HitAreas = 'HitAreas';
+
+const Moc = 'Moc';
+const Textures = 'Textures';
+const Physics = 'Physics';
+const Pose = 'Pose';
+const Expressions = 'Expressions';
+const Motions = 'Motions';
+
+const UserData = 'UserData';
+const Name = 'Name';
+const FilePath = 'File';
+const Id = 'Id';
+const Ids = 'Ids';
+const Target = 'Target';
+
+// Motions
+const Idle = 'Idle';
+const TapBody = 'TapBody';
+const PinchIn = 'PinchIn';
+const PinchOut = 'PinchOut';
+const Shake = 'Shake';
+const FlickHead = 'FlickHead';
+const Parameter = 'Parameter';
+
+const SoundPath = 'Sound';
+const FadeInTime = 'FadeInTime';
+const FadeOutTime = 'FadeOutTime';
+
+// Layout
+const CenterX = 'CenterX';
+const CenterY = 'CenterY';
+const X = 'X';
+const Y = 'Y';
+const Width = 'Width';
+const Height = 'Height';
+
+const LipSync = 'LipSync';
+const EyeBlink = 'EyeBlink';
+
+const InitParameter = 'init_param';
+const InitPartsVisible = 'init_parts_visible';
+const Val = 'val';
+
+enum FrequestNode {
+ FrequestNode_Groups, // getRoot().getValueByString(Groups)
+ FrequestNode_Moc, // getRoot().getValueByString(FileReferences).getValueByString(Moc)
+ FrequestNode_Motions, // getRoot().getValueByString(FileReferences).getValueByString(Motions)
+ FrequestNode_Expressions, // getRoot().getValueByString(FileReferences).getValueByString(Expressions)
+ FrequestNode_Textures, // getRoot().getValueByString(FileReferences).getValueByString(Textures)
+ FrequestNode_Physics, // getRoot().getValueByString(FileReferences).getValueByString(Physics)
+ FrequestNode_Pose, // getRoot().getValueByString(FileReferences).getValueByString(Pose)
+ FrequestNode_HitAreas // getRoot().getValueByString(HitAreas)
+}
+
+/**
+ * Model3Jsonパーサー
+ *
+ * model3.jsonファイルをパースして値を取得する
+ */
+export class CubismModelSettingJson extends ICubismModelSetting {
+ /**
+ * 引数付きコンストラクタ
+ *
+ * @param buffer Model3Jsonをバイト配列として読み込んだデータバッファ
+ * @param size Model3Jsonのデータサイズ
+ */
+ public constructor(buffer: ArrayBuffer, size: number) {
+ super();
+ this._json = CubismJson.create(buffer, size);
+
+ if (this._json) {
+ this._jsonValue = new csmVector();
+
+ // 順番はenum FrequestNodeと一致させる
+ this._jsonValue.pushBack(this._json.getRoot().getValueByString(Groups));
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Moc)
+ );
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Motions)
+ );
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Expressions)
+ );
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Textures)
+ );
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Physics)
+ );
+ this._jsonValue.pushBack(
+ this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(Pose)
+ );
+ this._jsonValue.pushBack(this._json.getRoot().getValueByString(HitAreas));
+ }
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ CubismJson.delete(this._json);
+
+ this._jsonValue = null;
+ }
+
+ /**
+ * CubismJsonオブジェクトを取得する
+ *
+ * @return CubismJson
+ */
+ public GetJson(): CubismJson {
+ return this._json;
+ }
+
+ /**
+ * Mocファイルの名前を取得する
+ * @return Mocファイルの名前
+ */
+ public getModelFileName(): string {
+ if (!this.isExistModelFile()) {
+ return '';
+ }
+ return this._jsonValue.at(FrequestNode.FrequestNode_Moc).getRawString();
+ }
+
+ /**
+ * モデルが使用するテクスチャの数を取得する
+ * テクスチャの数
+ */
+ public getTextureCount(): number {
+ if (!this.isExistTextureFiles()) {
+ return 0;
+ }
+
+ return this._jsonValue.at(FrequestNode.FrequestNode_Textures).getSize();
+ }
+
+ /**
+ * テクスチャが配置されたディレクトリの名前を取得する
+ * @return テクスチャが配置されたディレクトリの名前
+ */
+ public getTextureDirectory(): string {
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Textures)
+ .getRawString();
+ }
+
+ /**
+ * モデルが使用するテクスチャの名前を取得する
+ * @param index 配列のインデックス値
+ * @return テクスチャの名前
+ */
+ public getTextureFileName(index: number): string {
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Textures)
+ .getValueByIndex(index)
+ .getRawString();
+ }
+
+ /**
+ * モデルに設定された当たり判定の数を取得する
+ * @return モデルに設定された当たり判定の数
+ */
+ public getHitAreasCount(): number {
+ if (!this.isExistHitAreas()) {
+ return 0;
+ }
+
+ return this._jsonValue.at(FrequestNode.FrequestNode_HitAreas).getSize();
+ }
+
+ /**
+ * 当たり判定に設定されたIDを取得する
+ *
+ * @param index 配列のindex
+ * @return 当たり判定に設定されたID
+ */
+ public getHitAreaId(index: number): CubismIdHandle {
+ return CubismFramework.getIdManager().getId(
+ this._jsonValue
+ .at(FrequestNode.FrequestNode_HitAreas)
+ .getValueByIndex(index)
+ .getValueByString(Id)
+ .getRawString()
+ );
+ }
+
+ /**
+ * 当たり判定に設定された名前を取得する
+ * @param index 配列のインデックス値
+ * @return 当たり判定に設定された名前
+ */
+ public getHitAreaName(index: number): string {
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_HitAreas)
+ .getValueByIndex(index)
+ .getValueByString(Name)
+ .getRawString();
+ }
+
+ /**
+ * 物理演算設定ファイルの名前を取得する
+ * @return 物理演算設定ファイルの名前
+ */
+ public getPhysicsFileName(): string {
+ if (!this.isExistPhysicsFile()) {
+ return '';
+ }
+
+ return this._jsonValue.at(FrequestNode.FrequestNode_Physics).getRawString();
+ }
+
+ /**
+ * パーツ切り替え設定ファイルの名前を取得する
+ * @return パーツ切り替え設定ファイルの名前
+ */
+ public getPoseFileName(): string {
+ if (!this.isExistPoseFile()) {
+ return '';
+ }
+
+ return this._jsonValue.at(FrequestNode.FrequestNode_Pose).getRawString();
+ }
+
+ /**
+ * 表情設定ファイルの数を取得する
+ * @return 表情設定ファイルの数
+ */
+ public getExpressionCount(): number {
+ if (!this.isExistExpressionFile()) {
+ return 0;
+ }
+
+ return this._jsonValue.at(FrequestNode.FrequestNode_Expressions).getSize();
+ }
+
+ /**
+ * 表情設定ファイルを識別する名前(別名)を取得する
+ * @param index 配列のインデックス値
+ * @return 表情の名前
+ */
+ public getExpressionName(index: number): string {
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Expressions)
+ .getValueByIndex(index)
+ .getValueByString(Name)
+ .getRawString();
+ }
+
+ /**
+ * 表情設定ファイルの名前を取得する
+ * @param index 配列のインデックス値
+ * @return 表情設定ファイルの名前
+ */
+ public getExpressionFileName(index: number): string {
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Expressions)
+ .getValueByIndex(index)
+ .getValueByString(FilePath)
+ .getRawString();
+ }
+
+ /**
+ * モーショングループの数を取得する
+ * @return モーショングループの数
+ */
+ public getMotionGroupCount(): number {
+ if (!this.isExistMotionGroups()) {
+ return 0;
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getKeys()
+ .getSize();
+ }
+
+ /**
+ * モーショングループの名前を取得する
+ * @param index 配列のインデックス値
+ * @return モーショングループの名前
+ */
+ public getMotionGroupName(index: number): string {
+ if (!this.isExistMotionGroups()) {
+ return null;
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getKeys()
+ .at(index);
+ }
+
+ /**
+ * モーショングループに含まれるモーションの数を取得する
+ * @param groupName モーショングループの名前
+ * @return モーショングループの数
+ */
+ public getMotionCount(groupName: string): number {
+ if (!this.isExistMotionGroupName(groupName)) {
+ return 0;
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getSize();
+ }
+
+ /**
+ * グループ名とインデックス値からモーションファイル名を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return モーションファイルの名前
+ */
+ public getMotionFileName(groupName: string, index: number): string {
+ if (!this.isExistMotionGroupName(groupName)) {
+ return '';
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(FilePath)
+ .getRawString();
+ }
+
+ /**
+ * モーションに対応するサウンドファイルの名前を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return サウンドファイルの名前
+ */
+ public getMotionSoundFileName(groupName: string, index: number): string {
+ if (!this.isExistMotionSoundFile(groupName, index)) {
+ return '';
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(SoundPath)
+ .getRawString();
+ }
+
+ /**
+ * モーション開始時のフェードイン処理時間を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return フェードイン処理時間[秒]
+ */
+ public getMotionFadeInTimeValue(groupName: string, index: number): number {
+ if (!this.isExistMotionFadeIn(groupName, index)) {
+ return -1.0;
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(FadeInTime)
+ .toFloat();
+ }
+
+ /**
+ * モーション終了時のフェードアウト処理時間を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return フェードアウト処理時間[秒]
+ */
+ public getMotionFadeOutTimeValue(groupName: string, index: number): number {
+ if (!this.isExistMotionFadeOut(groupName, index)) {
+ return -1.0;
+ }
+
+ return this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(FadeOutTime)
+ .toFloat();
+ }
+
+ /**
+ * ユーザーデータのファイル名を取得する
+ * @return ユーザーデータのファイル名
+ */
+ public getUserDataFile(): string {
+ if (!this.isExistUserDataFile()) {
+ return '';
+ }
+
+ return this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(UserData)
+ .getRawString();
+ }
+
+ /**
+ * レイアウト情報を取得する
+ * @param outLayoutMap csmMapクラスのインスタンス
+ * @return true レイアウト情報が存在する
+ * @return false レイアウト情報が存在しない
+ */
+ public getLayoutMap(outLayoutMap: csmMap): boolean {
+ // 存在しない要素にアクセスするとエラーになるためValueがnullの場合はnullを代入する
+ const map: csmMap = this._json
+ .getRoot()
+ .getValueByString(Layout)
+ .getMap();
+
+ if (map == null) {
+ return false;
+ }
+
+ let ret = false;
+
+ for (
+ const ite: iterator = map.begin();
+ ite.notEqual(map.end());
+ ite.preIncrement()
+ ) {
+ outLayoutMap.setValue(ite.ptr().first, ite.ptr().second.toFloat());
+ ret = true;
+ }
+
+ return ret;
+ }
+
+ /**
+ * 目パチに関連付けられたパラメータの数を取得する
+ * @return 目パチに関連付けられたパラメータの数
+ */
+ public getEyeBlinkParameterCount(): number {
+ if (!this.isExistEyeBlinkParameters()) {
+ return 0;
+ }
+
+ let num = 0;
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ i++
+ ) {
+ const refI: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i);
+ if (refI.isNull() || refI.isError()) {
+ continue;
+ }
+
+ if (refI.getValueByString(Name).getRawString() == EyeBlink) {
+ num = refI
+ .getValueByString(Ids)
+ .getVector()
+ .getSize();
+ break;
+ }
+ }
+
+ return num;
+ }
+
+ /**
+ * 目パチに関連付けられたパラメータのIDを取得する
+ * @param index 配列のインデックス値
+ * @return パラメータID
+ */
+ public getEyeBlinkParameterId(index: number): CubismIdHandle {
+ if (!this.isExistEyeBlinkParameters()) {
+ return null;
+ }
+
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ i++
+ ) {
+ const refI: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i);
+ if (refI.isNull() || refI.isError()) {
+ continue;
+ }
+
+ if (refI.getValueByString(Name).getRawString() == EyeBlink) {
+ return CubismFramework.getIdManager().getId(
+ refI
+ .getValueByString(Ids)
+ .getValueByIndex(index)
+ .getRawString()
+ );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * リップシンクに関連付けられたパラメータの数を取得する
+ * @return リップシンクに関連付けられたパラメータの数
+ */
+ public getLipSyncParameterCount(): number {
+ if (!this.isExistLipSyncParameters()) {
+ return 0;
+ }
+
+ let num = 0;
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ i++
+ ) {
+ const refI: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i);
+ if (refI.isNull() || refI.isError()) {
+ continue;
+ }
+
+ if (refI.getValueByString(Name).getRawString() == LipSync) {
+ num = refI
+ .getValueByString(Ids)
+ .getVector()
+ .getSize();
+ break;
+ }
+ }
+
+ return num;
+ }
+
+ /**
+ * リップシンクに関連付けられたパラメータの数を取得する
+ * @param index 配列のインデックス値
+ * @return パラメータID
+ */
+ public getLipSyncParameterId(index: number): CubismIdHandle {
+ if (!this.isExistLipSyncParameters()) {
+ return null;
+ }
+
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ i++
+ ) {
+ const refI: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i);
+ if (refI.isNull() || refI.isError()) {
+ continue;
+ }
+
+ if (refI.getValueByString(Name).getRawString() == LipSync) {
+ return CubismFramework.getIdManager().getId(
+ refI
+ .getValueByString(Ids)
+ .getValueByIndex(index)
+ .getRawString()
+ );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * モデルファイルのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistModelFile(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Moc);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * テクスチャファイルのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistTextureFiles(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Textures);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 当たり判定のキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistHitAreas(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_HitAreas);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 物理演算ファイルのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistPhysicsFile(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Physics);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * ポーズ設定ファイルのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistPoseFile(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Pose);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 表情設定ファイルのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistExpressionFile(): boolean {
+ const node: Value = this._jsonValue.at(
+ FrequestNode.FrequestNode_Expressions
+ );
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * モーショングループのキーが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistMotionGroups(): boolean {
+ const node: Value = this._jsonValue.at(FrequestNode.FrequestNode_Motions);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 引数で指定したモーショングループのキーが存在するかどうかを確認する
+ * @param groupName グループ名
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistMotionGroupName(groupName: string): boolean {
+ const node: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 引数で指定したモーションに対応するサウンドファイルのキーが存在するかどうかを確認する
+ * @param groupName グループ名
+ * @param index 配列のインデックス値
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistMotionSoundFile(groupName: string, index: number): boolean {
+ const node: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(SoundPath);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 引数で指定したモーションに対応するフェードイン時間のキーが存在するかどうかを確認する
+ * @param groupName グループ名
+ * @param index 配列のインデックス値
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistMotionFadeIn(groupName: string, index: number): boolean {
+ const node: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(FadeInTime);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 引数で指定したモーションに対応するフェードアウト時間のキーが存在するかどうかを確認する
+ * @param groupName グループ名
+ * @param index 配列のインデックス値
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistMotionFadeOut(groupName: string, index: number): boolean {
+ const node: Value = this._jsonValue
+ .at(FrequestNode.FrequestNode_Motions)
+ .getValueByString(groupName)
+ .getValueByIndex(index)
+ .getValueByString(FadeOutTime);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * UserDataのファイル名が存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistUserDataFile(): boolean {
+ const node: Value = this._json
+ .getRoot()
+ .getValueByString(FileReferences)
+ .getValueByString(UserData);
+ return !node.isNull() && !node.isError();
+ }
+
+ /**
+ * 目ぱちに対応付けられたパラメータが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistEyeBlinkParameters(): boolean {
+ if (
+ this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() ||
+ this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError()
+ ) {
+ return false;
+ }
+
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ ++i
+ ) {
+ if (
+ this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i)
+ .getValueByString(Name)
+ .getRawString() == EyeBlink
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * リップシンクに対応付けられたパラメータが存在するかどうかを確認する
+ * @return true キーが存在する
+ * @return false キーが存在しない
+ */
+ private isExistLipSyncParameters(): boolean {
+ if (
+ this._jsonValue.at(FrequestNode.FrequestNode_Groups).isNull() ||
+ this._jsonValue.at(FrequestNode.FrequestNode_Groups).isError()
+ ) {
+ return false;
+ }
+ for (
+ let i = 0;
+ i < this._jsonValue.at(FrequestNode.FrequestNode_Groups).getSize();
+ ++i
+ ) {
+ if (
+ this._jsonValue
+ .at(FrequestNode.FrequestNode_Groups)
+ .getValueByIndex(i)
+ .getValueByString(Name)
+ .getRawString() == LipSync
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private _json: CubismJson;
+ private _jsonValue: csmVector;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmodelsettingjson';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismModelSettingJson = $.CubismModelSettingJson;
+ export type CubismModelSettingJson = $.CubismModelSettingJson;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismbreath.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismbreath.ts
new file mode 100644
index 000000000..0f35d2e3c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismbreath.ts
@@ -0,0 +1,124 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismModel } from '../model/cubismmodel';
+import { csmVector } from '../type/csmvector';
+
+/**
+ * 呼吸機能
+ *
+ * 呼吸機能を提供する。
+ */
+export class CubismBreath {
+ /**
+ * インスタンスの作成
+ */
+ public static create(): CubismBreath {
+ return new CubismBreath();
+ }
+
+ /**
+ * インスタンスの破棄
+ * @param instance 対象のCubismBreath
+ */
+ public static delete(instance: CubismBreath): void {
+ if (instance != null) {
+ instance = null;
+ }
+ }
+
+ /**
+ * 呼吸のパラメータの紐づけ
+ * @param breathParameters 呼吸を紐づけたいパラメータのリスト
+ */
+ public setParameters(breathParameters: csmVector): void {
+ this._breathParameters = breathParameters;
+ }
+
+ /**
+ * 呼吸に紐づいているパラメータの取得
+ * @return 呼吸に紐づいているパラメータのリスト
+ */
+ public getParameters(): csmVector {
+ return this._breathParameters;
+ }
+
+ /**
+ * モデルのパラメータの更新
+ * @param model 対象のモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ */
+ public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
+ this._currentTime += deltaTimeSeconds;
+
+ const t: number = this._currentTime * 2.0 * 3.14159;
+
+ for (let i = 0; i < this._breathParameters.getSize(); ++i) {
+ const data: BreathParameterData = this._breathParameters.at(i);
+
+ model.addParameterValueById(
+ data.parameterId,
+ data.offset + data.peak * Math.sin(t / data.cycle),
+ data.weight
+ );
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._currentTime = 0.0;
+ }
+
+ _breathParameters: csmVector; // 呼吸にひもづいているパラメータのリスト
+ _currentTime: number; // 積算時間[秒]
+}
+
+/**
+ * 呼吸のパラメータ情報
+ */
+export class BreathParameterData {
+ /**
+ * コンストラクタ
+ * @param parameterId 呼吸をひもづけるパラメータID
+ * @param offset 呼吸を正弦波としたときの、波のオフセット
+ * @param peak 呼吸を正弦波としたときの、波の高さ
+ * @param cycle 呼吸を正弦波としたときの、波の周期
+ * @param weight パラメータへの重み
+ */
+ constructor(
+ parameterId?: CubismIdHandle,
+ offset?: number,
+ peak?: number,
+ cycle?: number,
+ weight?: number
+ ) {
+ this.parameterId = parameterId == undefined ? null : parameterId;
+ this.offset = offset == undefined ? 0.0 : offset;
+ this.peak = peak == undefined ? 0.0 : peak;
+ this.cycle = cycle == undefined ? 0.0 : cycle;
+ this.weight = weight == undefined ? 0.0 : weight;
+ }
+
+ parameterId: CubismIdHandle; // 呼吸をひもづけるパラメータID\
+ offset: number; // 呼吸を正弦波としたときの、波のオフセット
+ peak: number; // 呼吸を正弦波としたときの、波の高さ
+ cycle: number; // 呼吸を正弦波としたときの、波の周期
+ weight: number; // パラメータへの重み
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismbreath';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const BreathParameterData = $.BreathParameterData;
+ export type BreathParameterData = $.BreathParameterData;
+ export const CubismBreath = $.CubismBreath;
+ export type CubismBreath = $.CubismBreath;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismeyeblink.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismeyeblink.ts
new file mode 100644
index 000000000..efb969f61
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismeyeblink.ts
@@ -0,0 +1,233 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { ICubismModelSetting } from '../icubismmodelsetting';
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismModel } from '../model/cubismmodel';
+import { csmVector } from '../type/csmvector';
+
+/**
+ * 自動まばたき機能
+ *
+ * 自動まばたき機能を提供する。
+ */
+export class CubismEyeBlink {
+ /**
+ * インスタンスを作成する
+ * @param modelSetting モデルの設定情報
+ * @return 作成されたインスタンス
+ * @note 引数がNULLの場合、パラメータIDが設定されていない空のインスタンスを作成する。
+ */
+ public static create(
+ modelSetting: ICubismModelSetting = null
+ ): CubismEyeBlink {
+ return new CubismEyeBlink(modelSetting);
+ }
+
+ /**
+ * インスタンスの破棄
+ * @param eyeBlink 対象のCubismEyeBlink
+ */
+ public static delete(eyeBlink: CubismEyeBlink): void {
+ if (eyeBlink != null) {
+ eyeBlink = null;
+ }
+ }
+
+ /**
+ * まばたきの間隔の設定
+ * @param blinkingInterval まばたきの間隔の時間[秒]
+ */
+ public setBlinkingInterval(blinkingInterval: number): void {
+ this._blinkingIntervalSeconds = blinkingInterval;
+ }
+
+ /**
+ * まばたきのモーションの詳細設定
+ * @param closing まぶたを閉じる動作の所要時間[秒]
+ * @param closed まぶたを閉じている動作の所要時間[秒]
+ * @param opening まぶたを開く動作の所要時間[秒]
+ */
+ public setBlinkingSetting(
+ closing: number,
+ closed: number,
+ opening: number
+ ): void {
+ this._closingSeconds = closing;
+ this._closedSeconds = closed;
+ this._openingSeconds = opening;
+ }
+
+ /**
+ * まばたきさせるパラメータIDのリストの設定
+ * @param parameterIds パラメータのIDのリスト
+ */
+ public setParameterIds(parameterIds: csmVector): void {
+ this._parameterIds = parameterIds;
+ }
+
+ /**
+ * まばたきさせるパラメータIDのリストの取得
+ * @return パラメータIDのリスト
+ */
+ public getParameterIds(): csmVector {
+ return this._parameterIds;
+ }
+
+ /**
+ * モデルのパラメータの更新
+ * @param model 対象のモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ */
+ public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
+ this._userTimeSeconds += deltaTimeSeconds;
+ let parameterValue: number;
+ let t = 0.0;
+
+ switch (this._blinkingState) {
+ case EyeState.EyeState_Closing:
+ t =
+ (this._userTimeSeconds - this._stateStartTimeSeconds) /
+ this._closingSeconds;
+
+ if (t >= 1.0) {
+ t = 1.0;
+ this._blinkingState = EyeState.EyeState_Closed;
+ this._stateStartTimeSeconds = this._userTimeSeconds;
+ }
+
+ parameterValue = 1.0 - t;
+
+ break;
+ case EyeState.EyeState_Closed:
+ t =
+ (this._userTimeSeconds - this._stateStartTimeSeconds) /
+ this._closedSeconds;
+
+ if (t >= 1.0) {
+ this._blinkingState = EyeState.EyeState_Opening;
+ this._stateStartTimeSeconds = this._userTimeSeconds;
+ }
+
+ parameterValue = 0.0;
+
+ break;
+ case EyeState.EyeState_Opening:
+ t =
+ (this._userTimeSeconds - this._stateStartTimeSeconds) /
+ this._openingSeconds;
+
+ if (t >= 1.0) {
+ t = 1.0;
+ this._blinkingState = EyeState.EyeState_Interval;
+ this._nextBlinkingTime = this.determinNextBlinkingTiming();
+ }
+
+ parameterValue = t;
+
+ break;
+ case EyeState.EyeState_Interval:
+ if (this._nextBlinkingTime < this._userTimeSeconds) {
+ this._blinkingState = EyeState.EyeState_Closing;
+ this._stateStartTimeSeconds = this._userTimeSeconds;
+ }
+
+ parameterValue = 1.0;
+
+ break;
+ case EyeState.EyeState_First:
+ default:
+ this._blinkingState = EyeState.EyeState_Interval;
+ this._nextBlinkingTime = this.determinNextBlinkingTiming();
+
+ parameterValue = 1.0;
+ break;
+ }
+
+ if (!CubismEyeBlink.CloseIfZero) {
+ parameterValue = -parameterValue;
+ }
+
+ for (let i = 0; i < this._parameterIds.getSize(); ++i) {
+ model.setParameterValueById(this._parameterIds.at(i), parameterValue);
+ }
+ }
+
+ /**
+ * コンストラクタ
+ * @param modelSetting モデルの設定情報
+ */
+ public constructor(modelSetting: ICubismModelSetting) {
+ this._blinkingState = EyeState.EyeState_First;
+ this._nextBlinkingTime = 0.0;
+ this._stateStartTimeSeconds = 0.0;
+ this._blinkingIntervalSeconds = 4.0;
+ this._closingSeconds = 0.1;
+ this._closedSeconds = 0.05;
+ this._openingSeconds = 0.15;
+ this._userTimeSeconds = 0.0;
+ this._parameterIds = new csmVector();
+
+ if (modelSetting == null) {
+ return;
+ }
+
+ for (let i = 0; i < modelSetting.getEyeBlinkParameterCount(); ++i) {
+ this._parameterIds.pushBack(modelSetting.getEyeBlinkParameterId(i));
+ }
+ }
+
+ /**
+ * 次の瞬きのタイミングの決定
+ *
+ * @return 次のまばたきを行う時刻[秒]
+ */
+ public determinNextBlinkingTiming(): number {
+ const r: number = Math.random();
+ return (
+ this._userTimeSeconds + r * (2.0 * this._blinkingIntervalSeconds - 1.0)
+ );
+ }
+
+ _blinkingState: number; // 現在の状態
+ _parameterIds: csmVector; // 操作対象のパラメータのIDのリスト
+ _nextBlinkingTime: number; // 次のまばたきの時刻[秒]
+ _stateStartTimeSeconds: number; // 現在の状態が開始した時刻[秒]
+ _blinkingIntervalSeconds: number; // まばたきの間隔[秒]
+ _closingSeconds: number; // まぶたを閉じる動作の所要時間[秒]
+ _closedSeconds: number; // まぶたを閉じている動作の所要時間[秒]
+ _openingSeconds: number; // まぶたを開く動作の所要時間[秒]
+ _userTimeSeconds: number; // デルタ時間の積算値[秒]
+
+ /**
+ * IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。
+ */
+ static readonly CloseIfZero: boolean = true;
+}
+
+/**
+ * まばたきの状態
+ *
+ * まばたきの状態を表す列挙型
+ */
+export enum EyeState {
+ EyeState_First = 0, // 初期状態
+ EyeState_Interval, // まばたきしていない状態
+ EyeState_Closing, // まぶたが閉じていく途中の状態
+ EyeState_Closed, // まぶたが閉じている状態
+ EyeState_Opening // まぶたが開いていく途中の状態
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismeyeblink';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismEyeBlink = $.CubismEyeBlink;
+ export type CubismEyeBlink = $.CubismEyeBlink;
+ export const EyeState = $.EyeState;
+ export type EyeState = $.EyeState;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismpose.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismpose.ts
new file mode 100644
index 000000000..ceaedee0b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/effect/cubismpose.ts
@@ -0,0 +1,400 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismModel } from '../model/cubismmodel';
+import { csmVector, iterator } from '../type/csmvector';
+import { CubismJson, Value } from '../utils/cubismjson';
+
+const Epsilon = 0.001;
+const DefaultFadeInSeconds = 0.5;
+
+// Pose.jsonのタグ
+const FadeIn = 'FadeInTime';
+const Link = 'Link';
+const Groups = 'Groups';
+const Id = 'Id';
+
+/**
+ * パーツの不透明度の設定
+ *
+ * パーツの不透明度の管理と設定を行う。
+ */
+export class CubismPose {
+ /**
+ * インスタンスの作成
+ * @param pose3json pose3.jsonのデータ
+ * @param size pose3.jsonのデータのサイズ[byte]
+ * @return 作成されたインスタンス
+ */
+ public static create(pose3json: ArrayBuffer, size: number): CubismPose {
+ const ret: CubismPose = new CubismPose();
+ const json: CubismJson = CubismJson.create(pose3json, size);
+ const root: Value = json.getRoot();
+
+ // フェード時間の指定
+ if (!root.getValueByString(FadeIn).isNull()) {
+ ret._fadeTimeSeconds = root
+ .getValueByString(FadeIn)
+ .toFloat(DefaultFadeInSeconds);
+
+ if (ret._fadeTimeSeconds <= 0.0) {
+ ret._fadeTimeSeconds = DefaultFadeInSeconds;
+ }
+ }
+
+ // パーツグループ
+ const poseListInfo: Value = root.getValueByString(Groups);
+ const poseCount: number = poseListInfo.getSize();
+
+ for (let poseIndex = 0; poseIndex < poseCount; ++poseIndex) {
+ const idListInfo: Value = poseListInfo.getValueByIndex(poseIndex);
+ const idCount: number = idListInfo.getSize();
+ let groupCount = 0;
+
+ for (let groupIndex = 0; groupIndex < idCount; ++groupIndex) {
+ const partInfo: Value = idListInfo.getValueByIndex(groupIndex);
+ const partData: PartData = new PartData();
+ const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId(
+ partInfo.getValueByString(Id).getRawString()
+ );
+
+ partData.partId = parameterId;
+
+ // リンクするパーツの設定
+ if (!partInfo.getValueByString(Link).isNull()) {
+ const linkListInfo: Value = partInfo.getValueByString(Link);
+ const linkCount: number = linkListInfo.getSize();
+
+ for (let linkIndex = 0; linkIndex < linkCount; ++linkIndex) {
+ const linkPart: PartData = new PartData();
+ const linkId: CubismIdHandle = CubismFramework.getIdManager().getId(
+ linkListInfo.getValueByIndex(linkIndex).getString()
+ );
+
+ linkPart.partId = linkId;
+
+ partData.link.pushBack(linkPart);
+ }
+ }
+
+ ret._partGroups.pushBack(partData.clone());
+
+ ++groupCount;
+ }
+
+ ret._partGroupCounts.pushBack(groupCount);
+ }
+
+ CubismJson.delete(json);
+
+ return ret;
+ }
+
+ /**
+ * インスタンスを破棄する
+ * @param pose 対象のCubismPose
+ */
+ public static delete(pose: CubismPose): void {
+ if (pose != null) {
+ pose = null;
+ }
+ }
+
+ /**
+ * モデルのパラメータの更新
+ * @param model 対象のモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ */
+ public updateParameters(model: CubismModel, deltaTimeSeconds: number): void {
+ // 前回のモデルと同じでない場合は初期化が必要
+ if (model != this._lastModel) {
+ // パラメータインデックスの初期化
+ this.reset(model);
+ }
+
+ this._lastModel = model;
+
+ // 設定から時間を変更すると、経過時間がマイナスになる事があるので、経過時間0として対応
+ if (deltaTimeSeconds < 0.0) {
+ deltaTimeSeconds = 0.0;
+ }
+
+ let beginIndex = 0;
+
+ for (let i = 0; i < this._partGroupCounts.getSize(); i++) {
+ const partGroupCount: number = this._partGroupCounts.at(i);
+
+ this.doFade(model, deltaTimeSeconds, beginIndex, partGroupCount);
+
+ beginIndex += partGroupCount;
+ }
+
+ this.copyPartOpacities(model);
+ }
+
+ /**
+ * 表示を初期化
+ * @param model 対象のモデル
+ * @note 不透明度の初期値が0でないパラメータは、不透明度を1に設定する
+ */
+ public reset(model: CubismModel): void {
+ let beginIndex = 0;
+
+ for (let i = 0; i < this._partGroupCounts.getSize(); ++i) {
+ const groupCount: number = this._partGroupCounts.at(i);
+
+ for (let j: number = beginIndex; j < beginIndex + groupCount; ++j) {
+ this._partGroups.at(j).initialize(model);
+
+ const partsIndex: number = this._partGroups.at(j).partIndex;
+ const paramIndex: number = this._partGroups.at(j).parameterIndex;
+
+ if (partsIndex < 0) {
+ continue;
+ }
+
+ model.setPartOpacityByIndex(partsIndex, j == beginIndex ? 1.0 : 0.0);
+ model.setParameterValueByIndex(paramIndex, j == beginIndex ? 1.0 : 0.0);
+
+ for (let k = 0; k < this._partGroups.at(j).link.getSize(); ++k) {
+ this._partGroups
+ .at(j)
+ .link.at(k)
+ .initialize(model);
+ }
+ }
+
+ beginIndex += groupCount;
+ }
+ }
+
+ /**
+ * パーツの不透明度をコピー
+ *
+ * @param model 対象のモデル
+ */
+ public copyPartOpacities(model: CubismModel): void {
+ for (
+ let groupIndex = 0;
+ groupIndex < this._partGroups.getSize();
+ ++groupIndex
+ ) {
+ const partData: PartData = this._partGroups.at(groupIndex);
+
+ if (partData.link.getSize() == 0) {
+ continue; // 連動するパラメータはない
+ }
+
+ const partIndex: number = this._partGroups.at(groupIndex).partIndex;
+ const opacity: number = model.getPartOpacityByIndex(partIndex);
+
+ for (
+ let linkIndex = 0;
+ linkIndex < partData.link.getSize();
+ ++linkIndex
+ ) {
+ const linkPart: PartData = partData.link.at(linkIndex);
+ const linkPartIndex: number = linkPart.partIndex;
+
+ if (linkPartIndex < 0) {
+ continue;
+ }
+
+ model.setPartOpacityByIndex(linkPartIndex, opacity);
+ }
+ }
+ }
+
+ /**
+ * パーツのフェード操作を行う。
+ * @param model 対象のモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ * @param beginIndex フェード操作を行うパーツグループの先頭インデックス
+ * @param partGroupCount フェード操作を行うパーツグループの個数
+ */
+ public doFade(
+ model: CubismModel,
+ deltaTimeSeconds: number,
+ beginIndex: number,
+ partGroupCount: number
+ ): void {
+ let visiblePartIndex = -1;
+ let newOpacity = 1.0;
+
+ const phi = 0.5;
+ const backOpacityThreshold = 0.15;
+
+ // 現在、表示状態になっているパーツを取得
+ for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
+ const partIndex: number = this._partGroups.at(i).partIndex;
+ const paramIndex: number = this._partGroups.at(i).parameterIndex;
+
+ if (model.getParameterValueByIndex(paramIndex) > Epsilon) {
+ if (visiblePartIndex >= 0) {
+ break;
+ }
+
+ visiblePartIndex = i;
+ newOpacity = model.getPartOpacityByIndex(partIndex);
+
+ // 新しい不透明度を計算
+ newOpacity += deltaTimeSeconds / this._fadeTimeSeconds;
+
+ if (newOpacity > 1.0) {
+ newOpacity = 1.0;
+ }
+ }
+ }
+
+ if (visiblePartIndex < 0) {
+ visiblePartIndex = 0;
+ newOpacity = 1.0;
+ }
+
+ // 表示パーツ、非表示パーツの不透明度を設定する
+ for (let i: number = beginIndex; i < beginIndex + partGroupCount; ++i) {
+ const partsIndex: number = this._partGroups.at(i).partIndex;
+
+ // 表示パーツの設定
+ if (visiblePartIndex == i) {
+ model.setPartOpacityByIndex(partsIndex, newOpacity); // 先に設定
+ }
+ // 非表示パーツの設定
+ else {
+ let opacity: number = model.getPartOpacityByIndex(partsIndex);
+ let a1: number; // 計算によって求められる不透明度
+
+ if (newOpacity < phi) {
+ a1 = (newOpacity * (phi - 1)) / phi + 1.0; // (0,1),(phi,phi)を通る直線式
+ } else {
+ a1 = ((1 - newOpacity) * phi) / (1.0 - phi); // (1,0),(phi,phi)を通る直線式
+ }
+
+ // 背景の見える割合を制限する場合
+ const backOpacity: number = (1.0 - a1) * (1.0 - newOpacity);
+
+ if (backOpacity > backOpacityThreshold) {
+ a1 = 1.0 - backOpacityThreshold / (1.0 - newOpacity);
+ }
+
+ if (opacity > a1) {
+ opacity = a1; // 計算の不透明度よりも大きければ(濃ければ)不透明度を上げる
+ }
+
+ model.setPartOpacityByIndex(partsIndex, opacity);
+ }
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._fadeTimeSeconds = DefaultFadeInSeconds;
+ this._lastModel = null;
+ this._partGroups = new csmVector();
+ this._partGroupCounts = new csmVector();
+ }
+
+ _partGroups: csmVector; // パーツグループ
+ _partGroupCounts: csmVector; // それぞれのパーツグループの個数
+ _fadeTimeSeconds: number; // フェード時間[秒]
+ _lastModel: CubismModel; // 前回操作したモデル
+}
+
+/**
+ * パーツにまつわるデータを管理
+ */
+export class PartData {
+ /**
+ * コンストラクタ
+ */
+ constructor(v?: PartData) {
+ this.parameterIndex = 0;
+ this.partIndex = 0;
+ this.link = new csmVector();
+
+ if (v != undefined) {
+ this.partId = v.partId;
+
+ for (
+ const ite: iterator = v.link.begin();
+ ite.notEqual(v.link.end());
+ ite.preIncrement()
+ ) {
+ this.link.pushBack(ite.ptr().clone());
+ }
+ }
+ }
+
+ /**
+ * =演算子のオーバーロード
+ */
+ public assignment(v: PartData): PartData {
+ this.partId = v.partId;
+
+ for (
+ const ite: iterator = v.link.begin();
+ ite.notEqual(v.link.end());
+ ite.preIncrement()
+ ) {
+ this.link.pushBack(ite.ptr().clone());
+ }
+
+ return this;
+ }
+
+ /**
+ * 初期化
+ * @param model 初期化に使用するモデル
+ */
+ public initialize(model: CubismModel): void {
+ this.parameterIndex = model.getParameterIndex(this.partId);
+ this.partIndex = model.getPartIndex(this.partId);
+
+ model.setParameterValueByIndex(this.parameterIndex, 1);
+ }
+
+ /**
+ * オブジェクトのコピーを生成する
+ */
+ public clone(): PartData {
+ const clonePartData: PartData = new PartData();
+
+ clonePartData.partId = this.partId;
+ clonePartData.parameterIndex = this.parameterIndex;
+ clonePartData.partIndex = this.partIndex;
+ clonePartData.link = new csmVector();
+
+ for (
+ let ite: iterator = this.link.begin();
+ ite.notEqual(this.link.end());
+ ite.increment()
+ ) {
+ clonePartData.link.pushBack(ite.ptr().clone());
+ }
+
+ return clonePartData;
+ }
+
+ partId: CubismIdHandle; // パーツID
+ parameterIndex: number; // パラメータのインデックス
+ partIndex: number; // パーツのインデックス
+ link: csmVector; // 連動するパラメータ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismpose';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismPose = $.CubismPose;
+ export type CubismPose = $.CubismPose;
+ export const PartData = $.PartData;
+ export type PartData = $.PartData;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismallcator.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismallcator.ts
new file mode 100644
index 000000000..5e10ace9b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismallcator.ts
@@ -0,0 +1,51 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * メモリアロケーションを抽象化したクラス
+ *
+ * メモリ確保・解放処理をプラットフォーム側で実装して
+ * フレームワークから呼び出すためのインターフェース
+ */
+export abstract class ICubismAllocator {
+ /**
+ * アラインメント制約なしのヒープ・メモリーを確保します
+ *
+ * @param size 確保するバイト数
+ * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
+ */
+ public abstract allocate(size: number): any;
+
+ /**
+ * アラインメント制約なしのヒープ・メモリーを解放します。
+ *
+ * @param memory 解放するメモリのアドレス
+ */
+ public abstract deallocate(memory: any): void;
+
+ /**
+ * アラインメント制約有のヒープ・メモリーを確保します。
+ * @param size 確保するバイト数
+ * @param alignment メモリーブロックのアラインメント幅
+ * @return 成功すると割り当てられたメモリのアドレス。そうでなければ'0'を返す
+ */
+ public abstract allocateAligned(size: number, alignment: number): any;
+
+ /**
+ * アラインメント制約ありのヒープ・メモリーを解放します。
+ * @param alignedMemory 解放するメモリのアドレス
+ */
+ public abstract deallocateAligned(alignedMemory: any): void;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './icubismallcator';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const ICubismAllocator = $.ICubismAllocator;
+ export type ICubismAllocator = $.ICubismAllocator;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismmodelsetting.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismmodelsetting.ts
new file mode 100644
index 000000000..9c92e1c89
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/icubismmodelsetting.ts
@@ -0,0 +1,203 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from './id/cubismid';
+import { csmMap } from './type/csmmap';
+
+/**
+ * モデル設定情報を取り扱う関数を宣言した純粋仮想クラス。
+ *
+ * このクラスを継承することで、モデル設定情報を取り扱うクラスになる。
+ */
+export abstract class ICubismModelSetting {
+ /**
+ * Mocファイルの名前を取得する
+ * @return Mocファイルの名前
+ */
+ public abstract getModelFileName(): string;
+
+ /**
+ * モデルが使用するテクスチャの数を取得する
+ * テクスチャの数
+ */
+ public abstract getTextureCount(): number;
+
+ /**
+ * テクスチャが配置されたディレクトリの名前を取得する
+ * @return テクスチャが配置されたディレクトリの名前
+ */
+ public abstract getTextureDirectory(): string;
+
+ /**
+ * モデルが使用するテクスチャの名前を取得する
+ * @param index 配列のインデックス値
+ * @return テクスチャの名前
+ */
+ public abstract getTextureFileName(index: number): string;
+
+ /**
+ * モデルに設定された当たり判定の数を取得する
+ * @return モデルに設定された当たり判定の数
+ */
+ public abstract getHitAreasCount(): number;
+
+ /**
+ * 当たり判定に設定されたIDを取得する
+ *
+ * @param index 配列のindex
+ * @return 当たり判定に設定されたID
+ */
+ public abstract getHitAreaId(index: number): CubismIdHandle;
+
+ /**
+ * 当たり判定に設定された名前を取得する
+ * @param index 配列のインデックス値
+ * @return 当たり判定に設定された名前
+ */
+ public abstract getHitAreaName(index: number): string;
+
+ /**
+ * 物理演算設定ファイルの名前を取得する
+ * @return 物理演算設定ファイルの名前
+ */
+ public abstract getPhysicsFileName(): string;
+
+ /**
+ * パーツ切り替え設定ファイルの名前を取得する
+ * @return パーツ切り替え設定ファイルの名前
+ */
+ public abstract getPoseFileName(): string;
+
+ /**
+ * 表情設定ファイルの数を取得する
+ * @return 表情設定ファイルの数
+ */
+ public abstract getExpressionCount(): number;
+
+ /**
+ * 表情設定ファイルを識別する名前(別名)を取得する
+ * @param index 配列のインデックス値
+ * @return 表情の名前
+ */
+ public abstract getExpressionName(index: number): string;
+
+ /**
+ * 表情設定ファイルの名前を取得する
+ * @param index 配列のインデックス値
+ * @return 表情設定ファイルの名前
+ */
+ public abstract getExpressionFileName(index: number): string;
+
+ /**
+ * モーショングループの数を取得する
+ * @return モーショングループの数
+ */
+ public abstract getMotionGroupCount(): number;
+
+ /**
+ * モーショングループの名前を取得する
+ * @param index 配列のインデックス値
+ * @return モーショングループの名前
+ */
+ public abstract getMotionGroupName(index: number): string;
+
+ /**
+ * モーショングループに含まれるモーションの数を取得する
+ * @param groupName モーショングループの名前
+ * @return モーショングループの数
+ */
+ public abstract getMotionCount(groupName: string): number;
+
+ /**
+ * グループ名とインデックス値からモーションファイル名を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return モーションファイルの名前
+ */
+ public abstract getMotionFileName(groupName: string, index: number): string;
+
+ /**
+ * モーションに対応するサウンドファイルの名前を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return サウンドファイルの名前
+ */
+ public abstract getMotionSoundFileName(
+ groupName: string,
+ index: number
+ ): string;
+
+ /**
+ * モーション開始時のフェードイン処理時間を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return フェードイン処理時間[秒]
+ */
+ public abstract getMotionFadeInTimeValue(
+ groupName: string,
+ index: number
+ ): number;
+
+ /**
+ * モーション終了時のフェードアウト処理時間を取得する
+ * @param groupName モーショングループの名前
+ * @param index 配列のインデックス値
+ * @return フェードアウト処理時間[秒]
+ */
+ public abstract getMotionFadeOutTimeValue(
+ groupName: string,
+ index: number
+ ): number;
+
+ /**
+ * ユーザーデータのファイル名を取得する
+ * @return ユーザーデータのファイル名
+ */
+ public abstract getUserDataFile(): string;
+
+ /**
+ * レイアウト情報を取得する
+ * @param outLayoutMap csmMapクラスのインスタンス
+ * @return true レイアウト情報が存在する
+ * @return false レイアウト情報が存在しない
+ */
+ public abstract getLayoutMap(outLayoutMap: csmMap): boolean;
+
+ /**
+ * 目パチに関連付けられたパラメータの数を取得する
+ * @return 目パチに関連付けられたパラメータの数
+ */
+ public abstract getEyeBlinkParameterCount(): number;
+
+ /**
+ * 目パチに関連付けられたパラメータのIDを取得する
+ * @param index 配列のインデックス値
+ * @return パラメータID
+ */
+ public abstract getEyeBlinkParameterId(index: number): CubismIdHandle;
+
+ /**
+ * リップシンクに関連付けられたパラメータの数を取得する
+ * @return リップシンクに関連付けられたパラメータの数
+ */
+ public abstract getLipSyncParameterCount(): number;
+
+ /**
+ * リップシンクに関連付けられたパラメータの数を取得する
+ * @param index 配列のインデックス値
+ * @return パラメータID
+ */
+ public abstract getLipSyncParameterId(index: number): CubismIdHandle;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './icubismmodelsetting';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const ICubismModelSetting = $.ICubismModelSetting;
+ export type ICubismModelSetting = $.ICubismModelSetting;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismid.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismid.ts
new file mode 100644
index 000000000..2d72b0c32
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismid.ts
@@ -0,0 +1,79 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { csmString } from '../type/csmstring';
+
+/**
+ * パラメータ名・パーツ名・Drawable名を保持
+ *
+ * パラメータ名・パーツ名・Drawable名を保持するクラス。
+ */
+export class CubismId {
+ /**
+ * ID名を取得する
+ */
+ public getString(): csmString {
+ return this._id;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor(id: string | csmString) {
+ if (typeof id === 'string') {
+ this._id = new csmString(id);
+ return;
+ }
+
+ this._id = id;
+ }
+
+ /**
+ * idを比較
+ * @param c 比較するid
+ * @return 同じならばtrue,異なっていればfalseを返す
+ */
+ public isEqual(c: string | csmString | CubismId): boolean {
+ if (typeof c === 'string') {
+ return this._id.isEqual(c);
+ } else if (c instanceof csmString) {
+ return this._id.isEqual(c.s);
+ } else if (c instanceof CubismId) {
+ return this._id.isEqual(c._id.s);
+ }
+ return false;
+ }
+
+ /**
+ * idを比較
+ * @param c 比較するid
+ * @return 同じならばtrue,異なっていればfalseを返す
+ */
+ public isNotEqual(c: string | csmString | CubismId): boolean {
+ if (typeof c == 'string') {
+ return !this._id.isEqual(c);
+ } else if (c instanceof csmString) {
+ return !this._id.isEqual(c.s);
+ } else if (c instanceof CubismId) {
+ return !this._id.isEqual(c._id.s);
+ }
+ return false;
+ }
+
+ private _id: csmString; // ID名
+}
+
+export declare type CubismIdHandle = CubismId;
+
+// Namespace definition for compatibility.
+import * as $ from './cubismid';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismId = $.CubismId;
+ export type CubismId = $.CubismId;
+ export type CubismIdHandle = $.CubismIdHandle;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismidmanager.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismidmanager.ts
new file mode 100644
index 000000000..4f8ecdee8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/id/cubismidmanager.ts
@@ -0,0 +1,121 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { csmString } from '../type/csmstring';
+import { csmVector } from '../type/csmvector';
+import { CubismId } from './cubismid';
+
+/**
+ * ID名の管理
+ *
+ * ID名を管理する。
+ */
+export class CubismIdManager {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._ids = new csmVector();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ for (let i = 0; i < this._ids.getSize(); ++i) {
+ this._ids.set(i, void 0);
+ }
+ this._ids = null;
+ }
+
+ /**
+ * ID名をリストから登録
+ *
+ * @param ids ID名リスト
+ * @param count IDの個数
+ */
+ public registerIds(ids: string[] | csmString[]): void {
+ for (let i = 0; i < ids.length; i++) {
+ this.registerId(ids[i]);
+ }
+ }
+
+ /**
+ * ID名を登録
+ *
+ * @param id ID名
+ */
+ public registerId(id: string | csmString): CubismId {
+ let result: CubismId = null;
+
+ if ('string' == typeof id) {
+ if ((result = this.findId(id)) != null) {
+ return result;
+ }
+
+ result = new CubismId(id);
+ this._ids.pushBack(result);
+ } else {
+ return this.registerId(id.s);
+ }
+
+ return result;
+ }
+
+ /**
+ * ID名からIDを取得する
+ *
+ * @param id ID名
+ */
+ public getId(id: csmString | string): CubismId {
+ return this.registerId(id);
+ }
+
+ /**
+ * ID名からIDの確認
+ *
+ * @return true 存在する
+ * @return false 存在しない
+ */
+ public isExist(id: csmString | string): boolean {
+ if ('string' == typeof id) {
+ return this.findId(id) != null;
+ }
+ return this.isExist(id.s);
+ }
+
+ /**
+ * ID名からIDを検索する。
+ *
+ * @param id ID名
+ * @return 登録されているID。なければNULL。
+ */
+ private findId(id: string): CubismId {
+ for (let i = 0; i < this._ids.getSize(); ++i) {
+ if (
+ this._ids
+ .at(i)
+ .getString()
+ .isEqual(id)
+ ) {
+ return this._ids.at(i);
+ }
+ }
+
+ return null;
+ }
+
+ private _ids: csmVector; // 登録されているIDのリスト
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismidmanager';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismIdManager = $.CubismIdManager;
+ export type CubismIdManager = $.CubismIdManager;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/live2dcubismframework.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/live2dcubismframework.ts
new file mode 100644
index 000000000..17dcefb3e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/live2dcubismframework.ts
@@ -0,0 +1,277 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdManager } from './id/cubismidmanager';
+import { CubismRenderer } from './rendering/cubismrenderer';
+import {
+ CSM_ASSERT,
+ CubismLogInfo,
+ CubismLogWarning
+} from './utils/cubismdebug';
+import { Value } from './utils/cubismjson';
+
+export function strtod(s: string, endPtr: string[]): number {
+ let index = 0;
+ for (let i = 1; ; i++) {
+ const testC: string = s.slice(i - 1, i);
+
+ // 指数・マイナスの可能性があるのでスキップする
+ if (testC == 'e' || testC == '-' || testC == 'E') {
+ continue;
+ } // 文字列の範囲を広げていく
+
+ const test: string = s.substring(0, i);
+ const number = Number(test);
+ if (isNaN(number)) {
+ // 数値として認識できなくなったので終了
+ break;
+ } // 最後に数値としてできたindexを格納しておく
+
+ index = i;
+ }
+ let d = parseFloat(s); // パースした数値
+
+ if (isNaN(d)) {
+ // 数値として認識できなくなったので終了
+ d = NaN;
+ }
+
+ endPtr[0] = s.slice(index); // 後続の文字列
+ return d;
+}
+
+// ファイルスコープの変数を初期化
+
+let s_isStarted = false;
+let s_isInitialized = false;
+let s_option: Option = null;
+let s_cubismIdManager: CubismIdManager = null;
+
+/**
+ * Framework内で使う定数の宣言
+ */
+export const Constant = Object.freeze>({
+ vertexOffset: 0, // メッシュ頂点のオフセット値
+ vertexStep: 2 // メッシュ頂点のステップ値
+});
+
+export function csmDelete(address: T): void {
+ if (!address) {
+ return;
+ }
+
+ address = void 0;
+}
+
+/**
+ * Live2D Cubism SDK Original Workflow SDKのエントリポイント
+ * 利用開始時はCubismFramework.initialize()を呼び、CubismFramework.dispose()で終了する。
+ */
+export class CubismFramework {
+ /**
+ * Cubism FrameworkのAPIを使用可能にする。
+ * APIを実行する前に必ずこの関数を実行すること。
+ * 一度準備が完了して以降は、再び実行しても内部処理がスキップされます。
+ *
+ * @param option Optionクラスのインスタンス
+ *
+ * @return 準備処理が完了したらtrueが返ります。
+ */
+ public static startUp(option: Option = null): boolean {
+ if (s_isStarted) {
+ CubismLogInfo('CubismFramework.startUp() is already done.');
+ return s_isStarted;
+ }
+
+ s_option = option;
+
+ if (s_option != null) {
+ Live2DCubismCore.Logging.csmSetLogFunction(s_option.logFunction);
+ }
+
+ s_isStarted = true;
+
+ // Live2D Cubism Coreバージョン情報を表示
+ if (s_isStarted) {
+ const version: number = Live2DCubismCore.Version.csmGetVersion();
+ const major: number = (version & 0xff000000) >> 24;
+ const minor: number = (version & 0x00ff0000) >> 16;
+ const patch: number = version & 0x0000ffff;
+ const versionNumber: number = version;
+
+ CubismLogInfo(
+ `Live2D Cubism Core version: {0}.{1}.{2} ({3})`,
+ ('00' + major).slice(-2),
+ ('00' + minor).slice(-2),
+ ('0000' + patch).slice(-4),
+ versionNumber
+ );
+ }
+
+ CubismLogInfo('CubismFramework.startUp() is complete.');
+
+ return s_isStarted;
+ }
+
+ /**
+ * StartUp()で初期化したCubismFrameworkの各パラメータをクリアします。
+ * Dispose()したCubismFrameworkを再利用する際に利用してください。
+ */
+ public static cleanUp(): void {
+ s_isStarted = false;
+ s_isInitialized = false;
+ s_option = null;
+ s_cubismIdManager = null;
+ }
+
+ /**
+ * Cubism Framework内のリソースを初期化してモデルを表示可能な状態にします。
+ * 再度Initialize()するには先にDispose()を実行する必要があります。
+ */
+ public static initialize(): void {
+ CSM_ASSERT(s_isStarted);
+ if (!s_isStarted) {
+ CubismLogWarning('CubismFramework is not started.');
+ return;
+ }
+
+ // --- s_isInitializedによる連続初期化ガード ---
+ // 連続してリソース確保が行われないようにする。
+ // 再度Initialize()するには先にDispose()を実行する必要がある。
+ if (s_isInitialized) {
+ CubismLogWarning(
+ 'CubismFramework.initialize() skipped, already initialized.'
+ );
+ return;
+ }
+
+ //---- static 初期化 ----
+ Value.staticInitializeNotForClientCall();
+
+ s_cubismIdManager = new CubismIdManager();
+
+ s_isInitialized = true;
+
+ CubismLogInfo('CubismFramework.initialize() is complete.');
+ }
+
+ /**
+ * Cubism Framework内の全てのリソースを解放します。
+ * ただし、外部で確保されたリソースについては解放しません。
+ * 外部で適切に破棄する必要があります。
+ */
+ public static dispose(): void {
+ CSM_ASSERT(s_isStarted);
+ if (!s_isStarted) {
+ CubismLogWarning('CubismFramework is not started.');
+ return;
+ }
+
+ // --- s_isInitializedによる未初期化解放ガード ---
+ // dispose()するには先にinitialize()を実行する必要がある。
+ if (!s_isInitialized) {
+ // false...リソース未確保の場合
+ CubismLogWarning('CubismFramework.dispose() skipped, not initialized.');
+ return;
+ }
+
+ Value.staticReleaseNotForClientCall();
+
+ s_cubismIdManager.release();
+ s_cubismIdManager = null;
+
+ // レンダラの静的リソース(シェーダプログラム他)を解放する
+ CubismRenderer.staticRelease();
+
+ s_isInitialized = false;
+
+ CubismLogInfo('CubismFramework.dispose() is complete.');
+ }
+
+ /**
+ * Cubism FrameworkのAPIを使用する準備が完了したかどうか
+ * @return APIを使用する準備が完了していればtrueが返ります。
+ */
+ public static isStarted(): boolean {
+ return s_isStarted;
+ }
+
+ /**
+ * Cubism Frameworkのリソース初期化がすでに行われているかどうか
+ * @return リソース確保が完了していればtrueが返ります
+ */
+ public static isInitialized(): boolean {
+ return s_isInitialized;
+ }
+
+ /**
+ * Core APIにバインドしたログ関数を実行する
+ *
+ * @praram message ログメッセージ
+ */
+ public static coreLogFunction(message: string): void {
+ // Return if logging not possible.
+ if (!Live2DCubismCore.Logging.csmGetLogFunction()) {
+ return;
+ }
+
+ Live2DCubismCore.Logging.csmGetLogFunction()(message);
+ }
+
+ /**
+ * 現在のログ出力レベル設定の値を返す。
+ *
+ * @return 現在のログ出力レベル設定の値
+ */
+ public static getLoggingLevel(): LogLevel {
+ if (s_option != null) {
+ return s_option.loggingLevel;
+ }
+ return LogLevel.LogLevel_Off;
+ }
+
+ /**
+ * IDマネージャのインスタンスを取得する
+ * @return CubismManagerクラスのインスタンス
+ */
+ public static getIdManager(): CubismIdManager {
+ return s_cubismIdManager;
+ }
+
+ /**
+ * 静的クラスとして使用する
+ * インスタンス化させない
+ */
+ private constructor() {}
+}
+
+export class Option {
+ logFunction: Live2DCubismCore.csmLogFunction; // ログ出力の関数オブジェクト
+ loggingLevel: LogLevel; // ログ出力レベルの設定
+}
+
+/**
+ * ログ出力のレベル
+ */
+export enum LogLevel {
+ LogLevel_Verbose = 0, // 詳細ログ
+ LogLevel_Debug, // デバッグログ
+ LogLevel_Info, // Infoログ
+ LogLevel_Warning, // 警告ログ
+ LogLevel_Error, // エラーログ
+ LogLevel_Off // ログ出力無効
+}
+
+// Namespace definition for compatibility.
+import * as $ from './live2dcubismframework';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const Constant = $.Constant;
+ export const csmDelete = $.csmDelete;
+ export const CubismFramework = $.CubismFramework;
+ export type CubismFramework = $.CubismFramework;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmath.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmath.ts
new file mode 100644
index 000000000..fef3b6b54
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmath.ts
@@ -0,0 +1,334 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismVector2 } from './cubismvector2';
+
+/**
+ * 数値計算などに使用するユーティリティクラス
+ */
+export class CubismMath {
+ static readonly Epsilon: number = 0.00001;
+
+ /**
+ * 第一引数の値を最小値と最大値の範囲に収めた値を返す
+ *
+ * @param value 収められる値
+ * @param min 範囲の最小値
+ * @param max 範囲の最大値
+ * @return 最小値と最大値の範囲に収めた値
+ */
+ static range(value: number, min: number, max: number): number {
+ if (value < min) {
+ value = min;
+ } else if (value > max) {
+ value = max;
+ }
+
+ return value;
+ }
+
+ /**
+ * サイン関数の値を求める
+ *
+ * @param x 角度値(ラジアン)
+ * @return サイン関数sin(x)の値
+ */
+ static sin(x: number): number {
+ return Math.sin(x);
+ }
+
+ /**
+ * コサイン関数の値を求める
+ *
+ * @param x 角度値(ラジアン)
+ * @return コサイン関数cos(x)の値
+ */
+ static cos(x: number): number {
+ return Math.cos(x);
+ }
+
+ /**
+ * 値の絶対値を求める
+ *
+ * @param x 絶対値を求める値
+ * @return 値の絶対値
+ */
+ static abs(x: number): number {
+ return Math.abs(x);
+ }
+
+ /**
+ * 平方根(ルート)を求める
+ * @param x -> 平方根を求める値
+ * @return 値の平方根
+ */
+ static sqrt(x: number): number {
+ return Math.sqrt(x);
+ }
+
+ /**
+ * 立方根を求める
+ * @param x -> 立方根を求める値
+ * @return 値の立方根
+ */
+ static cbrt(x: number): number {
+ if (x === 0) {
+ return x;
+ }
+
+ let cx: number = x;
+ const isNegativeNumber: boolean = cx < 0;
+
+ if (isNegativeNumber) {
+ cx = -cx;
+ }
+
+ let ret: number;
+ if (cx === Infinity) {
+ ret = Infinity;
+ } else {
+ ret = Math.exp(Math.log(cx) / 3);
+ ret = (cx / (ret * ret) + 2 * ret) / 3;
+ }
+ return isNegativeNumber ? -ret : ret;
+ }
+
+ /**
+ * イージング処理されたサインを求める
+ * フェードイン・アウト時のイージングに利用できる
+ *
+ * @param value イージングを行う値
+ * @return イージング処理されたサイン値
+ */
+ static getEasingSine(value: number): number {
+ if (value < 0.0) {
+ return 0.0;
+ } else if (value > 1.0) {
+ return 1.0;
+ }
+
+ return 0.5 - 0.5 * this.cos(value * Math.PI);
+ }
+
+ /**
+ * 大きい方の値を返す
+ *
+ * @param left 左辺の値
+ * @param right 右辺の値
+ * @return 大きい方の値
+ */
+ static max(left: number, right: number): number {
+ return left > right ? left : right;
+ }
+
+ /**
+ * 小さい方の値を返す
+ *
+ * @param left 左辺の値
+ * @param right 右辺の値
+ * @return 小さい方の値
+ */
+ static min(left: number, right: number): number {
+ return left > right ? right : left;
+ }
+
+ /**
+ * 角度値をラジアン値に変換する
+ *
+ * @param degrees 角度値
+ * @return 角度値から変換したラジアン値
+ */
+ static degreesToRadian(degrees: number): number {
+ return (degrees / 180.0) * Math.PI;
+ }
+
+ /**
+ * ラジアン値を角度値に変換する
+ *
+ * @param radian ラジアン値
+ * @return ラジアン値から変換した角度値
+ */
+ static radianToDegrees(radian: number): number {
+ return (radian * 180.0) / Math.PI;
+ }
+
+ /**
+ * 2つのベクトルからラジアン値を求める
+ *
+ * @param from 始点ベクトル
+ * @param to 終点ベクトル
+ * @return ラジアン値から求めた方向ベクトル
+ */
+ static directionToRadian(from: CubismVector2, to: CubismVector2): number {
+ const q1: number = Math.atan2(to.y, to.x);
+ const q2: number = Math.atan2(from.y, from.x);
+
+ let ret: number = q1 - q2;
+
+ while (ret < -Math.PI) {
+ ret += Math.PI * 2.0;
+ }
+
+ while (ret > Math.PI) {
+ ret -= Math.PI * 2.0;
+ }
+
+ return ret;
+ }
+
+ /**
+ * 2つのベクトルから角度値を求める
+ *
+ * @param from 始点ベクトル
+ * @param to 終点ベクトル
+ * @return 角度値から求めた方向ベクトル
+ */
+ static directionToDegrees(from: CubismVector2, to: CubismVector2): number {
+ const radian: number = this.directionToRadian(from, to);
+ let degree: number = this.radianToDegrees(radian);
+
+ if (to.x - from.x > 0.0) {
+ degree = -degree;
+ }
+
+ return degree;
+ }
+
+ /**
+ * ラジアン値を方向ベクトルに変換する。
+ *
+ * @param totalAngle ラジアン値
+ * @return ラジアン値から変換した方向ベクトル
+ */
+
+ static radianToDirection(totalAngle: number): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2();
+
+ ret.x = this.sin(totalAngle);
+ ret.y = this.cos(totalAngle);
+
+ return ret;
+ }
+
+ /**
+ * 三次方程式の三次項の係数が0になったときに補欠的に二次方程式の解をもとめる。
+ * a * x^2 + b * x + c = 0
+ *
+ * @param a -> 二次項の係数値
+ * @param b -> 一次項の係数値
+ * @param c -> 定数項の値
+ * @return 二次方程式の解
+ */
+ static quadraticEquation(a: number, b: number, c: number): number {
+ if (this.abs(a) < CubismMath.Epsilon) {
+ if (this.abs(b) < CubismMath.Epsilon) {
+ return -c;
+ }
+ return -c / b;
+ }
+
+ return -(b + this.sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
+ }
+
+ /**
+ * カルダノの公式によってベジェのt値に該当する3次方程式の解を求める。
+ * 重解になったときには0.0~1.0の値になる解を返す。
+ *
+ * a * x^3 + b * x^2 + c * x + d = 0
+ *
+ * @param a -> 三次項の係数値
+ * @param b -> 二次項の係数値
+ * @param c -> 一次項の係数値
+ * @param d -> 定数項の値
+ * @return 0.0~1.0の間にある解
+ */
+ static cardanoAlgorithmForBezier(
+ a: number,
+ b: number,
+ c: number,
+ d: number
+ ): number {
+ if (this.sqrt(a) < CubismMath.Epsilon) {
+ return this.range(this.quadraticEquation(b, c, d), 0.0, 1.0);
+ }
+
+ const ba: number = b / a;
+ const ca: number = c / a;
+ const da: number = d / a;
+
+ const p: number = (3.0 * ca - ba * ba) / 3.0;
+ const p3: number = p / 3.0;
+ const q: number = (2.0 * ba * ba * ba - 9.0 * ba * ca + 27.0 * da) / 27.0;
+ const q2: number = q / 2.0;
+ const discriminant: number = q2 * q2 + p3 * p3 * p3;
+
+ const center = 0.5;
+ const threshold: number = center + 0.01;
+
+ if (discriminant < 0.0) {
+ const mp3: number = -p / 3.0;
+ const mp33: number = mp3 * mp3 * mp3;
+ const r: number = this.sqrt(mp33);
+ const t: number = -q / (2.0 * r);
+ const cosphi: number = this.range(t, -1.0, 1.0);
+ const phi: number = Math.acos(cosphi);
+ const crtr: number = this.cbrt(r);
+ const t1: number = 2.0 * crtr;
+
+ const root1: number = t1 * this.cos(phi / 3.0) - ba / 3.0;
+ if (this.abs(root1 - center) < threshold) {
+ return this.range(root1, 0.0, 1.0);
+ }
+
+ const root2: number =
+ t1 * this.cos((phi + 2.0 * Math.PI) / 3.0) - ba / 3.0;
+ if (this.abs(root2 - center) < threshold) {
+ return this.range(root2, 0.0, 1.0);
+ }
+
+ const root3: number =
+ t1 * this.cos((phi + 4.0 * Math.PI) / 3.0) - ba / 3.0;
+ return this.range(root3, 0.0, 1.0);
+ }
+
+ if (discriminant == 0.0) {
+ let u1: number;
+ if (q2 < 0.0) {
+ u1 = this.cbrt(-q2);
+ } else {
+ u1 = -this.cbrt(q2);
+ }
+
+ const root1: number = 2.0 * u1 - ba / 3.0;
+ if (this.abs(root1 - center) < threshold) {
+ return this.range(root1, 0.0, 1.0);
+ }
+
+ const root2: number = -u1 - ba / 3.0;
+ return this.range(root2, 0.0, 1.0);
+ }
+
+ const sd: number = this.sqrt(discriminant);
+ const u1: number = this.cbrt(sd - q2);
+ const v1: number = this.cbrt(sd + q2);
+ const root1: number = u1 - v1 - ba / 3.0;
+ return this.range(root1, 0.0, 1.0);
+ }
+
+ /**
+ * コンストラクタ
+ */
+ private constructor() {}
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmath';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMath = $.CubismMath;
+ export type CubismMath = $.CubismMath;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmatrix44.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmatrix44.ts
new file mode 100644
index 000000000..eba595bde
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmatrix44.ts
@@ -0,0 +1,314 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * 4x4の行列
+ *
+ * 4x4行列の便利クラス。
+ */
+export class CubismMatrix44 {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._tr = new Float32Array(16); // 4 * 4のサイズ
+ this.loadIdentity();
+ }
+
+ /**
+ * 受け取った2つの行列の乗算を行う。
+ *
+ * @param a 行列a
+ * @param b 行列b
+ * @return 乗算結果の行列
+ */
+ public static multiply(
+ a: Float32Array,
+ b: Float32Array,
+ dst: Float32Array
+ ): void {
+ const c: Float32Array = new Float32Array([
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0
+ ]);
+
+ const n = 4;
+
+ for (let i = 0; i < n; ++i) {
+ for (let j = 0; j < n; ++j) {
+ for (let k = 0; k < n; ++k) {
+ c[j + i * 4] += a[k + i * 4] * b[j + k * 4];
+ }
+ }
+ }
+
+ for (let i = 0; i < 16; ++i) {
+ dst[i] = c[i];
+ }
+ }
+
+ /**
+ * 単位行列に初期化する
+ */
+ public loadIdentity(): void {
+ const c: Float32Array = new Float32Array([
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]);
+
+ this.setMatrix(c);
+ }
+
+ /**
+ * 行列を設定
+ *
+ * @param tr 16個の浮動小数点数で表される4x4の行列
+ */
+ public setMatrix(tr: Float32Array): void {
+ for (let i = 0; i < 16; ++i) {
+ this._tr[i] = tr[i];
+ }
+ }
+
+ /**
+ * 行列を浮動小数点数の配列で取得
+ *
+ * @return 16個の浮動小数点数で表される4x4の行列
+ */
+ public getArray(): Float32Array {
+ return this._tr;
+ }
+
+ /**
+ * X軸の拡大率を取得
+ * @return X軸の拡大率
+ */
+ public getScaleX(): number {
+ return this._tr[0];
+ }
+
+ /**
+ * Y軸の拡大率を取得する
+ *
+ * @return Y軸の拡大率
+ */
+ public getScaleY(): number {
+ return this._tr[5];
+ }
+
+ /**
+ * X軸の移動量を取得
+ * @return X軸の移動量
+ */
+ public getTranslateX(): number {
+ return this._tr[12];
+ }
+
+ /**
+ * Y軸の移動量を取得
+ * @return Y軸の移動量
+ */
+ public getTranslateY(): number {
+ return this._tr[13];
+ }
+
+ /**
+ * X軸の値を現在の行列で計算
+ *
+ * @param src X軸の値
+ * @return 現在の行列で計算されたX軸の値
+ */
+ public transformX(src: number): number {
+ return this._tr[0] * src + this._tr[12];
+ }
+
+ /**
+ * Y軸の値を現在の行列で計算
+ *
+ * @param src Y軸の値
+ * @return 現在の行列で計算されたY軸の値
+ */
+ public transformY(src: number): number {
+ return this._tr[5] * src + this._tr[13];
+ }
+
+ /**
+ * X軸の値を現在の行列で逆計算
+ */
+ public invertTransformX(src: number): number {
+ return (src - this._tr[12]) / this._tr[0];
+ }
+
+ /**
+ * Y軸の値を現在の行列で逆計算
+ */
+ public invertTransformY(src: number): number {
+ return (src - this._tr[13]) / this._tr[5];
+ }
+
+ /**
+ * 現在の行列の位置を起点にして移動
+ *
+ * 現在の行列の位置を起点にして相対的に移動する。
+ *
+ * @param x X軸の移動量
+ * @param y Y軸の移動量
+ */
+ public translateRelative(x: number, y: number): void {
+ const tr1: Float32Array = new Float32Array([
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ x,
+ y,
+ 0.0,
+ 1.0
+ ]);
+
+ CubismMatrix44.multiply(tr1, this._tr, this._tr);
+ }
+
+ /**
+ * 現在の行列の位置を移動
+ *
+ * 現在の行列の位置を指定した位置へ移動する
+ *
+ * @param x X軸の移動量
+ * @param y y軸の移動量
+ */
+ public translate(x: number, y: number): void {
+ this._tr[12] = x;
+ this._tr[13] = y;
+ }
+
+ /**
+ * 現在の行列のX軸の位置を指定した位置へ移動する
+ *
+ * @param x X軸の移動量
+ */
+ public translateX(x: number): void {
+ this._tr[12] = x;
+ }
+
+ /**
+ * 現在の行列のY軸の位置を指定した位置へ移動する
+ *
+ * @param y Y軸の移動量
+ */
+ public translateY(y: number): void {
+ this._tr[13] = y;
+ }
+
+ /**
+ * 現在の行列の拡大率を相対的に設定する
+ *
+ * @param x X軸の拡大率
+ * @param y Y軸の拡大率
+ */
+ public scaleRelative(x: number, y: number): void {
+ const tr1: Float32Array = new Float32Array([
+ x,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ y,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]);
+
+ CubismMatrix44.multiply(tr1, this._tr, this._tr);
+ }
+
+ /**
+ * 現在の行列の拡大率を指定した倍率に設定する
+ *
+ * @param x X軸の拡大率
+ * @param y Y軸の拡大率
+ */
+ public scale(x: number, y: number): void {
+ this._tr[0] = x;
+ this._tr[5] = y;
+ }
+
+ /**
+ * 現在の行列に行列を乗算
+ *
+ * @param m 行列
+ */
+ public multiplyByMatrix(m: CubismMatrix44): void {
+ CubismMatrix44.multiply(m.getArray(), this._tr, this._tr);
+ }
+
+ /**
+ * オブジェクトのコピーを生成する
+ */
+ public clone(): CubismMatrix44 {
+ const cloneMatrix: CubismMatrix44 = new CubismMatrix44();
+
+ for (let i = 0; i < this._tr.length; i++) {
+ cloneMatrix._tr[i] = this._tr[i];
+ }
+
+ return cloneMatrix;
+ }
+
+ protected _tr: Float32Array; // 4x4行列データ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmatrix44';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMatrix44 = $.CubismMatrix44;
+ export type CubismMatrix44 = $.CubismMatrix44;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmodelmatrix.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmodelmatrix.ts
new file mode 100644
index 000000000..a17167cc3
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismmodelmatrix.ts
@@ -0,0 +1,226 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { csmMap, iterator } from '../type/csmmap';
+import { CubismMatrix44 } from './cubismmatrix44';
+
+/**
+ * モデル座標設定用の4x4行列
+ *
+ * モデル座標設定用の4x4行列クラス
+ */
+export class CubismModelMatrix extends CubismMatrix44 {
+ /**
+ * コンストラクタ
+ *
+ * @param w 横幅
+ * @param h 縦幅
+ */
+ constructor(w?: number, h?: number) {
+ super();
+
+ this._width = w !== undefined ? w : 0.0;
+ this._height = h !== undefined ? h : 0.0;
+
+ this.setHeight(2.0);
+ }
+
+ /**
+ * 横幅を設定
+ *
+ * @param w 横幅
+ */
+ public setWidth(w: number): void {
+ const scaleX: number = w / this._width;
+ const scaleY: number = scaleX;
+ this.scale(scaleX, scaleY);
+ }
+
+ /**
+ * 縦幅を設定
+ * @param h 縦幅
+ */
+ public setHeight(h: number): void {
+ const scaleX: number = h / this._height;
+ const scaleY: number = scaleX;
+ this.scale(scaleX, scaleY);
+ }
+
+ /**
+ * 位置を設定
+ *
+ * @param x X軸の位置
+ * @param y Y軸の位置
+ */
+ public setPosition(x: number, y: number): void {
+ this.translate(x, y);
+ }
+
+ /**
+ * 中心位置を設定
+ *
+ * @param x X軸の中心位置
+ * @param y Y軸の中心位置
+ *
+ * @note widthかheightを設定したあとでないと、拡大率が正しく取得できないためずれる。
+ */
+ public setCenterPosition(x: number, y: number) {
+ this.centerX(x);
+ this.centerY(y);
+ }
+
+ /**
+ * 上辺の位置を設定する
+ *
+ * @param y 上辺のY軸位置
+ */
+ public top(y: number): void {
+ this.setY(y);
+ }
+
+ /**
+ * 下辺の位置を設定する
+ *
+ * @param y 下辺のY軸位置
+ */
+ public bottom(y: number) {
+ const h: number = this._height * this.getScaleY();
+
+ this.translateY(y - h);
+ }
+
+ /**
+ * 左辺の位置を設定
+ *
+ * @param x 左辺のX軸位置
+ */
+ public left(x: number): void {
+ this.setX(x);
+ }
+
+ /**
+ * 右辺の位置を設定
+ *
+ * @param x 右辺のX軸位置
+ */
+ public right(x: number): void {
+ const w = this._width * this.getScaleX();
+
+ this.translateX(x - w);
+ }
+
+ /**
+ * X軸の中心位置を設定
+ *
+ * @param x X軸の中心位置
+ */
+ public centerX(x: number): void {
+ const w = this._width * this.getScaleX();
+
+ this.translateX(x - w / 2.0);
+ }
+
+ /**
+ * X軸の位置を設定
+ *
+ * @param x X軸の位置
+ */
+ public setX(x: number): void {
+ this.translateX(x);
+ }
+
+ /**
+ * Y軸の中心位置を設定
+ *
+ * @param y Y軸の中心位置
+ */
+ public centerY(y: number): void {
+ const h: number = this._height * this.getScaleY();
+
+ this.translateY(y - h / 2.0);
+ }
+
+ /**
+ * Y軸の位置を設定する
+ *
+ * @param y Y軸の位置
+ */
+ public setY(y: number): void {
+ this.translateY(y);
+ }
+
+ /**
+ * レイアウト情報から位置を設定
+ *
+ * @param layout レイアウト情報
+ */
+ public setupFromLayout(layout: csmMap): void {
+ const keyWidth = 'width';
+ const keyHeight = 'height';
+ const keyX = 'x';
+ const keyY = 'y';
+ const keyCenterX = 'center_x';
+ const keyCenterY = 'center_y';
+ const keyTop = 'top';
+ const keyBottom = 'bottom';
+ const keyLeft = 'left';
+ const keyRight = 'right';
+
+ for (
+ const ite: iterator = layout.begin();
+ ite.notEqual(layout.end());
+ ite.preIncrement()
+ ) {
+ const key: string = ite.ptr().first;
+ const value: number = ite.ptr().second;
+
+ if (key == keyWidth) {
+ this.setWidth(value);
+ } else if (key == keyHeight) {
+ this.setHeight(value);
+ }
+ }
+
+ for (
+ const ite: iterator = layout.begin();
+ ite.notEqual(layout.end());
+ ite.preIncrement()
+ ) {
+ const key: string = ite.ptr().first;
+ const value: number = ite.ptr().second;
+
+ if (key == keyX) {
+ this.setX(value);
+ } else if (key == keyY) {
+ this.setY(value);
+ } else if (key == keyCenterX) {
+ this.centerX(value);
+ } else if (key == keyCenterY) {
+ this.centerY(value);
+ } else if (key == keyTop) {
+ this.top(value);
+ } else if (key == keyBottom) {
+ this.bottom(value);
+ } else if (key == keyLeft) {
+ this.left(value);
+ } else if (key == keyRight) {
+ this.right(value);
+ }
+ }
+ }
+
+ private _width: number; // 横幅
+ private _height: number; // 縦幅
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmodelmatrix';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismModelMatrix = $.CubismModelMatrix;
+ export type CubismModelMatrix = $.CubismModelMatrix;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismtargetpoint.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismtargetpoint.ts
new file mode 100644
index 000000000..9ee2e5947
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismtargetpoint.ts
@@ -0,0 +1,169 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismMath } from './cubismmath';
+
+const FrameRate = 30;
+const Epsilon = 0.01;
+
+/**
+ * 顔の向きの制御機能
+ *
+ * 顔の向きの制御機能を提供するクラス。
+ */
+export class CubismTargetPoint {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._faceTargetX = 0.0;
+ this._faceTargetY = 0.0;
+ this._faceX = 0.0;
+ this._faceY = 0.0;
+ this._faceVX = 0.0;
+ this._faceVY = 0.0;
+ this._lastTimeSeconds = 0.0;
+ this._userTimeSeconds = 0.0;
+ }
+
+ /**
+ * 更新処理
+ */
+ public update(deltaTimeSeconds: number): void {
+ // デルタ時間を加算する
+ this._userTimeSeconds += deltaTimeSeconds;
+
+ // 首を中央から左右に振るときの平均的な速さは 秒速度。加速・減速を考慮して、その2倍を最高速度とする
+ // 顔の振り具合を、中央(0.0)から、左右は(+-1.0)とする
+ const faceParamMaxV: number = 40.0 / 10.0; // 7.5秒間に40分移動(5.3/sc)
+ const maxV: number = (faceParamMaxV * 1.0) / FrameRate; // 1frameあたりに変化できる速度の上限
+
+ if (this._lastTimeSeconds == 0.0) {
+ this._lastTimeSeconds = this._userTimeSeconds;
+ return;
+ }
+
+ const deltaTimeWeight: number =
+ (this._userTimeSeconds - this._lastTimeSeconds) * FrameRate;
+ this._lastTimeSeconds = this._userTimeSeconds;
+
+ // 最高速度になるまでの時間を
+ const timeToMaxSpeed = 0.15;
+ const frameToMaxSpeed: number = timeToMaxSpeed * FrameRate; // sec * frame/sec
+ const maxA: number = (deltaTimeWeight * maxV) / frameToMaxSpeed; // 1frameあたりの加速度
+
+ // 目指す向きは、(dx, dy)方向のベクトルとなる
+ const dx: number = this._faceTargetX - this._faceX;
+ const dy: number = this._faceTargetY - this._faceY;
+
+ if (CubismMath.abs(dx) <= Epsilon && CubismMath.abs(dy) <= Epsilon) {
+ return; // 変化なし
+ }
+
+ // 速度の最大よりも大きい場合は、速度を落とす
+ const d: number = CubismMath.sqrt(dx * dx + dy * dy);
+
+ // 進行方向の最大速度ベクトル
+ const vx: number = (maxV * dx) / d;
+ const vy: number = (maxV * dy) / d;
+
+ // 現在の速度から、新規速度への変化(加速度)を求める
+ let ax: number = vx - this._faceVX;
+ let ay: number = vy - this._faceVY;
+
+ const a: number = CubismMath.sqrt(ax * ax + ay * ay);
+
+ // 加速のとき
+ if (a < -maxA || a > maxA) {
+ ax *= maxA / a;
+ ay *= maxA / a;
+ }
+
+ // 加速度を元の速度に足して、新速度とする
+ this._faceVX += ax;
+ this._faceVY += ay;
+
+ // 目的の方向に近づいたとき、滑らかに減速するための処理
+ // 設定された加速度で止まる事の出来る距離と速度の関係から
+ // 現在とりうる最高速度を計算し、それ以上の時は速度を落とす
+ // ※本来、人間は筋力で力(加速度)を調整できるため、より自由度が高いが、簡単な処理で済ませている
+ {
+ // 加速度、速度、距離の関係式。
+ // 2 6 2 3
+ // sqrt(a t + 16 a h t - 8 a h) - a t
+ // v = --------------------------------------
+ // 2
+ // 4 t - 2
+ // (t=1)
+ // 時刻tは、あらかじめ加速度、速度を1/60(フレームレート、単位なし)で
+ // 考えているので、t=1として消してよい(※未検証)
+
+ const maxV: number =
+ 0.5 *
+ (CubismMath.sqrt(maxA * maxA + 16.0 * maxA * d - 8.0 * maxA * d) -
+ maxA);
+ const curV: number = CubismMath.sqrt(
+ this._faceVX * this._faceVX + this._faceVY * this._faceVY
+ );
+
+ if (curV > maxV) {
+ // 現在の速度 > 最高速度のとき、最高速度まで減速
+ this._faceVX *= maxV / curV;
+ this._faceVY *= maxV / curV;
+ }
+ }
+
+ this._faceX += this._faceVX;
+ this._faceY += this._faceVY;
+ }
+
+ /**
+ * X軸の顔の向きの値を取得
+ *
+ * @return X軸の顔の向きの値(-1.0 ~ 1.0)
+ */
+ public getX(): number {
+ return this._faceX;
+ }
+
+ /**
+ * Y軸の顔の向きの値を取得
+ *
+ * @return Y軸の顔の向きの値(-1.0 ~ 1.0)
+ */
+ public getY(): number {
+ return this._faceY;
+ }
+
+ /**
+ * 顔の向きの目標値を設定
+ *
+ * @param x X軸の顔の向きの値(-1.0 ~ 1.0)
+ * @param y Y軸の顔の向きの値(-1.0 ~ 1.0)
+ */
+ public set(x: number, y: number): void {
+ this._faceTargetX = x;
+ this._faceTargetY = y;
+ }
+
+ private _faceTargetX: number; // 顔の向きのX目標値(この値に近づいていく)
+ private _faceTargetY: number; // 顔の向きのY目標値(この値に近づいていく)
+ private _faceX: number; // 顔の向きX(-1.0 ~ 1.0)
+ private _faceY: number; // 顔の向きY(-1.0 ~ 1.0)
+ private _faceVX: number; // 顔の向きの変化速度X
+ private _faceVY: number; // 顔の向きの変化速度Y
+ private _lastTimeSeconds: number; // 最後の実行時間[秒]
+ private _userTimeSeconds: number; // デルタ時間の積算値[秒]
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismtargetpoint';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismTargetPoint = $.CubismTargetPoint;
+ export type CubismTargetPoint = $.CubismTargetPoint;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismvector2.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismvector2.ts
new file mode 100644
index 000000000..1a7dc3d49
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismvector2.ts
@@ -0,0 +1,169 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * 2次元ベクトル型
+ *
+ * 2次元ベクトル型の機能を提供する。
+ */
+export class CubismVector2 {
+ /**
+ * コンストラクタ
+ */
+ public constructor(public x?: number, public y?: number) {
+ this.x = x == undefined ? 0.0 : x;
+
+ this.y = y == undefined ? 0.0 : y;
+ }
+
+ /**
+ * ベクトルの加算
+ *
+ * @param vector2 加算するベクトル値
+ * @return 加算結果 ベクトル値
+ */
+ public add(vector2: CubismVector2): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
+ ret.x = this.x + vector2.x;
+ ret.y = this.y + vector2.y;
+ return ret;
+ }
+
+ /**
+ * ベクトルの減算
+ *
+ * @param vector2 減算するベクトル値
+ * @return 減算結果 ベクトル値
+ */
+ public substract(vector2: CubismVector2): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
+ ret.x = this.x - vector2.x;
+ ret.y = this.y - vector2.y;
+ return ret;
+ }
+
+ /**
+ * ベクトルの乗算
+ *
+ * @param vector2 乗算するベクトル値
+ * @return 乗算結果 ベクトル値
+ */
+ public multiply(vector2: CubismVector2): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
+ ret.x = this.x * vector2.x;
+ ret.y = this.y * vector2.y;
+ return ret;
+ }
+
+ /**
+ * ベクトルの乗算(スカラー)
+ *
+ * @param scalar 乗算するスカラー値
+ * @return 乗算結果 ベクトル値
+ */
+ public multiplyByScaler(scalar: number): CubismVector2 {
+ return this.multiply(new CubismVector2(scalar, scalar));
+ }
+
+ /**
+ * ベクトルの除算
+ *
+ * @param vector2 除算するベクトル値
+ * @return 除算結果 ベクトル値
+ */
+ public division(vector2: CubismVector2): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0.0, 0.0);
+ ret.x = this.x / vector2.x;
+ ret.y = this.y / vector2.y;
+ return ret;
+ }
+
+ /**
+ * ベクトルの除算(スカラー)
+ *
+ * @param scalar 除算するスカラー値
+ * @return 除算結果 ベクトル値
+ */
+ public divisionByScalar(scalar: number): CubismVector2 {
+ return this.division(new CubismVector2(scalar, scalar));
+ }
+
+ /**
+ * ベクトルの長さを取得する
+ *
+ * @return ベクトルの長さ
+ */
+ public getLength(): number {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ }
+
+ /**
+ * ベクトルの距離の取得
+ *
+ * @param a 点
+ * @return ベクトルの距離
+ */
+ public getDistanceWith(a: CubismVector2): number {
+ return Math.sqrt(
+ (this.x - a.x) * (this.x - a.x) + (this.y - a.y) * (this.y - a.y)
+ );
+ }
+
+ /**
+ * ドット積の計算
+ *
+ * @param a 値
+ * @return 結果
+ */
+ public dot(a: CubismVector2): number {
+ return this.x * a.x + this.y * a.y;
+ }
+
+ /**
+ * 正規化の適用
+ */
+ public normalize(): void {
+ const length: number = Math.pow(this.x * this.x + this.y * this.y, 0.5);
+
+ this.x = this.x / length;
+ this.y = this.y / length;
+ }
+
+ /**
+ * 等しさの確認(等しいか?)
+ *
+ * 値が等しいか?
+ *
+ * @param rhs 確認する値
+ * @return true 値は等しい
+ * @return false 値は等しくない
+ */
+ public isEqual(rhs: CubismVector2): boolean {
+ return this.x == rhs.x && this.y == rhs.y;
+ }
+
+ /**
+ * 等しさの確認(等しくないか?)
+ *
+ * 値が等しくないか?
+ *
+ * @param rhs 確認する値
+ * @return true 値は等しくない
+ * @return false 値は等しい
+ */
+ public isNotEqual(rhs: CubismVector2): boolean {
+ return !this.isEqual(rhs);
+ }
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismvector2';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismVector2 = $.CubismVector2;
+ export type CubismVector2 = $.CubismVector2;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismviewmatrix.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismviewmatrix.ts
new file mode 100644
index 000000000..c81a05a9b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/math/cubismviewmatrix.ts
@@ -0,0 +1,339 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismMatrix44 } from './cubismmatrix44';
+
+/**
+ * カメラの位置変更に使うと便利な4x4行列
+ *
+ * カメラの位置変更に使うと便利な4x4行列のクラス。
+ */
+export class CubismViewMatrix extends CubismMatrix44 {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ super();
+ this._screenLeft = 0.0;
+ this._screenRight = 0.0;
+ this._screenTop = 0.0;
+ this._screenBottom = 0.0;
+ this._maxLeft = 0.0;
+ this._maxRight = 0.0;
+ this._maxTop = 0.0;
+ this._maxBottom = 0.0;
+ this._maxScale = 0.0;
+ this._minScale = 0.0;
+ }
+
+ /**
+ * 移動を調整
+ *
+ * @param x X軸の移動量
+ * @param y Y軸の移動量
+ */
+ public adjustTranslate(x: number, y: number): void {
+ if (this._tr[0] * this._maxLeft + (this._tr[12] + x) > this._screenLeft) {
+ x = this._screenLeft - this._tr[0] * this._maxLeft - this._tr[12];
+ }
+
+ if (this._tr[0] * this._maxRight + (this._tr[12] + x) < this._screenRight) {
+ x = this._screenRight - this._tr[0] * this._maxRight - this._tr[12];
+ }
+
+ if (this._tr[5] * this._maxTop + (this._tr[13] + y) < this._screenTop) {
+ y = this._screenTop - this._tr[5] * this._maxTop - this._tr[13];
+ }
+
+ if (
+ this._tr[5] * this._maxBottom + (this._tr[13] + y) >
+ this._screenBottom
+ ) {
+ y = this._screenBottom - this._tr[5] * this._maxBottom - this._tr[13];
+ }
+
+ const tr1: Float32Array = new Float32Array([
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ x,
+ y,
+ 0.0,
+ 1.0
+ ]);
+
+ CubismMatrix44.multiply(tr1, this._tr, this._tr);
+ }
+
+ /**
+ * 拡大率を調整
+ *
+ * @param cx 拡大を行うX軸の中心位置
+ * @param cy 拡大を行うY軸の中心位置
+ * @param scale 拡大率
+ */
+ public adjustScale(cx: number, cy: number, scale: number): void {
+ const maxScale: number = this.getMaxScale();
+ const minScale: number = this.getMinScale();
+
+ const targetScale = scale * this._tr[0];
+
+ if (targetScale < minScale) {
+ if (this._tr[0] > 0.0) {
+ scale = minScale / this._tr[0];
+ }
+ } else if (targetScale > maxScale) {
+ if (this._tr[0] > 0.0) {
+ scale = maxScale / this._tr[0];
+ }
+ }
+
+ const tr1: Float32Array = new Float32Array([
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ cx,
+ cy,
+ 0.0,
+ 1.0
+ ]);
+
+ const tr2: Float32Array = new Float32Array([
+ scale,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ scale,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ]);
+
+ const tr3: Float32Array = new Float32Array([
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0.0,
+ -cx,
+ -cy,
+ 0.0,
+ 1.0
+ ]);
+
+ CubismMatrix44.multiply(tr3, this._tr, this._tr);
+ CubismMatrix44.multiply(tr2, this._tr, this._tr);
+ CubismMatrix44.multiply(tr1, this._tr, this._tr);
+ }
+
+ /**
+ * デバイスに対応する論理座養生の範囲の設定
+ *
+ * @param left 左辺のX軸の位置
+ * @param right 右辺のX軸の位置
+ * @param bottom 下辺のY軸の位置
+ * @param top 上辺のY軸の位置
+ */
+ public setScreenRect(
+ left: number,
+ right: number,
+ bottom: number,
+ top: number
+ ): void {
+ this._screenLeft = left;
+ this._screenRight = right;
+ this._screenBottom = bottom;
+ this._screenTop = top;
+ }
+
+ /**
+ * デバイスに対応する論理座標上の移動可能範囲の設定
+ * @param left 左辺のX軸の位置
+ * @param right 右辺のX軸の位置
+ * @param bottom 下辺のY軸の位置
+ * @param top 上辺のY軸の位置
+ */
+ public setMaxScreenRect(
+ left: number,
+ right: number,
+ bottom: number,
+ top: number
+ ): void {
+ this._maxLeft = left;
+ this._maxRight = right;
+ this._maxTop = top;
+ this._maxBottom = bottom;
+ }
+
+ /**
+ * 最大拡大率の設定
+ * @param maxScale 最大拡大率
+ */
+ public setMaxScale(maxScale: number): void {
+ this._maxScale = maxScale;
+ }
+
+ /**
+ * 最小拡大率の設定
+ * @param minScale 最小拡大率
+ */
+ public setMinScale(minScale: number): void {
+ this._minScale = minScale;
+ }
+
+ /**
+ * 最大拡大率の取得
+ * @return 最大拡大率
+ */
+ public getMaxScale(): number {
+ return this._maxScale;
+ }
+
+ /**
+ * 最小拡大率の取得
+ * @return 最小拡大率
+ */
+ public getMinScale(): number {
+ return this._minScale;
+ }
+
+ /**
+ * 拡大率が最大になっているかを確認する
+ *
+ * @return true 拡大率は最大
+ * @return false 拡大率は最大ではない
+ */
+ public isMaxScale(): boolean {
+ return this.getScaleX() >= this._maxScale;
+ }
+
+ /**
+ * 拡大率が最小になっているかを確認する
+ *
+ * @return true 拡大率は最小
+ * @return false 拡大率は最小ではない
+ */
+ public isMinScale(): boolean {
+ return this.getScaleX() <= this._minScale;
+ }
+
+ /**
+ * デバイスに対応する論理座標の左辺のX軸位置を取得する
+ * @return デバイスに対応する論理座標の左辺のX軸位置
+ */
+ public getScreenLeft(): number {
+ return this._screenLeft;
+ }
+
+ /**
+ * デバイスに対応する論理座標の右辺のX軸位置を取得する
+ * @return デバイスに対応する論理座標の右辺のX軸位置
+ */
+ public getScreenRight(): number {
+ return this._screenRight;
+ }
+
+ /**
+ * デバイスに対応する論理座標の下辺のY軸位置を取得する
+ * @return デバイスに対応する論理座標の下辺のY軸位置
+ */
+ public getScreenBottom(): number {
+ return this._screenBottom;
+ }
+
+ /**
+ * デバイスに対応する論理座標の上辺のY軸位置を取得する
+ * @return デバイスに対応する論理座標の上辺のY軸位置
+ */
+ public getScreenTop(): number {
+ return this._screenTop;
+ }
+
+ /**
+ * 左辺のX軸位置の最大値の取得
+ * @return 左辺のX軸位置の最大値
+ */
+ public getMaxLeft(): number {
+ return this._maxLeft;
+ }
+
+ /**
+ * 右辺のX軸位置の最大値の取得
+ * @return 右辺のX軸位置の最大値
+ */
+ public getMaxRight(): number {
+ return this._maxRight;
+ }
+
+ /**
+ * 下辺のY軸位置の最大値の取得
+ * @return 下辺のY軸位置の最大値
+ */
+ public getMaxBottom(): number {
+ return this._maxBottom;
+ }
+
+ /**
+ * 上辺のY軸位置の最大値の取得
+ * @return 上辺のY軸位置の最大値
+ */
+ public getMaxTop(): number {
+ return this._maxTop;
+ }
+
+ private _screenLeft: number; // デバイスに対応する論理座標上の範囲(左辺X軸位置)
+ private _screenRight: number; // デバイスに対応する論理座標上の範囲(右辺X軸位置)
+ private _screenTop: number; // デバイスに対応する論理座標上の範囲(上辺Y軸位置)
+ private _screenBottom: number; // デバイスに対応する論理座標上の範囲(下辺Y軸位置)
+ private _maxLeft: number; // 論理座標上の移動可能範囲(左辺X軸位置)
+ private _maxRight: number; // 論理座標上の移動可能範囲(右辺X軸位置)
+ private _maxTop: number; // 論理座標上の移動可能範囲(上辺Y軸位置)
+ private _maxBottom: number; // 論理座標上の移動可能範囲(下辺Y軸位置)
+ private _maxScale: number; // 拡大率の最大値
+ private _minScale: number; // 拡大率の最小値
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismviewmatrix';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismViewMatrix = $.CubismViewMatrix;
+ export type CubismViewMatrix = $.CubismViewMatrix;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmoc.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmoc.ts
new file mode 100644
index 000000000..5e2acecfd
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmoc.ts
@@ -0,0 +1,105 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CSM_ASSERT } from '../utils/cubismdebug';
+import { CubismModel } from './cubismmodel';
+
+/**
+ * Mocデータの管理
+ *
+ * Mocデータの管理を行うクラス。
+ */
+export class CubismMoc {
+ /**
+ * Mocデータの作成
+ */
+ public static create(mocBytes: ArrayBuffer): CubismMoc {
+ let cubismMoc: CubismMoc = null;
+ const moc: Live2DCubismCore.Moc = Live2DCubismCore.Moc.fromArrayBuffer(
+ mocBytes
+ );
+
+ if (moc) {
+ cubismMoc = new CubismMoc(moc);
+ }
+
+ return cubismMoc;
+ }
+
+ /**
+ * Mocデータを削除
+ *
+ * Mocデータを削除する
+ */
+ public static delete(moc: CubismMoc): void {
+ moc._moc._release();
+ moc._moc = null;
+ moc = null;
+ }
+
+ /**
+ * モデルを作成する
+ *
+ * @return Mocデータから作成されたモデル
+ */
+ createModel(): CubismModel {
+ let cubismModel: CubismModel = null;
+
+ const model: Live2DCubismCore.Model = Live2DCubismCore.Model.fromMoc(
+ this._moc
+ );
+
+ if (model) {
+ cubismModel = new CubismModel(model);
+ cubismModel.initialize();
+
+ ++this._modelCount;
+ }
+
+ return cubismModel;
+ }
+
+ /**
+ * モデルを削除する
+ */
+ deleteModel(model: CubismModel): void {
+ if (model != null) {
+ model.release();
+ model = null;
+ --this._modelCount;
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ private constructor(moc: Live2DCubismCore.Moc) {
+ this._moc = moc;
+ this._modelCount = 0;
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ CSM_ASSERT(this._modelCount == 0);
+
+ this._moc._release();
+ this._moc = null;
+ }
+
+ _moc: Live2DCubismCore.Moc; // Mocデータ
+ _modelCount: number; // Mocデータから作られたモデルの個数
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmoc';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMoc = $.CubismMoc;
+ export type CubismMoc = $.CubismMoc;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodel.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodel.ts
new file mode 100644
index 000000000..5cfb14a1b
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodel.ts
@@ -0,0 +1,818 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismBlendMode } from '../rendering/cubismrenderer';
+import { csmMap } from '../type/csmmap';
+import { csmVector } from '../type/csmvector';
+import { CSM_ASSERT } from '../utils/cubismdebug';
+
+/**
+ * モデル
+ *
+ * Mocデータから生成されるモデルのクラス。
+ */
+export class CubismModel {
+ /**
+ * モデルのパラメータの更新
+ */
+ public update(): void {
+ // Update model
+ this._model.update();
+
+ this._model.drawables.resetDynamicFlags();
+ }
+
+ /**
+ * キャンバスの幅を取得する
+ */
+ public getCanvasWidth(): number {
+ if (this._model == null) {
+ return 0.0;
+ }
+
+ return (
+ this._model.canvasinfo.CanvasWidth / this._model.canvasinfo.PixelsPerUnit
+ );
+ }
+
+ /**
+ * キャンバスの高さを取得する
+ */
+ public getCanvasHeight(): number {
+ if (this._model == null) {
+ return 0.0;
+ }
+
+ return (
+ this._model.canvasinfo.CanvasHeight / this._model.canvasinfo.PixelsPerUnit
+ );
+ }
+
+ /**
+ * パラメータを保存する
+ */
+ public saveParameters(): void {
+ const parameterCount: number = this._model.parameters.count;
+ const savedParameterCount: number = this._savedParameters.getSize();
+
+ for (let i = 0; i < parameterCount; ++i) {
+ if (i < savedParameterCount) {
+ this._savedParameters.set(i, this._parameterValues[i]);
+ } else {
+ this._savedParameters.pushBack(this._parameterValues[i]);
+ }
+ }
+ }
+
+ /**
+ * モデルを取得
+ */
+ public getModel(): Live2DCubismCore.Model {
+ return this._model;
+ }
+
+ /**
+ * パーツのインデックスを取得
+ * @param partId パーツのID
+ * @return パーツのインデックス
+ */
+ public getPartIndex(partId: CubismIdHandle): number {
+ let partIndex: number;
+ const partCount: number = this._model.parts.count;
+
+ for (partIndex = 0; partIndex < partCount; ++partIndex) {
+ if (partId == this._partIds.at(partIndex)) {
+ return partIndex;
+ }
+ }
+
+ // モデルに存在していない場合、非存在パーツIDリスト内にあるかを検索し、そのインデックスを返す
+ if (this._notExistPartId.isExist(partId)) {
+ return this._notExistPartId.getValue(partId);
+ }
+
+ // 非存在パーツIDリストにない場合、新しく要素を追加する
+ partIndex = partCount + this._notExistPartId.getSize();
+ this._notExistPartId.setValue(partId, partIndex);
+ this._notExistPartOpacities.appendKey(partIndex);
+
+ return partIndex;
+ }
+
+ /**
+ * パーツの個数の取得
+ * @return パーツの個数
+ */
+ public getPartCount(): number {
+ const partCount: number = this._model.parts.count;
+ return partCount;
+ }
+
+ /**
+ * パーツの不透明度の設定(Index)
+ * @param partIndex パーツのインデックス
+ * @param opacity 不透明度
+ */
+ public setPartOpacityByIndex(partIndex: number, opacity: number): void {
+ if (this._notExistPartOpacities.isExist(partIndex)) {
+ this._notExistPartOpacities.setValue(partIndex, opacity);
+ return;
+ }
+
+ // インデックスの範囲内検知
+ CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount());
+
+ this._partOpacities[partIndex] = opacity;
+ }
+
+ /**
+ * パーツの不透明度の設定(Id)
+ * @param partId パーツのID
+ * @param opacity パーツの不透明度
+ */
+ public setPartOpacityById(partId: CubismIdHandle, opacity: number): void {
+ // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要
+ const index: number = this.getPartIndex(partId);
+
+ if (index < 0) {
+ return; // パーツがないのでスキップ
+ }
+
+ this.setPartOpacityByIndex(index, opacity);
+ }
+
+ /**
+ * パーツの不透明度の取得(index)
+ * @param partIndex パーツのインデックス
+ * @return パーツの不透明度
+ */
+ public getPartOpacityByIndex(partIndex: number): number {
+ if (this._notExistPartOpacities.isExist(partIndex)) {
+ // モデルに存在しないパーツIDの場合、非存在パーツリストから不透明度を返す。
+ return this._notExistPartOpacities.getValue(partIndex);
+ }
+
+ // インデックスの範囲内検知
+ CSM_ASSERT(0 <= partIndex && partIndex < this.getPartCount());
+
+ return this._partOpacities[partIndex];
+ }
+
+ /**
+ * パーツの不透明度の取得(id)
+ * @param partId パーツのId
+ * @return パーツの不透明度
+ */
+ public getPartOpacityById(partId: CubismIdHandle): number {
+ // 高速化のためにPartIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要
+ const index: number = this.getPartIndex(partId);
+
+ if (index < 0) {
+ return 0; // パーツが無いのでスキップ
+ }
+
+ return this.getPartOpacityByIndex(index);
+ }
+
+ /**
+ * パラメータのインデックスの取得
+ * @param パラメータID
+ * @return パラメータのインデックス
+ */
+ public getParameterIndex(parameterId: CubismIdHandle): number {
+ let parameterIndex: number;
+ const idCount: number = this._model.parameters.count;
+
+ for (parameterIndex = 0; parameterIndex < idCount; ++parameterIndex) {
+ if (parameterId != this._parameterIds.at(parameterIndex)) {
+ continue;
+ }
+
+ return parameterIndex;
+ }
+
+ // モデルに存在していない場合、非存在パラメータIDリスト内を検索し、そのインデックスを返す
+ if (this._notExistParameterId.isExist(parameterId)) {
+ return this._notExistParameterId.getValue(parameterId);
+ }
+
+ // 非存在パラメータIDリストにない場合新しく要素を追加する
+ parameterIndex =
+ this._model.parameters.count + this._notExistParameterId.getSize();
+
+ this._notExistParameterId.setValue(parameterId, parameterIndex);
+ this._notExistParameterValues.appendKey(parameterIndex);
+
+ return parameterIndex;
+ }
+
+ /**
+ * パラメータの個数の取得
+ * @return パラメータの個数
+ */
+ public getParameterCount(): number {
+ return this._model.parameters.count;
+ }
+
+ /**
+ * パラメータの最大値の取得
+ * @param parameterIndex パラメータのインデックス
+ * @return パラメータの最大値
+ */
+ public getParameterMaximumValue(parameterIndex: number): number {
+ return this._model.parameters.maximumValues[parameterIndex];
+ }
+
+ /**
+ * パラメータの最小値の取得
+ * @param parameterIndex パラメータのインデックス
+ * @return パラメータの最小値
+ */
+ public getParameterMinimumValue(parameterIndex: number): number {
+ return this._model.parameters.minimumValues[parameterIndex];
+ }
+
+ /**
+ * パラメータのデフォルト値の取得
+ * @param parameterIndex パラメータのインデックス
+ * @return パラメータのデフォルト値
+ */
+ public getParameterDefaultValue(parameterIndex: number): number {
+ return this._model.parameters.defaultValues[parameterIndex];
+ }
+
+ /**
+ * パラメータの値の取得
+ * @param parameterIndex パラメータのインデックス
+ * @return パラメータの値
+ */
+ public getParameterValueByIndex(parameterIndex: number): number {
+ if (this._notExistParameterValues.isExist(parameterIndex)) {
+ return this._notExistParameterValues.getValue(parameterIndex);
+ }
+
+ // インデックスの範囲内検知
+ CSM_ASSERT(
+ 0 <= parameterIndex && parameterIndex < this.getParameterCount()
+ );
+
+ return this._parameterValues[parameterIndex];
+ }
+
+ /**
+ * パラメータの値の取得
+ * @param parameterId パラメータのID
+ * @return パラメータの値
+ */
+ public getParameterValueById(parameterId: CubismIdHandle): number {
+ // 高速化のためにparameterIndexを取得できる機構になっているが、外部からの設定の時は呼び出し頻度が低いため不要
+ const parameterIndex: number = this.getParameterIndex(parameterId);
+ return this.getParameterValueByIndex(parameterIndex);
+ }
+
+ /**
+ * パラメータの値の設定
+ * @param parameterIndex パラメータのインデックス
+ * @param value パラメータの値
+ * @param weight 重み
+ */
+ public setParameterValueByIndex(
+ parameterIndex: number,
+ value: number,
+ weight = 1.0
+ ): void {
+ if (this._notExistParameterValues.isExist(parameterIndex)) {
+ this._notExistParameterValues.setValue(
+ parameterIndex,
+ weight == 1
+ ? value
+ : this._notExistParameterValues.getValue(parameterIndex) *
+ (1 - weight) +
+ value * weight
+ );
+
+ return;
+ }
+
+ // インデックスの範囲内検知
+ CSM_ASSERT(
+ 0 <= parameterIndex && parameterIndex < this.getParameterCount()
+ );
+
+ if (this._model.parameters.maximumValues[parameterIndex] < value) {
+ value = this._model.parameters.maximumValues[parameterIndex];
+ }
+ if (this._model.parameters.minimumValues[parameterIndex] > value) {
+ value = this._model.parameters.minimumValues[parameterIndex];
+ }
+
+ this._parameterValues[parameterIndex] =
+ weight == 1
+ ? value
+ : (this._parameterValues[parameterIndex] =
+ this._parameterValues[parameterIndex] * (1 - weight) +
+ value * weight);
+ }
+
+ /**
+ * パラメータの値の設定
+ * @param parameterId パラメータのID
+ * @param value パラメータの値
+ * @param weight 重み
+ */
+ public setParameterValueById(
+ parameterId: CubismIdHandle,
+ value: number,
+ weight = 1.0
+ ): void {
+ const index: number = this.getParameterIndex(parameterId);
+ this.setParameterValueByIndex(index, value, weight);
+ }
+
+ /**
+ * パラメータの値の加算(index)
+ * @param parameterIndex パラメータインデックス
+ * @param value 加算する値
+ * @param weight 重み
+ */
+ public addParameterValueByIndex(
+ parameterIndex: number,
+ value: number,
+ weight = 1.0
+ ): void {
+ this.setParameterValueByIndex(
+ parameterIndex,
+ this.getParameterValueByIndex(parameterIndex) + value * weight
+ );
+ }
+
+ /**
+ * パラメータの値の加算(id)
+ * @param parameterId パラメータID
+ * @param value 加算する値
+ * @param weight 重み
+ */
+ public addParameterValueById(
+ parameterId: any,
+ value: number,
+ weight = 1.0
+ ): void {
+ const index: number = this.getParameterIndex(parameterId);
+ this.addParameterValueByIndex(index, value, weight);
+ }
+
+ /**
+ * パラメータの値の乗算
+ * @param parameterId パラメータのID
+ * @param value 乗算する値
+ * @param weight 重み
+ */
+ public multiplyParameterValueById(
+ parameterId: CubismIdHandle,
+ value: number,
+ weight = 1.0
+ ): void {
+ const index: number = this.getParameterIndex(parameterId);
+ this.multiplyParameterValueByIndex(index, value, weight);
+ }
+
+ /**
+ * パラメータの値の乗算
+ * @param parameterIndex パラメータのインデックス
+ * @param value 乗算する値
+ * @param weight 重み
+ */
+ public multiplyParameterValueByIndex(
+ parameterIndex: number,
+ value: number,
+ weight = 1.0
+ ): void {
+ this.setParameterValueByIndex(
+ parameterIndex,
+ this.getParameterValueByIndex(parameterIndex) *
+ (1.0 + (value - 1.0) * weight)
+ );
+ }
+
+ /**
+ * Drawableのインデックスの取得
+ * @param drawableId DrawableのID
+ * @return Drawableのインデックス
+ */
+ public getDrawableIndex(drawableId: CubismIdHandle): number {
+ const drawableCount = this._model.drawables.count;
+
+ for (
+ let drawableIndex = 0;
+ drawableIndex < drawableCount;
+ ++drawableIndex
+ ) {
+ if (this._drawableIds.at(drawableIndex) == drawableId) {
+ return drawableIndex;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Drawableの個数の取得
+ * @return drawableの個数
+ */
+ public getDrawableCount(): number {
+ const drawableCount = this._model.drawables.count;
+ return drawableCount;
+ }
+
+ /**
+ * DrawableのIDを取得する
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableのID
+ */
+ public getDrawableId(drawableIndex: number): CubismIdHandle {
+ const parameterIds: string[] = this._model.drawables.ids;
+ return CubismFramework.getIdManager().getId(parameterIds[drawableIndex]);
+ }
+
+ /**
+ * Drawableの描画順リストの取得
+ * @return Drawableの描画順リスト
+ */
+ public getDrawableRenderOrders(): Int32Array {
+ const renderOrders: Int32Array = this._model.drawables.renderOrders;
+ return renderOrders;
+ }
+
+ /**
+ * Drawableのテクスチャインデックスリストの取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableのテクスチャインデックスリスト
+ */
+ public getDrawableTextureIndices(drawableIndex: number): number {
+ const textureIndices: Int32Array = this._model.drawables.textureIndices;
+ return textureIndices[drawableIndex];
+ }
+
+ /**
+ * DrawableのVertexPositionsの変化情報の取得
+ *
+ * 直近のCubismModel.update関数でDrawableの頂点情報が変化したかを取得する。
+ *
+ * @param drawableIndex Drawableのインデックス
+ * @retval true Drawableの頂点情報が直近のCubismModel.update関数で変化した
+ * @retval false Drawableの頂点情報が直近のCubismModel.update関数で変化していない
+ */
+ public getDrawableDynamicFlagVertexPositionsDidChange(
+ drawableIndex: number
+ ): boolean {
+ const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags;
+ return Live2DCubismCore.Utils.hasVertexPositionsDidChangeBit(
+ dynamicFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * Drawableの頂点インデックスの個数の取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableの頂点インデックスの個数
+ */
+ public getDrawableVertexIndexCount(drawableIndex: number): number {
+ const indexCounts: Int32Array = this._model.drawables.indexCounts;
+ return indexCounts[drawableIndex];
+ }
+
+ /**
+ * Drawableの頂点の個数の取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableの頂点の個数
+ */
+ public getDrawableVertexCount(drawableIndex: number): number {
+ const vertexCounts = this._model.drawables.vertexCounts;
+ return vertexCounts[drawableIndex];
+ }
+
+ /**
+ * Drawableの頂点リストの取得
+ * @param drawableIndex drawableのインデックス
+ * @return drawableの頂点リスト
+ */
+ public getDrawableVertices(drawableIndex: number): Float32Array {
+ return this.getDrawableVertexPositions(drawableIndex);
+ }
+
+ /**
+ * Drawableの頂点インデックスリストの取得
+ * @param drarableIndex Drawableのインデックス
+ * @return drawableの頂点インデックスリスト
+ */
+ public getDrawableVertexIndices(drawableIndex: number): Uint16Array {
+ const indicesArray: Uint16Array[] = this._model.drawables.indices;
+ return indicesArray[drawableIndex];
+ }
+
+ /**
+ * Drawableの頂点リストの取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableの頂点リスト
+ */
+ public getDrawableVertexPositions(drawableIndex: number): Float32Array {
+ const verticesArray: Float32Array[] = this._model.drawables.vertexPositions;
+ return verticesArray[drawableIndex];
+ }
+
+ /**
+ * Drawableの頂点のUVリストの取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableの頂点UVリスト
+ */
+ public getDrawableVertexUvs(drawableIndex: number): Float32Array {
+ const uvsArray: Float32Array[] = this._model.drawables.vertexUvs;
+ return uvsArray[drawableIndex];
+ }
+
+ /**
+ * Drawableの不透明度の取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableの不透明度
+ */
+ public getDrawableOpacity(drawableIndex: number): number {
+ const opacities: Float32Array = this._model.drawables.opacities;
+ return opacities[drawableIndex];
+ }
+
+ /**
+ * Drawableのカリング情報の取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableのカリング情報
+ */
+ public getDrawableCulling(drawableIndex: number): boolean {
+ const constantFlags = this._model.drawables.constantFlags;
+
+ return !Live2DCubismCore.Utils.hasIsDoubleSidedBit(
+ constantFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * Drawableのブレンドモードを取得
+ * @param drawableIndex Drawableのインデックス
+ * @return drawableのブレンドモード
+ */
+ public getDrawableBlendMode(drawableIndex: number): CubismBlendMode {
+ const constantFlags = this._model.drawables.constantFlags;
+
+ return Live2DCubismCore.Utils.hasBlendAdditiveBit(
+ constantFlags[drawableIndex]
+ )
+ ? CubismBlendMode.CubismBlendMode_Additive
+ : Live2DCubismCore.Utils.hasBlendMultiplicativeBit(
+ constantFlags[drawableIndex]
+ )
+ ? CubismBlendMode.CubismBlendMode_Multiplicative
+ : CubismBlendMode.CubismBlendMode_Normal;
+ }
+
+ /**
+ * Drawableのマスクの反転使用の取得
+ *
+ * Drawableのマスク使用時の反転設定を取得する。
+ * マスクを使用しない場合は無視される。
+ *
+ * @param drawableIndex Drawableのインデックス
+ * @return Drawableの反転設定
+ */
+ public getDrawableInvertedMaskBit(drawableIndex: number): boolean {
+ const constantFlags: Uint8Array = this._model.drawables.constantFlags;
+
+ return Live2DCubismCore.Utils.hasIsInvertedMaskBit(
+ constantFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * Drawableのクリッピングマスクリストの取得
+ * @return Drawableのクリッピングマスクリスト
+ */
+ public getDrawableMasks(): Int32Array[] {
+ const masks: Int32Array[] = this._model.drawables.masks;
+ return masks;
+ }
+
+ /**
+ * Drawableのクリッピングマスクの個数リストの取得
+ * @return Drawableのクリッピングマスクの個数リスト
+ */
+ public getDrawableMaskCounts(): Int32Array {
+ const maskCounts: Int32Array = this._model.drawables.maskCounts;
+ return maskCounts;
+ }
+
+ /**
+ * クリッピングマスクの使用状態
+ *
+ * @return true クリッピングマスクを使用している
+ * @return false クリッピングマスクを使用していない
+ */
+ public isUsingMasking(): boolean {
+ for (let d = 0; d < this._model.drawables.count; ++d) {
+ if (this._model.drawables.maskCounts[d] <= 0) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Drawableの表示情報を取得する
+ *
+ * @param drawableIndex Drawableのインデックス
+ * @return true Drawableが表示
+ * @return false Drawableが非表示
+ */
+ public getDrawableDynamicFlagIsVisible(drawableIndex: number): boolean {
+ const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags;
+ return Live2DCubismCore.Utils.hasIsVisibleBit(dynamicFlags[drawableIndex]);
+ }
+
+ /**
+ * DrawableのDrawOrderの変化情報の取得
+ *
+ * 直近のCubismModel.update関数でdrawableのdrawOrderが変化したかを取得する。
+ * drawOrderはartMesh上で指定する0から1000の情報
+ * @param drawableIndex drawableのインデックス
+ * @return true drawableの不透明度が直近のCubismModel.update関数で変化した
+ * @return false drawableの不透明度が直近のCubismModel.update関数で変化している
+ */
+ public getDrawableDynamicFlagVisibilityDidChange(
+ drawableIndex: number
+ ): boolean {
+ const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags;
+ return Live2DCubismCore.Utils.hasVisibilityDidChangeBit(
+ dynamicFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * Drawableの不透明度の変化情報の取得
+ *
+ * 直近のCubismModel.update関数でdrawableの不透明度が変化したかを取得する。
+ *
+ * @param drawableIndex drawableのインデックス
+ * @return true Drawableの不透明度が直近のCubismModel.update関数で変化した
+ * @return false Drawableの不透明度が直近のCubismModel.update関数で変化してない
+ */
+ public getDrawableDynamicFlagOpacityDidChange(
+ drawableIndex: number
+ ): boolean {
+ const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags;
+ return Live2DCubismCore.Utils.hasOpacityDidChangeBit(
+ dynamicFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * Drawableの描画順序の変化情報の取得
+ *
+ * 直近のCubismModel.update関数でDrawableの描画の順序が変化したかを取得する。
+ *
+ * @param drawableIndex Drawableのインデックス
+ * @return true Drawableの描画の順序が直近のCubismModel.update関数で変化した
+ * @return false Drawableの描画の順序が直近のCubismModel.update関数で変化してない
+ */
+ public getDrawableDynamicFlagRenderOrderDidChange(
+ drawableIndex: number
+ ): boolean {
+ const dynamicFlags: Uint8Array = this._model.drawables.dynamicFlags;
+ return Live2DCubismCore.Utils.hasRenderOrderDidChangeBit(
+ dynamicFlags[drawableIndex]
+ );
+ }
+
+ /**
+ * 保存されたパラメータの読み込み
+ */
+ public loadParameters(): void {
+ let parameterCount: number = this._model.parameters.count;
+ const savedParameterCount: number = this._savedParameters.getSize();
+
+ if (parameterCount > savedParameterCount) {
+ parameterCount = savedParameterCount;
+ }
+
+ for (let i = 0; i < parameterCount; ++i) {
+ this._parameterValues[i] = this._savedParameters.at(i);
+ }
+ }
+
+ /**
+ * 初期化する
+ */
+ public initialize(): void {
+ CSM_ASSERT(this._model);
+
+ this._parameterValues = this._model.parameters.values;
+ this._partOpacities = this._model.parts.opacities;
+ this._parameterMaximumValues = this._model.parameters.maximumValues;
+ this._parameterMinimumValues = this._model.parameters.minimumValues;
+
+ {
+ const parameterIds: string[] = this._model.parameters.ids;
+ const parameterCount: number = this._model.parameters.count;
+
+ this._parameterIds.prepareCapacity(parameterCount);
+ for (let i = 0; i < parameterCount; ++i) {
+ this._parameterIds.pushBack(
+ CubismFramework.getIdManager().getId(parameterIds[i])
+ );
+ }
+ }
+
+ {
+ const partIds: string[] = this._model.parts.ids;
+ const partCount: number = this._model.parts.count;
+
+ this._partIds.prepareCapacity(partCount);
+ for (let i = 0; i < partCount; ++i) {
+ this._partIds.pushBack(
+ CubismFramework.getIdManager().getId(partIds[i])
+ );
+ }
+ }
+
+ {
+ const drawableIds: string[] = this._model.drawables.ids;
+ const drawableCount: number = this._model.drawables.count;
+
+ this._drawableIds.prepareCapacity(drawableCount);
+ for (let i = 0; i < drawableCount; ++i) {
+ this._drawableIds.pushBack(
+ CubismFramework.getIdManager().getId(drawableIds[i])
+ );
+ }
+ }
+ }
+
+ /**
+ * コンストラクタ
+ * @param model モデル
+ */
+ public constructor(model: Live2DCubismCore.Model) {
+ this._model = model;
+ this._parameterValues = null;
+ this._parameterMaximumValues = null;
+ this._parameterMinimumValues = null;
+ this._partOpacities = null;
+ this._savedParameters = new csmVector();
+ this._parameterIds = new csmVector();
+ this._drawableIds = new csmVector();
+ this._partIds = new csmVector();
+
+ this._notExistPartId = new csmMap();
+ this._notExistParameterId = new csmMap();
+ this._notExistParameterValues = new csmMap();
+ this._notExistPartOpacities = new csmMap();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this._model.release();
+ this._model = null;
+ }
+
+ private _notExistPartOpacities: csmMap; // 存在していないパーツの不透明度のリスト
+ private _notExistPartId: csmMap; // 存在していないパーツIDのリスト
+
+ private _notExistParameterValues: csmMap; // 存在していないパラメータの値のリスト
+ private _notExistParameterId: csmMap; // 存在していないパラメータIDのリスト
+
+ private _savedParameters: csmVector; // 保存されたパラメータ
+
+ private _model: Live2DCubismCore.Model; // モデル
+
+ private _parameterValues: Float32Array; // パラメータの値のリスト
+ private _parameterMaximumValues: Float32Array; // パラメータの最大値のリスト
+ private _parameterMinimumValues: Float32Array; // パラメータの最小値のリスト
+
+ private _partOpacities: Float32Array; // パーツの不透明度のリスト
+
+ private _parameterIds: csmVector;
+ private _partIds: csmVector;
+ private _drawableIds: csmVector;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmodel';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismModel = $.CubismModel;
+ export type CubismModel = $.CubismModel;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdata.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdata.ts
new file mode 100644
index 000000000..407fb5574
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdata.ts
@@ -0,0 +1,136 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { csmString } from '../type/csmstring';
+import { csmVector } from '../type/csmvector';
+import { CubismModelUserDataJson } from './cubismmodeluserdatajson';
+
+const ArtMesh = 'ArtMesh';
+
+/**
+ * ユーザーデータインターフェース
+ *
+ * Jsonから読み込んだユーザーデータを記録しておくための構造体
+ */
+export class CubismModelUserDataNode {
+ targetType: CubismIdHandle; // ユーザーデータターゲットタイプ
+ targetId: CubismIdHandle; // ユーザーデータターゲットのID
+ value: csmString; // ユーザーデータ
+}
+
+/**
+ * ユーザデータの管理クラス
+ *
+ * ユーザデータをロード、管理、検索インターフェイス、解放までを行う。
+ */
+export class CubismModelUserData {
+ /**
+ * インスタンスの作成
+ *
+ * @param buffer userdata3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @return 作成されたインスタンス
+ */
+ public static create(buffer: ArrayBuffer, size: number): CubismModelUserData {
+ const ret: CubismModelUserData = new CubismModelUserData();
+
+ ret.parseUserData(buffer, size);
+
+ return ret;
+ }
+
+ /**
+ * インスタンスを破棄する
+ *
+ * @param modelUserData 破棄するインスタンス
+ */
+ public static delete(modelUserData: CubismModelUserData): void {
+ if (modelUserData != null) {
+ modelUserData.release();
+ modelUserData = null;
+ }
+ }
+
+ /**
+ * ArtMeshのユーザーデータのリストの取得
+ *
+ * @return ユーザーデータリスト
+ */
+ public getArtMeshUserDatas(): csmVector {
+ return this._artMeshUserDataNode;
+ }
+
+ /**
+ * userdata3.jsonのパース
+ *
+ * @param buffer userdata3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public parseUserData(buffer: ArrayBuffer, size: number): void {
+ let json: CubismModelUserDataJson = new CubismModelUserDataJson(
+ buffer,
+ size
+ );
+
+ const typeOfArtMesh = CubismFramework.getIdManager().getId(ArtMesh);
+ const nodeCount: number = json.getUserDataCount();
+
+ for (let i = 0; i < nodeCount; i++) {
+ const addNode: CubismModelUserDataNode = new CubismModelUserDataNode();
+
+ addNode.targetId = json.getUserDataId(i);
+ addNode.targetType = CubismFramework.getIdManager().getId(
+ json.getUserDataTargetType(i)
+ );
+ addNode.value = new csmString(json.getUserDataValue(i));
+ this._userDataNodes.pushBack(addNode);
+
+ if (addNode.targetType == typeOfArtMesh) {
+ this._artMeshUserDataNode.pushBack(addNode);
+ }
+ }
+
+ json.release();
+ json = void 0;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._userDataNodes = new csmVector();
+ this._artMeshUserDataNode = new csmVector();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ *
+ * ユーザーデータ構造体配列を解放する
+ */
+ public release(): void {
+ for (let i = 0; i < this._userDataNodes.getSize(); ++i) {
+ this._userDataNodes.set(i, null);
+ }
+
+ this._userDataNodes = null;
+ }
+
+ private _userDataNodes: csmVector; // ユーザーデータ構造体配列
+ private _artMeshUserDataNode: csmVector; // 閲覧リストの保持
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmodeluserdata';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismModelUserData = $.CubismModelUserData;
+ export type CubismModelUserData = $.CubismModelUserData;
+ export const CubismModelUserDataNode = $.CubismModelUserDataNode;
+ export type CubismModelUserDataNode = $.CubismModelUserDataNode;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdatajson.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdatajson.ts
new file mode 100644
index 000000000..a691a70fa
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismmodeluserdatajson.ts
@@ -0,0 +1,117 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismJson } from '../utils/cubismjson';
+
+const Meta = 'Meta';
+const UserDataCount = 'UserDataCount';
+const TotalUserDataSize = 'TotalUserDataSize';
+const UserData = 'UserData';
+const Target = 'Target';
+const Id = 'Id';
+const Value = 'Value';
+
+export class CubismModelUserDataJson {
+ /**
+ * コンストラクタ
+ * @param buffer userdata3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public constructor(buffer: ArrayBuffer, size: number) {
+ this._json = CubismJson.create(buffer, size);
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ CubismJson.delete(this._json);
+ }
+
+ /**
+ * ユーザーデータ個数の取得
+ * @return ユーザーデータの個数
+ */
+ public getUserDataCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(UserDataCount)
+ .toInt();
+ }
+
+ /**
+ * ユーザーデータ総文字列数の取得
+ *
+ * @return ユーザーデータ総文字列数
+ */
+ public getTotalUserDataSize(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalUserDataSize)
+ .toInt();
+ }
+
+ /**
+ * ユーザーデータのタイプの取得
+ *
+ * @return ユーザーデータのタイプ
+ */
+ public getUserDataTargetType(i: number): string {
+ return this._json
+ .getRoot()
+ .getValueByString(UserData)
+ .getValueByIndex(i)
+ .getValueByString(Target)
+ .getRawString();
+ }
+
+ /**
+ * ユーザーデータのターゲットIDの取得
+ *
+ * @param i インデックス
+ * @return ユーザーデータターゲットID
+ */
+ public getUserDataId(i: number): CubismIdHandle {
+ return CubismFramework.getIdManager().getId(
+ this._json
+ .getRoot()
+ .getValueByString(UserData)
+ .getValueByIndex(i)
+ .getValueByString(Id)
+ .getRawString()
+ );
+ }
+
+ /**
+ * ユーザーデータの文字列の取得
+ *
+ * @param i インデックス
+ * @return ユーザーデータ
+ */
+ public getUserDataValue(i: number): string {
+ return this._json
+ .getRoot()
+ .getValueByString(UserData)
+ .getValueByIndex(i)
+ .getValueByString(Value)
+ .getRawString();
+ }
+
+ private _json: CubismJson;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmodeluserdatajson';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismModelUserDataJson = $.CubismModelUserDataJson;
+ export type CubismModelUserDataJson = $.CubismModelUserDataJson;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismusermodel.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismusermodel.ts
new file mode 100644
index 000000000..d6e4cdff7
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/model/cubismusermodel.ts
@@ -0,0 +1,440 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismBreath } from '../effect/cubismbreath';
+import { CubismEyeBlink } from '../effect/cubismeyeblink';
+import { CubismPose } from '../effect/cubismpose';
+import { CubismIdHandle } from '../id/cubismid';
+import { Constant } from '../live2dcubismframework';
+import { CubismModelMatrix } from '../math/cubismmodelmatrix';
+import { CubismTargetPoint } from '../math/cubismtargetpoint';
+import { ACubismMotion, FinishedMotionCallback } from '../motion/acubismmotion';
+import { CubismExpressionMotion } from '../motion/cubismexpressionmotion';
+import { CubismMotion } from '../motion/cubismmotion';
+import { CubismMotionManager } from '../motion/cubismmotionmanager';
+import { CubismMotionQueueManager } from '../motion/cubismmotionqueuemanager';
+import { CubismPhysics } from '../physics/cubismphysics';
+import { CubismRenderer_WebGL } from '../rendering/cubismrenderer_webgl';
+import { csmString } from '../type/csmstring';
+import { CubismLogError, CubismLogInfo } from '../utils/cubismdebug';
+import { CubismMoc } from './cubismmoc';
+import { CubismModel } from './cubismmodel';
+import { CubismModelUserData } from './cubismmodeluserdata';
+
+/**
+ * ユーザーが実際に使用するモデル
+ *
+ * ユーザーが実際に使用するモデルの基底クラス。これを継承してユーザーが実装する。
+ */
+export class CubismUserModel {
+ /**
+ * 初期化状態の取得
+ *
+ * 初期化されている状態か?
+ *
+ * @return true 初期化されている
+ * @return false 初期化されていない
+ */
+ public isInitialized(): boolean {
+ return this._initialized;
+ }
+
+ /**
+ * 初期化状態の設定
+ *
+ * 初期化状態を設定する。
+ *
+ * @param v 初期化状態
+ */
+ public setInitialized(v: boolean): void {
+ this._initialized = v;
+ }
+
+ /**
+ * 更新状態の取得
+ *
+ * 更新されている状態か?
+ *
+ * @return true 更新されている
+ * @return false 更新されていない
+ */
+ public isUpdating(): boolean {
+ return this._updating;
+ }
+
+ /**
+ * 更新状態の設定
+ *
+ * 更新状態を設定する
+ *
+ * @param v 更新状態
+ */
+ public setUpdating(v: boolean): void {
+ this._updating = v;
+ }
+
+ /**
+ * マウスドラッグ情報の設定
+ * @param ドラッグしているカーソルのX位置
+ * @param ドラッグしているカーソルのY位置
+ */
+ public setDragging(x: number, y: number): void {
+ this._dragManager.set(x, y);
+ }
+
+ /**
+ * 加速度の情報を設定する
+ * @param x X軸方向の加速度
+ * @param y Y軸方向の加速度
+ * @param z Z軸方向の加速度
+ */
+ public setAcceleration(x: number, y: number, z: number): void {
+ this._accelerationX = x;
+ this._accelerationY = y;
+ this._accelerationZ = z;
+ }
+
+ /**
+ * モデル行列を取得する
+ * @return モデル行列
+ */
+ public getModelMatrix(): CubismModelMatrix {
+ return this._modelMatrix;
+ }
+
+ /**
+ * 不透明度の設定
+ * @param a 不透明度
+ */
+ public setOpacity(a: number): void {
+ this._opacity = a;
+ }
+
+ /**
+ * 不透明度の取得
+ * @return 不透明度
+ */
+ public getOpacity(): number {
+ return this._opacity;
+ }
+
+ /**
+ * モデルデータを読み込む
+ *
+ * @param buffer moc3ファイルが読み込まれているバッファ
+ */
+ public loadModel(buffer: ArrayBuffer) {
+ this._moc = CubismMoc.create(buffer);
+ this._model = this._moc.createModel();
+ this._model.saveParameters();
+
+ if (this._moc == null || this._model == null) {
+ CubismLogError('Failed to CreateModel().');
+ return;
+ }
+
+ this._modelMatrix = new CubismModelMatrix(
+ this._model.getCanvasWidth(),
+ this._model.getCanvasHeight()
+ );
+ }
+
+ /**
+ * モーションデータを読み込む
+ * @param buffer motion3.jsonファイルが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @param name モーションの名前
+ * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数
+ * @return モーションクラス
+ */
+ public loadMotion = (
+ buffer: ArrayBuffer,
+ size: number,
+ name: string,
+ onFinishedMotionHandler?: FinishedMotionCallback
+ ) => CubismMotion.create(buffer, size, onFinishedMotionHandler);
+
+ /**
+ * 表情データの読み込み
+ * @param buffer expファイルが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @param name 表情の名前
+ */
+ public loadExpression(
+ buffer: ArrayBuffer,
+ size: number,
+ name: string
+ ): ACubismMotion {
+ return CubismExpressionMotion.create(buffer, size);
+ }
+
+ /**
+ * ポーズデータの読み込み
+ * @param buffer pose3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public loadPose(buffer: ArrayBuffer, size: number): void {
+ this._pose = CubismPose.create(buffer, size);
+ }
+
+ /**
+ * モデルに付属するユーザーデータを読み込む
+ * @param buffer userdata3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public loadUserData(buffer: ArrayBuffer, size: number): void {
+ this._modelUserData = CubismModelUserData.create(buffer, size);
+ }
+
+ /**
+ * 物理演算データの読み込み
+ * @param buffer physics3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public loadPhysics(buffer: ArrayBuffer, size: number): void {
+ this._physics = CubismPhysics.create(buffer, size);
+ }
+
+ /**
+ * 当たり判定の取得
+ * @param drawableId 検証したいDrawableのID
+ * @param pointX X位置
+ * @param pointY Y位置
+ * @return true ヒットしている
+ * @return false ヒットしていない
+ */
+ public isHit(
+ drawableId: CubismIdHandle,
+ pointX: number,
+ pointY: number
+ ): boolean {
+ const drawIndex: number = this._model.getDrawableIndex(drawableId);
+
+ if (drawIndex < 0) {
+ return false; // 存在しない場合はfalse
+ }
+
+ const count: number = this._model.getDrawableVertexCount(drawIndex);
+ const vertices: Float32Array = this._model.getDrawableVertices(drawIndex);
+
+ let left: number = vertices[0];
+ let right: number = vertices[0];
+ let top: number = vertices[1];
+ let bottom: number = vertices[1];
+
+ for (let j = 1; j < count; ++j) {
+ const x = vertices[Constant.vertexOffset + j * Constant.vertexStep];
+ const y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1];
+
+ if (x < left) {
+ left = x; // Min x
+ }
+
+ if (x > right) {
+ right = x; // Max x
+ }
+
+ if (y < top) {
+ top = y; // Min y
+ }
+
+ if (y > bottom) {
+ bottom = y; // Max y
+ }
+ }
+
+ const tx: number = this._modelMatrix.invertTransformX(pointX);
+ const ty: number = this._modelMatrix.invertTransformY(pointY);
+
+ return left <= tx && tx <= right && top <= ty && ty <= bottom;
+ }
+
+ /**
+ * モデルの取得
+ * @return モデル
+ */
+ public getModel(): CubismModel {
+ return this._model;
+ }
+
+ /**
+ * レンダラの取得
+ * @return レンダラ
+ */
+ public getRenderer(): CubismRenderer_WebGL {
+ return this._renderer;
+ }
+
+ /**
+ * レンダラを作成して初期化を実行する
+ */
+ public createRenderer(): void {
+ if (this._renderer) {
+ this.deleteRenderer();
+ }
+
+ this._renderer = new CubismRenderer_WebGL();
+ this._renderer.initialize(this._model);
+ }
+
+ /**
+ * レンダラの解放
+ */
+ public deleteRenderer(): void {
+ if (this._renderer != null) {
+ this._renderer.release();
+ this._renderer = null;
+ }
+ }
+
+ /**
+ * イベント発火時の標準処理
+ *
+ * Eventが再生処理時にあった場合の処理をする。
+ * 継承で上書きすることを想定している。
+ * 上書きしない場合はログ出力をする。
+ *
+ * @param eventValue 発火したイベントの文字列データ
+ */
+ public motionEventFired(eventValue: csmString): void {
+ CubismLogInfo('{0}', eventValue.s);
+ }
+
+ /**
+ * イベント用のコールバック
+ *
+ * CubismMotionQueueManagerにイベント用に登録するためのCallback。
+ * CubismUserModelの継承先のEventFiredを呼ぶ。
+ *
+ * @param caller 発火したイベントを管理していたモーションマネージャー、比較用
+ * @param eventValue 発火したイベントの文字列データ
+ * @param customData CubismUserModelを継承したインスタンスを想定
+ */
+ public static cubismDefaultMotionEventCallback(
+ caller: CubismMotionQueueManager,
+ eventValue: csmString,
+ customData: CubismUserModel
+ ): void {
+ const model: CubismUserModel = customData;
+
+ if (model != null) {
+ model.motionEventFired(eventValue);
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ // 各変数初期化
+ this._moc = null;
+ this._model = null;
+ this._motionManager = null;
+ this._expressionManager = null;
+ this._eyeBlink = null;
+ this._breath = null;
+ this._modelMatrix = null;
+ this._pose = null;
+ this._dragManager = null;
+ this._physics = null;
+ this._modelUserData = null;
+ this._initialized = false;
+ this._updating = false;
+ this._opacity = 1.0;
+ this._lipsync = true;
+ this._lastLipSyncValue = 0.0;
+ this._dragX = 0.0;
+ this._dragY = 0.0;
+ this._accelerationX = 0.0;
+ this._accelerationY = 0.0;
+ this._accelerationZ = 0.0;
+ this._debugMode = false;
+ this._renderer = null;
+
+ // モーションマネージャーを作成
+ this._motionManager = new CubismMotionManager();
+ this._motionManager.setEventCallback(
+ CubismUserModel.cubismDefaultMotionEventCallback,
+ this
+ );
+
+ // 表情マネージャーを作成
+ this._expressionManager = new CubismMotionManager();
+
+ // ドラッグによるアニメーション
+ this._dragManager = new CubismTargetPoint();
+ }
+
+ /**
+ * デストラクタに相当する処理
+ */
+ public release() {
+ if (this._motionManager != null) {
+ this._motionManager.release();
+ this._motionManager = null;
+ }
+
+ if (this._expressionManager != null) {
+ this._expressionManager.release();
+ this._expressionManager = null;
+ }
+
+ if (this._moc != null) {
+ this._moc.deleteModel(this._model);
+ this._moc.release();
+ this._moc = null;
+ }
+
+ this._modelMatrix = null;
+
+ CubismPose.delete(this._pose);
+ CubismEyeBlink.delete(this._eyeBlink);
+ CubismBreath.delete(this._breath);
+
+ this._dragManager = null;
+
+ CubismPhysics.delete(this._physics);
+ CubismModelUserData.delete(this._modelUserData);
+
+ this.deleteRenderer();
+ }
+
+ protected _moc: CubismMoc; // Mocデータ
+ protected _model: CubismModel; // Modelインスタンス
+
+ protected _motionManager: CubismMotionManager; // モーション管理
+ protected _expressionManager: CubismMotionManager; // 表情管理
+ protected _eyeBlink: CubismEyeBlink; // 自動まばたき
+ protected _breath: CubismBreath; // 呼吸
+ protected _modelMatrix: CubismModelMatrix; // モデル行列
+ protected _pose: CubismPose; // ポーズ管理
+ protected _dragManager: CubismTargetPoint; // マウスドラッグ
+ protected _physics: CubismPhysics; // 物理演算
+ protected _modelUserData: CubismModelUserData; // ユーザーデータ
+
+ protected _initialized: boolean; // 初期化されたかどうか
+ protected _updating: boolean; // 更新されたかどうか
+ protected _opacity: number; // 不透明度
+ protected _lipsync: boolean; // リップシンクするかどうか
+ protected _lastLipSyncValue: number; // 最後のリップシンクの制御地
+ protected _dragX: number; // マウスドラッグのX位置
+ protected _dragY: number; // マウスドラッグのY位置
+ protected _accelerationX: number; // X軸方向の加速度
+ protected _accelerationY: number; // Y軸方向の加速度
+ protected _accelerationZ: number; // Z軸方向の加速度
+ protected _debugMode: boolean; // デバッグモードかどうか
+
+ private _renderer: CubismRenderer_WebGL; // レンダラ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismusermodel';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismUserModel = $.CubismUserModel;
+ export type CubismUserModel = $.CubismUserModel;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/acubismmotion.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/acubismmotion.ts
new file mode 100644
index 000000000..3afcad2af
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/acubismmotion.ts
@@ -0,0 +1,279 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismMath } from '../math/cubismmath';
+import { CubismModel } from '../model/cubismmodel';
+import { csmString } from '../type/csmstring';
+import { csmVector } from '../type/csmvector';
+import { CSM_ASSERT } from '../utils/cubismdebug';
+import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
+
+/** モーション再生終了コールバック関数定義 */
+export type FinishedMotionCallback = (self: ACubismMotion) => void;
+
+/**
+ * モーションの抽象基底クラス
+ *
+ * モーションの抽象基底クラス。MotionQueueManagerによってモーションの再生を管理する。
+ */
+export abstract class ACubismMotion {
+ /**
+ * インスタンスの破棄
+ */
+ public static delete(motion: ACubismMotion): void {
+ motion.release();
+ motion = null;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._fadeInSeconds = -1.0;
+ this._fadeOutSeconds = -1.0;
+ this._weight = 1.0;
+ this._offsetSeconds = 0.0; // 再生の開始時刻
+ this._firedEventValues = new csmVector();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this._weight = 0.0;
+ }
+
+ /**
+ * モデルのパラメータ
+ * @param model 対象のモデル
+ * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ */
+ public updateParameters(
+ model: CubismModel,
+ motionQueueEntry: CubismMotionQueueEntry,
+ userTimeSeconds: number
+ ): void {
+ if (!motionQueueEntry.isAvailable() || motionQueueEntry.isFinished()) {
+ return;
+ }
+
+ if (!motionQueueEntry.isStarted()) {
+ motionQueueEntry.setIsStarted(true);
+ motionQueueEntry.setStartTime(userTimeSeconds - this._offsetSeconds); // モーションの開始時刻を記録
+ motionQueueEntry.setFadeInStartTime(userTimeSeconds); // フェードインの開始時刻
+
+ const duration: number = this.getDuration();
+
+ if (motionQueueEntry.getEndTime() < 0) {
+ // 開始していないうちに終了設定している場合がある。
+ motionQueueEntry.setEndTime(
+ duration <= 0 ? -1 : motionQueueEntry.getStartTime() + duration
+ );
+ // duration == -1 の場合はループする
+ }
+ }
+
+ let fadeWeight: number = this._weight; // 現在の値と掛け合わせる割合
+
+ //---- フェードイン・アウトの処理 ----
+ // 単純なサイン関数でイージングする
+ const fadeIn: number =
+ this._fadeInSeconds == 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
+ this._fadeInSeconds
+ );
+
+ const fadeOut: number =
+ this._fadeOutSeconds == 0.0 || motionQueueEntry.getEndTime() < 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (motionQueueEntry.getEndTime() - userTimeSeconds) /
+ this._fadeOutSeconds
+ );
+
+ fadeWeight = fadeWeight * fadeIn * fadeOut;
+
+ motionQueueEntry.setState(userTimeSeconds, fadeWeight);
+
+ CSM_ASSERT(0.0 <= fadeWeight && fadeWeight <= 1.0);
+
+ //---- 全てのパラメータIDをループする ----
+ this.doUpdateParameters(
+ model,
+ userTimeSeconds,
+ fadeWeight,
+ motionQueueEntry
+ );
+
+ // 後処理
+ // 終了時刻を過ぎたら終了フラグを立てる(CubismMotionQueueManager)
+ if (
+ motionQueueEntry.getEndTime() > 0 &&
+ motionQueueEntry.getEndTime() < userTimeSeconds
+ ) {
+ motionQueueEntry.setIsFinished(true); // 終了
+ }
+ }
+
+ /**
+ * フェードインの時間を設定する
+ * @param fadeInSeconds フェードインにかかる時間[秒]
+ */
+ public setFadeInTime(fadeInSeconds: number): void {
+ this._fadeInSeconds = fadeInSeconds;
+ }
+
+ /**
+ * フェードアウトの時間を設定する
+ * @param fadeOutSeconds フェードアウトにかかる時間[秒]
+ */
+ public setFadeOutTime(fadeOutSeconds: number): void {
+ this._fadeOutSeconds = fadeOutSeconds;
+ }
+
+ /**
+ * フェードアウトにかかる時間の取得
+ * @return フェードアウトにかかる時間[秒]
+ */
+ public getFadeOutTime(): number {
+ return this._fadeOutSeconds;
+ }
+
+ /**
+ * フェードインにかかる時間の取得
+ * @return フェードインにかかる時間[秒]
+ */
+ public getFadeInTime(): number {
+ return this._fadeInSeconds;
+ }
+
+ /**
+ * モーション適用の重みの設定
+ * @param weight 重み(0.0 - 1.0)
+ */
+ public setWeight(weight: number): void {
+ this._weight = weight;
+ }
+
+ /**
+ * モーション適用の重みの取得
+ * @return 重み(0.0 - 1.0)
+ */
+ public getWeight(): number {
+ return this._weight;
+ }
+
+ /**
+ * モーションの長さの取得
+ * @return モーションの長さ[秒]
+ *
+ * @note ループの時は「-1」。
+ * ループでない場合は、オーバーライドする。
+ * 正の値の時は取得される時間で終了する。
+ * 「-1」の時は外部から停止命令がない限り終わらない処理となる。
+ */
+ public getDuration(): number {
+ return -1.0;
+ }
+
+ /**
+ * モーションのループ1回分の長さの取得
+ * @return モーションのループ一回分の長さ[秒]
+ *
+ * @note ループしない場合は、getDuration()と同じ値を返す
+ * ループ一回分の長さが定義できない場合(プログラム的に動き続けるサブクラスなど)の場合は「-1」を返す
+ */
+ public getLoopDuration(): number {
+ return -1.0;
+ }
+
+ /**
+ * モーション再生の開始時刻の設定
+ * @param offsetSeconds モーション再生の開始時刻[秒]
+ */
+ public setOffsetTime(offsetSeconds: number): void {
+ this._offsetSeconds = offsetSeconds;
+ }
+
+ /**
+ * モデルのパラメータ更新
+ *
+ * イベント発火のチェック。
+ * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
+ *
+ * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
+ * @param motionTimeSeconds 今回の再生時間[秒]
+ */
+ public getFiredEvent(
+ beforeCheckTimeSeconds: number,
+ motionTimeSeconds: number
+ ): csmVector {
+ return this._firedEventValues;
+ }
+
+ /**
+ * モーションを更新して、モデルにパラメータ値を反映する
+ * @param model 対象のモデル
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ * @param weight モーションの重み
+ * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
+ * @return true モデルへパラメータ値の反映あり
+ * @return false モデルへのパラメータ値の反映なし(モーションの変化なし)
+ */
+ public abstract doUpdateParameters(
+ model: CubismModel,
+ userTimeSeconds: number,
+ weight: number,
+ motionQueueEntry: CubismMotionQueueEntry
+ ): void;
+
+ /**
+ * モーション再生終了コールバックの登録
+ *
+ * モーション再生終了コールバックを登録する。
+ * isFinishedフラグを設定するタイミングで呼び出される。
+ * 以下の状態の際には呼び出されない:
+ * 1. 再生中のモーションが「ループ」として設定されているとき
+ * 2. コールバックが登録されていない時
+ *
+ * @param onFinishedMotionHandler モーション再生終了コールバック関数
+ */
+ public setFinishedMotionHandler = (
+ onFinishedMotionHandler: FinishedMotionCallback
+ ) => (this._onFinishedMotion = onFinishedMotionHandler);
+
+ /**
+ * モーション再生終了コールバックの取得
+ *
+ * モーション再生終了コールバックを取得する。
+ *
+ * @return 登録されているモーション再生終了コールバック関数
+ */
+ public getFinishedMotionHandler = () => this._onFinishedMotion;
+
+ public _fadeInSeconds: number; // フェードインにかかる時間[秒]
+ public _fadeOutSeconds: number; // フェードアウトにかかる時間[秒]
+ public _weight: number; // モーションの重み
+ public _offsetSeconds: number; // モーション再生の開始時間[秒]
+
+ public _firedEventValues: csmVector;
+
+ // モーション再生終了コールバック関数
+ public _onFinishedMotion?: FinishedMotionCallback;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './acubismmotion';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const ACubismMotion = $.ACubismMotion;
+ export type ACubismMotion = $.ACubismMotion;
+ export type FinishedMotionCallback = $.FinishedMotionCallback;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismexpressionmotion.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismexpressionmotion.ts
new file mode 100644
index 000000000..9ffa94e04
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismexpressionmotion.ts
@@ -0,0 +1,199 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismModel } from '../model/cubismmodel';
+import { csmVector } from '../type/csmvector';
+import { CubismJson, Value } from '../utils/cubismjson';
+import { ACubismMotion } from './acubismmotion';
+import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
+
+// exp3.jsonのキーとデフォルト
+const ExpressionKeyFadeIn = 'FadeInTime';
+const ExpressionKeyFadeOut = 'FadeOutTime';
+const ExpressionKeyParameters = 'Parameters';
+const ExpressionKeyId = 'Id';
+const ExpressionKeyValue = 'Value';
+const ExpressionKeyBlend = 'Blend';
+const BlendValueAdd = 'Add';
+const BlendValueMultiply = 'Multiply';
+const BlendValueOverwrite = 'Overwrite';
+const DefaultFadeTime = 1.0;
+
+/**
+ * 表情のモーション
+ *
+ * 表情のモーションクラス。
+ */
+export class CubismExpressionMotion extends ACubismMotion {
+ /**
+ * インスタンスを作成する。
+ * @param buffer expファイルが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @return 作成されたインスタンス
+ */
+ public static create(
+ buffer: ArrayBuffer,
+ size: number
+ ): CubismExpressionMotion {
+ const expression: CubismExpressionMotion = new CubismExpressionMotion();
+
+ const json: CubismJson = CubismJson.create(buffer, size);
+ const root: Value = json.getRoot();
+
+ expression.setFadeInTime(
+ root.getValueByString(ExpressionKeyFadeIn).toFloat(DefaultFadeTime)
+ ); // フェードイン
+ expression.setFadeOutTime(
+ root.getValueByString(ExpressionKeyFadeOut).toFloat(DefaultFadeTime)
+ ); // フェードアウト
+
+ // 各パラメータについて
+ const parameterCount = root
+ .getValueByString(ExpressionKeyParameters)
+ .getSize();
+ expression._parameters.prepareCapacity(parameterCount);
+
+ for (let i = 0; i < parameterCount; ++i) {
+ const param: Value = root
+ .getValueByString(ExpressionKeyParameters)
+ .getValueByIndex(i);
+ const parameterId: CubismIdHandle = CubismFramework.getIdManager().getId(
+ param.getValueByString(ExpressionKeyId).getRawString()
+ ); // パラメータID
+
+ const value: number = param
+ .getValueByString(ExpressionKeyValue)
+ .toFloat(); // 値
+
+ // 計算方法の設定
+ let blendType: ExpressionBlendType;
+
+ if (
+ param.getValueByString(ExpressionKeyBlend).isNull() ||
+ param.getValueByString(ExpressionKeyBlend).getString() == BlendValueAdd
+ ) {
+ blendType = ExpressionBlendType.ExpressionBlendType_Add;
+ } else if (
+ param.getValueByString(ExpressionKeyBlend).getString() ==
+ BlendValueMultiply
+ ) {
+ blendType = ExpressionBlendType.ExpressionBlendType_Multiply;
+ } else if (
+ param.getValueByString(ExpressionKeyBlend).getString() ==
+ BlendValueOverwrite
+ ) {
+ blendType = ExpressionBlendType.ExpressionBlendType_Overwrite;
+ } else {
+ // その他 仕様にない値を設定した時は加算モードにすることで復旧
+ blendType = ExpressionBlendType.ExpressionBlendType_Add;
+ }
+
+ // 設定オブジェクトを作成してリストに追加する
+ const item: ExpressionParameter = new ExpressionParameter();
+
+ item.parameterId = parameterId;
+ item.blendType = blendType;
+ item.value = value;
+
+ expression._parameters.pushBack(item);
+ }
+
+ CubismJson.delete(json); // JSONデータは不要になったら削除する
+ return expression;
+ }
+
+ /**
+ * モデルのパラメータの更新の実行
+ * @param model 対象のモデル
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ * @param weight モーションの重み
+ * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
+ */
+ public doUpdateParameters(
+ model: CubismModel,
+ userTimeSeconds: number,
+ weight: number,
+ motionQueueEntry: CubismMotionQueueEntry
+ ): void {
+ for (let i = 0; i < this._parameters.getSize(); ++i) {
+ const parameter: ExpressionParameter = this._parameters.at(i);
+
+ switch (parameter.blendType) {
+ case ExpressionBlendType.ExpressionBlendType_Add: {
+ model.addParameterValueById(
+ parameter.parameterId,
+ parameter.value,
+ weight
+ );
+ break;
+ }
+ case ExpressionBlendType.ExpressionBlendType_Multiply: {
+ model.multiplyParameterValueById(
+ parameter.parameterId,
+ parameter.value,
+ weight
+ );
+ break;
+ }
+ case ExpressionBlendType.ExpressionBlendType_Overwrite: {
+ model.setParameterValueById(
+ parameter.parameterId,
+ parameter.value,
+ weight
+ );
+ break;
+ }
+ default:
+ // 仕様にない値を設定した時はすでに加算モードになっている
+ break;
+ }
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ constructor() {
+ super();
+
+ this._parameters = new csmVector();
+ }
+
+ _parameters: csmVector; // 表情のパラメータ情報リスト
+}
+
+/**
+ * 表情パラメータ値の計算方式
+ */
+export enum ExpressionBlendType {
+ ExpressionBlendType_Add = 0, // 加算
+ ExpressionBlendType_Multiply = 1, // 乗算
+ ExpressionBlendType_Overwrite = 2 // 上書き
+}
+
+/**
+ * 表情のパラメータ情報
+ */
+export class ExpressionParameter {
+ parameterId: CubismIdHandle; // パラメータID
+ blendType: ExpressionBlendType; // パラメータの演算種類
+ value: number; // 値
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismexpressionmotion';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismExpressionMotion = $.CubismExpressionMotion;
+ export type CubismExpressionMotion = $.CubismExpressionMotion;
+ export const ExpressionBlendType = $.ExpressionBlendType;
+ export type ExpressionBlendType = $.ExpressionBlendType;
+ export const ExpressionParameter = $.ExpressionParameter;
+ export type ExpressionParameter = $.ExpressionParameter;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotion.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotion.ts
new file mode 100644
index 000000000..e690c52b0
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotion.ts
@@ -0,0 +1,1060 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismMath } from '../math/cubismmath';
+import { CubismModel } from '../model/cubismmodel';
+import { csmString } from '../type/csmstring';
+import { csmVector } from '../type/csmvector';
+import {
+ CSM_ASSERT,
+ CubismLogDebug,
+ CubismLogWarning
+} from '../utils/cubismdebug';
+import { ACubismMotion, FinishedMotionCallback } from './acubismmotion';
+import {
+ CubismMotionCurve,
+ CubismMotionCurveTarget,
+ CubismMotionData,
+ CubismMotionEvent,
+ CubismMotionPoint,
+ CubismMotionSegment,
+ CubismMotionSegmentType
+} from './cubismmotioninternal';
+import { CubismMotionJson, EvaluationOptionFlag } from './cubismmotionjson';
+import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
+
+const EffectNameEyeBlink = 'EyeBlink';
+const EffectNameLipSync = 'LipSync';
+const TargetNameModel = 'Model';
+const TargetNameParameter = 'Parameter';
+const TargetNamePartOpacity = 'PartOpacity';
+
+/**
+ * Cubism SDK R2 以前のモーションを再現させるなら true 、アニメータのモーションを正しく再現するなら false 。
+ */
+const UseOldBeziersCurveMotion = false;
+
+function lerpPoints(
+ a: CubismMotionPoint,
+ b: CubismMotionPoint,
+ t: number
+): CubismMotionPoint {
+ const result: CubismMotionPoint = new CubismMotionPoint();
+
+ result.time = a.time + (b.time - a.time) * t;
+ result.value = a.value + (b.value - a.value) * t;
+
+ return result;
+}
+
+function linearEvaluate(points: CubismMotionPoint[], time: number): number {
+ let t: number = (time - points[0].time) / (points[1].time - points[0].time);
+
+ if (t < 0.0) {
+ t = 0.0;
+ }
+
+ return points[0].value + (points[1].value - points[0].value) * t;
+}
+
+function bezierEvaluate(points: CubismMotionPoint[], time: number): number {
+ let t: number = (time - points[0].time) / (points[3].time - points[0].time);
+
+ if (t < 0.0) {
+ t = 0.0;
+ }
+
+ const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t);
+ const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t);
+ const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t);
+
+ const p012: CubismMotionPoint = lerpPoints(p01, p12, t);
+ const p123: CubismMotionPoint = lerpPoints(p12, p23, t);
+
+ return lerpPoints(p012, p123, t).value;
+}
+
+function bezierEvaluateBinarySearch(
+ points: CubismMotionPoint[],
+ time: number
+): number {
+ const x_error = 0.01;
+
+ const x: number = time;
+ let x1: number = points[0].time;
+ let x2: number = points[3].time;
+ let cx1: number = points[1].time;
+ let cx2: number = points[2].time;
+
+ let ta = 0.0;
+ let tb = 1.0;
+ let t = 0.0;
+ let i = 0;
+
+ for (let var33 = true; i < 20; ++i) {
+ if (x < x1 + x_error) {
+ t = ta;
+ break;
+ }
+
+ if (x2 - x_error < x) {
+ t = tb;
+ break;
+ }
+
+ let centerx: number = (cx1 + cx2) * 0.5;
+ cx1 = (x1 + cx1) * 0.5;
+ cx2 = (x2 + cx2) * 0.5;
+ const ctrlx12: number = (cx1 + centerx) * 0.5;
+ const ctrlx21: number = (cx2 + centerx) * 0.5;
+ centerx = (ctrlx12 + ctrlx21) * 0.5;
+ if (x < centerx) {
+ tb = (ta + tb) * 0.5;
+ if (centerx - x_error < x) {
+ t = tb;
+ break;
+ }
+
+ x2 = centerx;
+ cx2 = ctrlx12;
+ } else {
+ ta = (ta + tb) * 0.5;
+ if (x < centerx + x_error) {
+ t = ta;
+ break;
+ }
+
+ x1 = centerx;
+ cx1 = ctrlx21;
+ }
+ }
+
+ if (i == 20) {
+ t = (ta + tb) * 0.5;
+ }
+
+ if (t < 0.0) {
+ t = 0.0;
+ }
+ if (t > 1.0) {
+ t = 1.0;
+ }
+
+ const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t);
+ const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t);
+ const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t);
+
+ const p012: CubismMotionPoint = lerpPoints(p01, p12, t);
+ const p123: CubismMotionPoint = lerpPoints(p12, p23, t);
+
+ return lerpPoints(p012, p123, t).value;
+}
+
+function bezierEvaluateCardanoInterpretation(
+ points: CubismMotionPoint[],
+ time: number
+): number {
+ const x: number = time;
+ const x1: number = points[0].time;
+ const x2: number = points[3].time;
+ const cx1: number = points[1].time;
+ const cx2: number = points[2].time;
+
+ const a: number = x2 - 3.0 * cx2 + 3.0 * cx1 - x1;
+ const b: number = 3.0 * cx2 - 6.0 * cx1 + 3.0 * x1;
+ const c: number = 3.0 * cx1 - 3.0 * x1;
+ const d: number = x1 - x;
+
+ const t: number = CubismMath.cardanoAlgorithmForBezier(a, b, c, d);
+
+ const p01: CubismMotionPoint = lerpPoints(points[0], points[1], t);
+ const p12: CubismMotionPoint = lerpPoints(points[1], points[2], t);
+ const p23: CubismMotionPoint = lerpPoints(points[2], points[3], t);
+
+ const p012: CubismMotionPoint = lerpPoints(p01, p12, t);
+ const p123: CubismMotionPoint = lerpPoints(p12, p23, t);
+
+ return lerpPoints(p012, p123, t).value;
+}
+
+function steppedEvaluate(points: CubismMotionPoint[], time: number): number {
+ return points[0].value;
+}
+
+function inverseSteppedEvaluate(
+ points: CubismMotionPoint[],
+ time: number
+): number {
+ return points[1].value;
+}
+
+function evaluateCurve(
+ motionData: CubismMotionData,
+ index: number,
+ time: number
+): number {
+ // Find segment to evaluate.
+ const curve: CubismMotionCurve = motionData.curves.at(index);
+
+ let target = -1;
+ const totalSegmentCount: number = curve.baseSegmentIndex + curve.segmentCount;
+ let pointPosition = 0;
+ for (let i: number = curve.baseSegmentIndex; i < totalSegmentCount; ++i) {
+ // Get first point of next segment.
+ pointPosition =
+ motionData.segments.at(i).basePointIndex +
+ (motionData.segments.at(i).segmentType ==
+ CubismMotionSegmentType.CubismMotionSegmentType_Bezier
+ ? 3
+ : 1);
+
+ // Break if time lies within current segment.
+ if (motionData.points.at(pointPosition).time > time) {
+ target = i;
+ break;
+ }
+ }
+
+ if (target == -1) {
+ return motionData.points.at(pointPosition).value;
+ }
+
+ const segment: CubismMotionSegment = motionData.segments.at(target);
+
+ return segment.evaluate(motionData.points.get(segment.basePointIndex), time);
+}
+
+/**
+ * モーションクラス
+ *
+ * モーションのクラス。
+ */
+export class CubismMotion extends ACubismMotion {
+ /**
+ * インスタンスを作成する
+ *
+ * @param buffer motion3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @param onFinishedMotionHandler モーション再生終了時に呼び出されるコールバック関数
+ * @return 作成されたインスタンス
+ */
+ public static create(
+ buffer: ArrayBuffer,
+ size: number,
+ onFinishedMotionHandler?: FinishedMotionCallback
+ ): CubismMotion {
+ const ret = new CubismMotion();
+
+ ret.parse(buffer, size);
+ ret._sourceFrameRate = ret._motionData.fps;
+ ret._loopDurationSeconds = ret._motionData.duration;
+ ret._onFinishedMotion = onFinishedMotionHandler;
+
+ // NOTE: Editorではループありのモーション書き出しは非対応
+ // ret->_loop = (ret->_motionData->Loop > 0);
+ return ret;
+ }
+
+ /**
+ * モデルのパラメータの更新の実行
+ * @param model 対象のモデル
+ * @param userTimeSeconds 現在の時刻[秒]
+ * @param fadeWeight モーションの重み
+ * @param motionQueueEntry CubismMotionQueueManagerで管理されているモーション
+ */
+ public doUpdateParameters(
+ model: CubismModel,
+ userTimeSeconds: number,
+ fadeWeight: number,
+ motionQueueEntry: CubismMotionQueueEntry
+ ): void {
+ if (this._modelCurveIdEyeBlink == null) {
+ this._modelCurveIdEyeBlink = CubismFramework.getIdManager().getId(
+ EffectNameEyeBlink
+ );
+ }
+
+ if (this._modelCurveIdLipSync == null) {
+ this._modelCurveIdLipSync = CubismFramework.getIdManager().getId(
+ EffectNameLipSync
+ );
+ }
+
+ let timeOffsetSeconds: number =
+ userTimeSeconds - motionQueueEntry.getStartTime();
+
+ if (timeOffsetSeconds < 0.0) {
+ timeOffsetSeconds = 0.0; // エラー回避
+ }
+
+ let lipSyncValue: number = Number.MAX_VALUE;
+ let eyeBlinkValue: number = Number.MAX_VALUE;
+
+ //まばたき、リップシンクのうちモーションの適用を検出するためのビット(maxFlagCount個まで
+ const MaxTargetSize = 64;
+ let lipSyncFlags = 0;
+ let eyeBlinkFlags = 0;
+
+ //瞬き、リップシンクのターゲット数が上限を超えている場合
+ if (this._eyeBlinkParameterIds.getSize() > MaxTargetSize) {
+ CubismLogDebug(
+ 'too many eye blink targets : {0}',
+ this._eyeBlinkParameterIds.getSize()
+ );
+ }
+ if (this._lipSyncParameterIds.getSize() > MaxTargetSize) {
+ CubismLogDebug(
+ 'too many lip sync targets : {0}',
+ this._lipSyncParameterIds.getSize()
+ );
+ }
+
+ const tmpFadeIn: number =
+ this._fadeInSeconds <= 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
+ this._fadeInSeconds
+ );
+
+ const tmpFadeOut: number =
+ this._fadeOutSeconds <= 0.0 || motionQueueEntry.getEndTime() < 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (motionQueueEntry.getEndTime() - userTimeSeconds) /
+ this._fadeOutSeconds
+ );
+ let value: number;
+ let c: number, parameterIndex: number;
+
+ // 'Repeat' time as necessary.
+ let time: number = timeOffsetSeconds;
+
+ if (this._isLoop) {
+ while (time > this._motionData.duration) {
+ time -= this._motionData.duration;
+ }
+ }
+
+ const curves: csmVector = this._motionData.curves;
+
+ // Evaluate model curves.
+ for (
+ c = 0;
+ c < this._motionData.curveCount &&
+ curves.at(c).type ==
+ CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
+ ++c
+ ) {
+ // Evaluate curve and call handler.
+ value = evaluateCurve(this._motionData, c, time);
+
+ if (curves.at(c).id == this._modelCurveIdEyeBlink) {
+ eyeBlinkValue = value;
+ } else if (curves.at(c).id == this._modelCurveIdLipSync) {
+ lipSyncValue = value;
+ }
+ }
+
+ let parameterMotionCurveCount = 0;
+
+ for (
+ ;
+ c < this._motionData.curveCount &&
+ curves.at(c).type ==
+ CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter;
+ ++c
+ ) {
+ parameterMotionCurveCount++;
+
+ // Find parameter index.
+ parameterIndex = model.getParameterIndex(curves.at(c).id);
+
+ // Skip curve evaluation if no value in sink.
+ if (parameterIndex == -1) {
+ continue;
+ }
+
+ const sourceValue: number = model.getParameterValueByIndex(
+ parameterIndex
+ );
+
+ // Evaluate curve and apply value.
+ value = evaluateCurve(this._motionData, c, time);
+
+ if (eyeBlinkValue != Number.MAX_VALUE) {
+ for (
+ let i = 0;
+ i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize;
+ ++i
+ ) {
+ if (this._eyeBlinkParameterIds.at(i) == curves.at(c).id) {
+ value *= eyeBlinkValue;
+ eyeBlinkFlags |= 1 << i;
+ break;
+ }
+ }
+ }
+
+ if (lipSyncValue != Number.MAX_VALUE) {
+ for (
+ let i = 0;
+ i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize;
+ ++i
+ ) {
+ if (this._lipSyncParameterIds.at(i) == curves.at(c).id) {
+ value += lipSyncValue;
+ lipSyncFlags |= 1 << i;
+ break;
+ }
+ }
+ }
+
+ let v: number;
+
+ // パラメータごとのフェード
+ if (curves.at(c).fadeInTime < 0.0 && curves.at(c).fadeOutTime < 0.0) {
+ // モーションのフェードを適用
+ v = sourceValue + (value - sourceValue) * fadeWeight;
+ } else {
+ // パラメータに対してフェードインかフェードアウトが設定してある場合はそちらを適用
+ let fin: number;
+ let fout: number;
+
+ if (curves.at(c).fadeInTime < 0.0) {
+ fin = tmpFadeIn;
+ } else {
+ fin =
+ curves.at(c).fadeInTime == 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (userTimeSeconds - motionQueueEntry.getFadeInStartTime()) /
+ curves.at(c).fadeInTime
+ );
+ }
+
+ if (curves.at(c).fadeOutTime < 0.0) {
+ fout = tmpFadeOut;
+ } else {
+ fout =
+ curves.at(c).fadeOutTime == 0.0 ||
+ motionQueueEntry.getEndTime() < 0.0
+ ? 1.0
+ : CubismMath.getEasingSine(
+ (motionQueueEntry.getEndTime() - userTimeSeconds) /
+ curves.at(c).fadeOutTime
+ );
+ }
+
+ const paramWeight: number = this._weight * fin * fout;
+
+ // パラメータごとのフェードを適用
+ v = sourceValue + (value - sourceValue) * paramWeight;
+ }
+
+ model.setParameterValueByIndex(parameterIndex, v, 1.0);
+ }
+
+ {
+ if (eyeBlinkValue != Number.MAX_VALUE) {
+ for (
+ let i = 0;
+ i < this._eyeBlinkParameterIds.getSize() && i < MaxTargetSize;
+ ++i
+ ) {
+ const sourceValue: number = model.getParameterValueById(
+ this._eyeBlinkParameterIds.at(i)
+ );
+
+ // モーションでの上書きがあった時にはまばたきは適用しない
+ if ((eyeBlinkFlags >> i) & 0x01) {
+ continue;
+ }
+
+ const v: number =
+ sourceValue + (eyeBlinkValue - sourceValue) * fadeWeight;
+
+ model.setParameterValueById(this._eyeBlinkParameterIds.at(i), v);
+ }
+ }
+
+ if (lipSyncValue != Number.MAX_VALUE) {
+ for (
+ let i = 0;
+ i < this._lipSyncParameterIds.getSize() && i < MaxTargetSize;
+ ++i
+ ) {
+ const sourceValue: number = model.getParameterValueById(
+ this._lipSyncParameterIds.at(i)
+ );
+
+ // モーションでの上書きがあった時にはリップシンクは適用しない
+ if ((lipSyncFlags >> i) & 0x01) {
+ continue;
+ }
+
+ const v: number =
+ sourceValue + (lipSyncValue - sourceValue) * fadeWeight;
+
+ model.setParameterValueById(this._lipSyncParameterIds.at(i), v);
+ }
+ }
+ }
+
+ for (
+ ;
+ c < this._motionData.curveCount &&
+ curves.at(c).type ==
+ CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity;
+ ++c
+ ) {
+ // Find parameter index.
+ parameterIndex = model.getParameterIndex(curves.at(c).id);
+
+ // Skip curve evaluation if no value in sink.
+ if (parameterIndex == -1) {
+ continue;
+ }
+
+ // Evaluate curve and apply value.
+ value = evaluateCurve(this._motionData, c, time);
+
+ model.setParameterValueByIndex(parameterIndex, value);
+ }
+
+ if (timeOffsetSeconds >= this._motionData.duration) {
+ if (this._isLoop) {
+ motionQueueEntry.setStartTime(userTimeSeconds); // 最初の状態へ
+ if (this._isLoopFadeIn) {
+ // ループ内でループ用フェードインが有効の時は、フェードイン設定し直し
+ motionQueueEntry.setFadeInStartTime(userTimeSeconds);
+ }
+ } else {
+ if (this._onFinishedMotion) {
+ this._onFinishedMotion(this);
+ }
+
+ motionQueueEntry.setIsFinished(true);
+ }
+ }
+ this._lastWeight = fadeWeight;
+ }
+
+ /**
+ * ループ情報の設定
+ * @param loop ループ情報
+ */
+ public setIsLoop(loop: boolean): void {
+ this._isLoop = loop;
+ }
+
+ /**
+ * ループ情報の取得
+ * @return true ループする
+ * @return false ループしない
+ */
+ public isLoop(): boolean {
+ return this._isLoop;
+ }
+
+ /**
+ * ループ時のフェードイン情報の設定
+ * @param loopFadeIn ループ時のフェードイン情報
+ */
+ public setIsLoopFadeIn(loopFadeIn: boolean): void {
+ this._isLoopFadeIn = loopFadeIn;
+ }
+
+ /**
+ * ループ時のフェードイン情報の取得
+ *
+ * @return true する
+ * @return false しない
+ */
+ public isLoopFadeIn(): boolean {
+ return this._isLoopFadeIn;
+ }
+
+ /**
+ * モーションの長さを取得する。
+ *
+ * @return モーションの長さ[秒]
+ */
+ public getDuration(): number {
+ return this._isLoop ? -1.0 : this._loopDurationSeconds;
+ }
+
+ /**
+ * モーションのループ時の長さを取得する。
+ *
+ * @return モーションのループ時の長さ[秒]
+ */
+ public getLoopDuration(): number {
+ return this._loopDurationSeconds;
+ }
+
+ /**
+ * パラメータに対するフェードインの時間を設定する。
+ *
+ * @param parameterId パラメータID
+ * @param value フェードインにかかる時間[秒]
+ */
+ public setParameterFadeInTime(
+ parameterId: CubismIdHandle,
+ value: number
+ ): void {
+ const curves: csmVector = this._motionData.curves;
+
+ for (let i = 0; i < this._motionData.curveCount; ++i) {
+ if (parameterId == curves.at(i).id) {
+ curves.at(i).fadeInTime = value;
+ return;
+ }
+ }
+ }
+
+ /**
+ * パラメータに対するフェードアウトの時間の設定
+ * @param parameterId パラメータID
+ * @param value フェードアウトにかかる時間[秒]
+ */
+ public setParameterFadeOutTime(
+ parameterId: CubismIdHandle,
+ value: number
+ ): void {
+ const curves: csmVector = this._motionData.curves;
+
+ for (let i = 0; i < this._motionData.curveCount; ++i) {
+ if (parameterId == curves.at(i).id) {
+ curves.at(i).fadeOutTime = value;
+ return;
+ }
+ }
+ }
+
+ /**
+ * パラメータに対するフェードインの時間の取得
+ * @param parameterId パラメータID
+ * @return フェードインにかかる時間[秒]
+ */
+ public getParameterFadeInTime(parameterId: CubismIdHandle): number {
+ const curves: csmVector = this._motionData.curves;
+
+ for (let i = 0; i < this._motionData.curveCount; ++i) {
+ if (parameterId == curves.at(i).id) {
+ return curves.at(i).fadeInTime;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * パラメータに対するフェードアウトの時間を取得
+ *
+ * @param parameterId パラメータID
+ * @return フェードアウトにかかる時間[秒]
+ */
+ public getParameterFadeOutTime(parameterId: CubismIdHandle): number {
+ const curves: csmVector = this._motionData.curves;
+
+ for (let i = 0; i < this._motionData.curveCount; ++i) {
+ if (parameterId == curves.at(i).id) {
+ return curves.at(i).fadeOutTime;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * 自動エフェクトがかかっているパラメータIDリストの設定
+ * @param eyeBlinkParameterIds 自動まばたきがかかっているパラメータIDのリスト
+ * @param lipSyncParameterIds リップシンクがかかっているパラメータIDのリスト
+ */
+ public setEffectIds(
+ eyeBlinkParameterIds: csmVector,
+ lipSyncParameterIds: csmVector
+ ): void {
+ this._eyeBlinkParameterIds = eyeBlinkParameterIds;
+ this._lipSyncParameterIds = lipSyncParameterIds;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ super();
+ this._sourceFrameRate = 30.0;
+ this._loopDurationSeconds = -1.0;
+ this._isLoop = false; // trueから false へデフォルトを変更
+ this._isLoopFadeIn = true; // ループ時にフェードインが有効かどうかのフラグ
+ this._lastWeight = 0.0;
+ this._motionData = null;
+ this._modelCurveIdEyeBlink = null;
+ this._modelCurveIdLipSync = null;
+ this._eyeBlinkParameterIds = null;
+ this._lipSyncParameterIds = null;
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this._motionData = void 0;
+ this._motionData = null;
+ }
+
+ /**
+ * motion3.jsonをパースする。
+ *
+ * @param motionJson motion3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public parse(motionJson: ArrayBuffer, size: number): void {
+ this._motionData = new CubismMotionData();
+
+ let json: CubismMotionJson = new CubismMotionJson(motionJson, size);
+
+ this._motionData.duration = json.getMotionDuration();
+ this._motionData.loop = json.isMotionLoop();
+ this._motionData.curveCount = json.getMotionCurveCount();
+ this._motionData.fps = json.getMotionFps();
+ this._motionData.eventCount = json.getEventCount();
+
+ const areBeziersRestructed: boolean = json.getEvaluationOptionFlag(
+ EvaluationOptionFlag.EvaluationOptionFlag_AreBeziersRistricted
+ );
+
+ if (json.isExistMotionFadeInTime()) {
+ this._fadeInSeconds =
+ json.getMotionFadeInTime() < 0.0 ? 1.0 : json.getMotionFadeInTime();
+ } else {
+ this._fadeInSeconds = 1.0;
+ }
+
+ if (json.isExistMotionFadeOutTime()) {
+ this._fadeOutSeconds =
+ json.getMotionFadeOutTime() < 0.0 ? 1.0 : json.getMotionFadeOutTime();
+ } else {
+ this._fadeOutSeconds = 1.0;
+ }
+
+ this._motionData.curves.updateSize(
+ this._motionData.curveCount,
+ CubismMotionCurve,
+ true
+ );
+ this._motionData.segments.updateSize(
+ json.getMotionTotalSegmentCount(),
+ CubismMotionSegment,
+ true
+ );
+ this._motionData.points.updateSize(
+ json.getMotionTotalPointCount(),
+ CubismMotionPoint,
+ true
+ );
+ this._motionData.events.updateSize(
+ this._motionData.eventCount,
+ CubismMotionEvent,
+ true
+ );
+
+ let totalPointCount = 0;
+ let totalSegmentCount = 0;
+
+ // Curves
+ for (
+ let curveCount = 0;
+ curveCount < this._motionData.curveCount;
+ ++curveCount
+ ) {
+ if (json.getMotionCurveTarget(curveCount) == TargetNameModel) {
+ this._motionData.curves.at(curveCount).type =
+ CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
+ } else if (json.getMotionCurveTarget(curveCount) == TargetNameParameter) {
+ this._motionData.curves.at(curveCount).type =
+ CubismMotionCurveTarget.CubismMotionCurveTarget_Parameter;
+ } else if (
+ json.getMotionCurveTarget(curveCount) == TargetNamePartOpacity
+ ) {
+ this._motionData.curves.at(curveCount).type =
+ CubismMotionCurveTarget.CubismMotionCurveTarget_PartOpacity;
+ } else {
+ CubismLogWarning(
+ 'Warning : Unable to get segment type from Curve! The number of "CurveCount" may be incorrect!'
+ );
+ }
+
+ this._motionData.curves.at(curveCount).id = json.getMotionCurveId(
+ curveCount
+ );
+
+ this._motionData.curves.at(
+ curveCount
+ ).baseSegmentIndex = totalSegmentCount;
+
+ this._motionData.curves.at(
+ curveCount
+ ).fadeInTime = json.isExistMotionCurveFadeInTime(curveCount)
+ ? json.getMotionCurveFadeInTime(curveCount)
+ : -1.0;
+ this._motionData.curves.at(
+ curveCount
+ ).fadeOutTime = json.isExistMotionCurveFadeOutTime(curveCount)
+ ? json.getMotionCurveFadeOutTime(curveCount)
+ : -1.0;
+
+ // Segments
+ for (
+ let segmentPosition = 0;
+ segmentPosition < json.getMotionCurveSegmentCount(curveCount);
+
+ ) {
+ if (segmentPosition == 0) {
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).basePointIndex = totalPointCount;
+
+ this._motionData.points.at(
+ totalPointCount
+ ).time = json.getMotionCurveSegment(curveCount, segmentPosition);
+ this._motionData.points.at(
+ totalPointCount
+ ).value = json.getMotionCurveSegment(curveCount, segmentPosition + 1);
+
+ totalPointCount += 1;
+ segmentPosition += 2;
+ } else {
+ this._motionData.segments.at(totalSegmentCount).basePointIndex =
+ totalPointCount - 1;
+ }
+
+ const segment: number = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition
+ );
+ switch (segment) {
+ case CubismMotionSegmentType.CubismMotionSegmentType_Linear: {
+ this._motionData.segments.at(totalSegmentCount).segmentType =
+ CubismMotionSegmentType.CubismMotionSegmentType_Linear;
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).evaluate = linearEvaluate;
+
+ this._motionData.points.at(
+ totalPointCount
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 1
+ );
+ this._motionData.points.at(
+ totalPointCount
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 2
+ );
+
+ totalPointCount += 1;
+ segmentPosition += 3;
+
+ break;
+ }
+ case CubismMotionSegmentType.CubismMotionSegmentType_Bezier: {
+ this._motionData.segments.at(totalSegmentCount).segmentType =
+ CubismMotionSegmentType.CubismMotionSegmentType_Bezier;
+
+ if (areBeziersRestructed || UseOldBeziersCurveMotion) {
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).evaluate = bezierEvaluate;
+ } else {
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).evaluate = bezierEvaluateCardanoInterpretation;
+ }
+
+ this._motionData.points.at(
+ totalPointCount
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 1
+ );
+ this._motionData.points.at(
+ totalPointCount
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 2
+ );
+
+ this._motionData.points.at(
+ totalPointCount + 1
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 3
+ );
+ this._motionData.points.at(
+ totalPointCount + 1
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 4
+ );
+
+ this._motionData.points.at(
+ totalPointCount + 2
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 5
+ );
+ this._motionData.points.at(
+ totalPointCount + 2
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 6
+ );
+
+ totalPointCount += 3;
+ segmentPosition += 7;
+
+ break;
+ }
+
+ case CubismMotionSegmentType.CubismMotionSegmentType_Stepped: {
+ this._motionData.segments.at(totalSegmentCount).segmentType =
+ CubismMotionSegmentType.CubismMotionSegmentType_Stepped;
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).evaluate = steppedEvaluate;
+
+ this._motionData.points.at(
+ totalPointCount
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 1
+ );
+ this._motionData.points.at(
+ totalPointCount
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 2
+ );
+
+ totalPointCount += 1;
+ segmentPosition += 3;
+
+ break;
+ }
+
+ case CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped: {
+ this._motionData.segments.at(totalSegmentCount).segmentType =
+ CubismMotionSegmentType.CubismMotionSegmentType_InverseStepped;
+ this._motionData.segments.at(
+ totalSegmentCount
+ ).evaluate = inverseSteppedEvaluate;
+
+ this._motionData.points.at(
+ totalPointCount
+ ).time = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 1
+ );
+ this._motionData.points.at(
+ totalPointCount
+ ).value = json.getMotionCurveSegment(
+ curveCount,
+ segmentPosition + 2
+ );
+
+ totalPointCount += 1;
+ segmentPosition += 3;
+
+ break;
+ }
+ default: {
+ CSM_ASSERT(0);
+ break;
+ }
+ }
+
+ ++this._motionData.curves.at(curveCount).segmentCount;
+ ++totalSegmentCount;
+ }
+ }
+
+ for (
+ let userdatacount = 0;
+ userdatacount < json.getEventCount();
+ ++userdatacount
+ ) {
+ this._motionData.events.at(userdatacount).fireTime = json.getEventTime(
+ userdatacount
+ );
+ this._motionData.events.at(userdatacount).value = json.getEventValue(
+ userdatacount
+ );
+ }
+
+ json.release();
+ json = void 0;
+ json = null;
+ }
+
+ /**
+ * モデルのパラメータ更新
+ *
+ * イベント発火のチェック。
+ * 入力する時間は呼ばれるモーションタイミングを0とした秒数で行う。
+ *
+ * @param beforeCheckTimeSeconds 前回のイベントチェック時間[秒]
+ * @param motionTimeSeconds 今回の再生時間[秒]
+ */
+ public getFiredEvent(
+ beforeCheckTimeSeconds: number,
+ motionTimeSeconds: number
+ ): csmVector {
+ this._firedEventValues.updateSize(0);
+
+ // イベントの発火チェック
+ for (let u = 0; u < this._motionData.eventCount; ++u) {
+ if (
+ this._motionData.events.at(u).fireTime > beforeCheckTimeSeconds &&
+ this._motionData.events.at(u).fireTime <= motionTimeSeconds
+ ) {
+ this._firedEventValues.pushBack(
+ new csmString(this._motionData.events.at(u).value.s)
+ );
+ }
+ }
+
+ return this._firedEventValues;
+ }
+
+ public _sourceFrameRate: number; // ロードしたファイルのFPS。記述が無ければデフォルト値15fpsとなる
+ public _loopDurationSeconds: number; // mtnファイルで定義される一連のモーションの長さ
+ public _isLoop: boolean; // ループするか?
+ public _isLoopFadeIn: boolean; // ループ時にフェードインが有効かどうかのフラグ。初期値では有効。
+ public _lastWeight: number; // 最後に設定された重み
+
+ public _motionData: CubismMotionData; // 実際のモーションデータ本体
+
+ public _eyeBlinkParameterIds: csmVector; // 自動まばたきを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。
+ public _lipSyncParameterIds: csmVector; // リップシンクを適用するパラメータIDハンドルのリスト。 モデル(モデルセッティング)とパラメータを対応付ける。
+
+ public _modelCurveIdEyeBlink: CubismIdHandle; // モデルが持つ自動まばたき用パラメータIDのハンドル。 モデルとモーションを対応付ける。
+ public _modelCurveIdLipSync: CubismIdHandle; // モデルが持つリップシンク用パラメータIDのハンドル。 モデルとモーションを対応付ける。
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotion';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotion = $.CubismMotion;
+ export type CubismMotion = $.CubismMotion;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotioninternal.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotioninternal.ts
new file mode 100644
index 000000000..8b48faa4c
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotioninternal.ts
@@ -0,0 +1,156 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { csmString } from '../type/csmstring';
+import { csmVector } from '../type/csmvector';
+
+/**
+ * @brief モーションカーブの種類
+ *
+ * モーションカーブの種類。
+ */
+export enum CubismMotionCurveTarget {
+ CubismMotionCurveTarget_Model, // モデルに対して
+ CubismMotionCurveTarget_Parameter, // パラメータに対して
+ CubismMotionCurveTarget_PartOpacity // パーツの不透明度に対して
+}
+
+/**
+ * @brief モーションカーブのセグメントの種類
+ *
+ * モーションカーブのセグメントの種類。
+ */
+export enum CubismMotionSegmentType {
+ CubismMotionSegmentType_Linear = 0, // リニア
+ CubismMotionSegmentType_Bezier = 1, // ベジェ曲線
+ CubismMotionSegmentType_Stepped = 2, // ステップ
+ CubismMotionSegmentType_InverseStepped = 3 // インバースステップ
+}
+
+/**
+ * @brief モーションカーブの制御点
+ *
+ * モーションカーブの制御点。
+ */
+export class CubismMotionPoint {
+ time = 0.0; // 時間[秒]
+ value = 0.0; // 値
+}
+
+/**
+ * モーションカーブのセグメントの評価関数
+ *
+ * @param points モーションカーブの制御点リスト
+ * @param time 評価する時間[秒]
+ */
+export interface csmMotionSegmentEvaluationFunction {
+ (points: CubismMotionPoint[], time: number): number;
+}
+
+/**
+ * @brief モーションカーブのセグメント
+ *
+ * モーションカーブのセグメント。
+ */
+export class CubismMotionSegment {
+ /**
+ * @brief コンストラクタ
+ *
+ * コンストラクタ。
+ */
+ public constructor() {
+ this.evaluate = null;
+ this.basePointIndex = 0;
+ this.segmentType = 0;
+ }
+
+ evaluate: csmMotionSegmentEvaluationFunction; // 使用する評価関数
+ basePointIndex: number; // 最初のセグメントへのインデックス
+ segmentType: number; // セグメントの種類
+}
+
+/**
+ * @brief モーションカーブ
+ *
+ * モーションカーブ。
+ */
+export class CubismMotionCurve {
+ public constructor() {
+ this.type = CubismMotionCurveTarget.CubismMotionCurveTarget_Model;
+ this.segmentCount = 0;
+ this.baseSegmentIndex = 0;
+ this.fadeInTime = 0.0;
+ this.fadeOutTime = 0.0;
+ }
+
+ type: CubismMotionCurveTarget; // カーブの種類
+ id: CubismIdHandle; // カーブのID
+ segmentCount: number; // セグメントの個数
+ baseSegmentIndex: number; // 最初のセグメントのインデックス
+ fadeInTime: number; // フェードインにかかる時間[秒]
+ fadeOutTime: number; // フェードアウトにかかる時間[秒]
+}
+
+/**
+ * イベント。
+ */
+export class CubismMotionEvent {
+ fireTime = 0.0;
+ value: csmString;
+}
+
+/**
+ * @brief モーションデータ
+ *
+ * モーションデータ。
+ */
+export class CubismMotionData {
+ public constructor() {
+ this.duration = 0.0;
+ this.loop = false;
+ this.curveCount = 0;
+ this.eventCount = 0;
+ this.fps = 0.0;
+
+ this.curves = new csmVector();
+ this.segments = new csmVector();
+ this.points = new csmVector();
+ this.events = new csmVector();
+ }
+
+ duration: number; // モーションの長さ[秒]
+ loop: boolean; // ループするかどうか
+ curveCount: number; // カーブの個数
+ eventCount: number; // UserDataの個数
+ fps: number; // フレームレート
+ curves: csmVector; // カーブのリスト
+ segments: csmVector; // セグメントのリスト
+ points: csmVector; // ポイントのリスト
+ events: csmVector; // イベントのリスト
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotioninternal';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotionCurve = $.CubismMotionCurve;
+ export type CubismMotionCurve = $.CubismMotionCurve;
+ export const CubismMotionCurveTarget = $.CubismMotionCurveTarget;
+ export type CubismMotionCurveTarget = $.CubismMotionCurveTarget;
+ export const CubismMotionData = $.CubismMotionData;
+ export type CubismMotionData = $.CubismMotionData;
+ export const CubismMotionEvent = $.CubismMotionEvent;
+ export type CubismMotionEvent = $.CubismMotionEvent;
+ export const CubismMotionPoint = $.CubismMotionPoint;
+ export type CubismMotionPoint = $.CubismMotionPoint;
+ export const CubismMotionSegment = $.CubismMotionSegment;
+ export type CubismMotionSegment = $.CubismMotionSegment;
+ export const CubismMotionSegmentType = $.CubismMotionSegmentType;
+ export type CubismMotionSegmentType = $.CubismMotionSegmentType;
+ export type csmMotionSegmentEvaluationFunction = $.csmMotionSegmentEvaluationFunction;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionjson.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionjson.ts
new file mode 100644
index 000000000..eace15b12
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionjson.ts
@@ -0,0 +1,383 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { csmString } from '../type/csmstring';
+import { CubismJson } from '../utils/cubismjson';
+
+// JSON keys
+const Meta = 'Meta';
+const Duration = 'Duration';
+const Loop = 'Loop';
+const AreBeziersRestricted = 'AreBeziersRestricted';
+const CurveCount = 'CurveCount';
+const Fps = 'Fps';
+const TotalSegmentCount = 'TotalSegmentCount';
+const TotalPointCount = 'TotalPointCount';
+const Curves = 'Curves';
+const Target = 'Target';
+const Id = 'Id';
+const FadeInTime = 'FadeInTime';
+const FadeOutTime = 'FadeOutTime';
+const Segments = 'Segments';
+const UserData = 'UserData';
+const UserDataCount = 'UserDataCount';
+const TotalUserDataSize = 'TotalUserDataSize';
+const Time = 'Time';
+const Value = 'Value';
+
+/**
+ * motion3.jsonのコンテナ。
+ */
+export class CubismMotionJson {
+ /**
+ * コンストラクタ
+ * @param buffer motion3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public constructor(buffer: ArrayBuffer, size: number) {
+ this._json = CubismJson.create(buffer, size);
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ CubismJson.delete(this._json);
+ }
+
+ /**
+ * モーションの長さを取得する
+ * @return モーションの長さ[秒]
+ */
+ public getMotionDuration(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(Duration)
+ .toFloat();
+ }
+
+ /**
+ * モーションのループ情報の取得
+ * @return true ループする
+ * @return false ループしない
+ */
+ public isMotionLoop(): boolean {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(Loop)
+ .toBoolean();
+ }
+
+ public getEvaluationOptionFlag(flagType: number): boolean {
+ if (
+ EvaluationOptionFlag.EvaluationOptionFlag_AreBeziersRistricted == flagType
+ ) {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(AreBeziersRestricted)
+ .toBoolean();
+ }
+
+ return false;
+ }
+
+ /**
+ * モーションカーブの個数の取得
+ * @return モーションカーブの個数
+ */
+ public getMotionCurveCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(CurveCount)
+ .toInt();
+ }
+
+ /**
+ * モーションのフレームレートの取得
+ * @return フレームレート[FPS]
+ */
+ public getMotionFps(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(Fps)
+ .toFloat();
+ }
+
+ /**
+ * モーションのセグメントの総合計の取得
+ * @return モーションのセグメントの取得
+ */
+ public getMotionTotalSegmentCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalSegmentCount)
+ .toInt();
+ }
+
+ /**
+ * モーションのカーブの制御店の総合計の取得
+ * @return モーションのカーブの制御点の総合計
+ */
+ public getMotionTotalPointCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalPointCount)
+ .toInt();
+ }
+
+ /**
+ * モーションのフェードイン時間の存在
+ * @return true 存在する
+ * @return false 存在しない
+ */
+ public isExistMotionFadeInTime(): boolean {
+ return !this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(FadeInTime)
+ .isNull();
+ }
+
+ /**
+ * モーションのフェードアウト時間の存在
+ * @return true 存在する
+ * @return false 存在しない
+ */
+ public isExistMotionFadeOutTime(): boolean {
+ return !this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(FadeOutTime)
+ .isNull();
+ }
+
+ /**
+ * モーションのフェードイン時間の取得
+ * @return フェードイン時間[秒]
+ */
+ public getMotionFadeInTime(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(FadeInTime)
+ .toFloat();
+ }
+
+ /**
+ * モーションのフェードアウト時間の取得
+ * @return フェードアウト時間[秒]
+ */
+ public getMotionFadeOutTime(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(FadeOutTime)
+ .toFloat();
+ }
+
+ /**
+ * モーションのカーブの種類の取得
+ * @param curveIndex カーブのインデックス
+ * @return カーブの種類
+ */
+ public getMotionCurveTarget(curveIndex: number): string {
+ return this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(Target)
+ .getRawString();
+ }
+
+ /**
+ * モーションのカーブのIDの取得
+ * @param curveIndex カーブのインデックス
+ * @return カーブのID
+ */
+ public getMotionCurveId(curveIndex: number): CubismIdHandle {
+ return CubismFramework.getIdManager().getId(
+ this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(Id)
+ .getRawString()
+ );
+ }
+
+ /**
+ * モーションのカーブのフェードイン時間の存在
+ * @param curveIndex カーブのインデックス
+ * @return true 存在する
+ * @return false 存在しない
+ */
+ public isExistMotionCurveFadeInTime(curveIndex: number): boolean {
+ return !this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(FadeInTime)
+ .isNull();
+ }
+
+ /**
+ * モーションのカーブのフェードアウト時間の存在
+ * @param curveIndex カーブのインデックス
+ * @return true 存在する
+ * @return false 存在しない
+ */
+ public isExistMotionCurveFadeOutTime(curveIndex: number): boolean {
+ return !this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(FadeOutTime)
+ .isNull();
+ }
+
+ /**
+ * モーションのカーブのフェードイン時間の取得
+ * @param curveIndex カーブのインデックス
+ * @return フェードイン時間[秒]
+ */
+ public getMotionCurveFadeInTime(curveIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(FadeInTime)
+ .toFloat();
+ }
+
+ /**
+ * モーションのカーブのフェードアウト時間の取得
+ * @param curveIndex カーブのインデックス
+ * @return フェードアウト時間[秒]
+ */
+ public getMotionCurveFadeOutTime(curveIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(FadeOutTime)
+ .toFloat();
+ }
+
+ /**
+ * モーションのカーブのセグメントの個数を取得する
+ * @param curveIndex カーブのインデックス
+ * @return モーションのカーブのセグメントの個数
+ */
+ public getMotionCurveSegmentCount(curveIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(Segments)
+ .getVector()
+ .getSize();
+ }
+
+ /**
+ * モーションのカーブのセグメントの値の取得
+ * @param curveIndex カーブのインデックス
+ * @param segmentIndex セグメントのインデックス
+ * @return セグメントの値
+ */
+ public getMotionCurveSegment(
+ curveIndex: number,
+ segmentIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Curves)
+ .getValueByIndex(curveIndex)
+ .getValueByString(Segments)
+ .getValueByIndex(segmentIndex)
+ .toFloat();
+ }
+
+ /**
+ * イベントの個数の取得
+ * @return イベントの個数
+ */
+ public getEventCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(UserDataCount)
+ .toInt();
+ }
+
+ /**
+ * イベントの総文字数の取得
+ * @return イベントの総文字数
+ */
+ public getTotalEventValueSize(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalUserDataSize)
+ .toInt();
+ }
+
+ /**
+ * イベントの時間の取得
+ * @param userDataIndex イベントのインデックス
+ * @return イベントの時間[秒]
+ */
+ public getEventTime(userDataIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(UserData)
+ .getValueByIndex(userDataIndex)
+ .getValueByString(Time)
+ .toFloat();
+ }
+
+ /**
+ * イベントの取得
+ * @param userDataIndex イベントのインデックス
+ * @return イベントの文字列
+ */
+ public getEventValue(userDataIndex: number): csmString {
+ return new csmString(
+ this._json
+ .getRoot()
+ .getValueByString(UserData)
+ .getValueByIndex(userDataIndex)
+ .getValueByString(Value)
+ .getRawString()
+ );
+ }
+
+ _json: CubismJson; // motion3.jsonのデータ
+}
+
+/**
+ * @brief ベジェカーブの解釈方法のフラグタイプ
+ */
+export enum EvaluationOptionFlag {
+ EvaluationOptionFlag_AreBeziersRistricted = 0 ///< ベジェハンドルの規制状態
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotionjson';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotionJson = $.CubismMotionJson;
+ export type CubismMotionJson = $.CubismMotionJson;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionmanager.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionmanager.ts
new file mode 100644
index 000000000..9d1ea486f
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionmanager.ts
@@ -0,0 +1,126 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismModel } from '../model/cubismmodel';
+import { ACubismMotion } from './acubismmotion';
+import {
+ CubismMotionQueueEntryHandle,
+ CubismMotionQueueManager
+} from './cubismmotionqueuemanager';
+
+/**
+ * モーションの管理
+ *
+ * モーションの管理を行うクラス
+ */
+export class CubismMotionManager extends CubismMotionQueueManager {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ super();
+ this._currentPriority = 0;
+ this._reservePriority = 0;
+ }
+
+ /**
+ * 再生中のモーションの優先度の取得
+ * @return モーションの優先度
+ */
+ public getCurrentPriority(): number {
+ return this._currentPriority;
+ }
+
+ /**
+ * 予約中のモーションの優先度を取得する。
+ * @return モーションの優先度
+ */
+ public getReservePriority(): number {
+ return this._reservePriority;
+ }
+
+ /**
+ * 予約中のモーションの優先度を設定する。
+ * @param val 優先度
+ */
+ public setReservePriority(val: number): void {
+ this._reservePriority = val;
+ }
+
+ /**
+ * 優先度を設定してモーションを開始する。
+ *
+ * @param motion モーション
+ * @param autoDelete 再生が狩猟したモーションのインスタンスを削除するならtrue
+ * @param priority 優先度
+ * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
+ */
+ public startMotionPriority(
+ motion: ACubismMotion,
+ autoDelete: boolean,
+ priority: number
+ ): CubismMotionQueueEntryHandle {
+ if (priority == this._reservePriority) {
+ this._reservePriority = 0; // 予約を解除
+ }
+
+ this._currentPriority = priority; // 再生中モーションの優先度を設定
+
+ return super.startMotion(motion, autoDelete, this._userTimeSeconds);
+ }
+
+ /**
+ * モーションを更新して、モデルにパラメータ値を反映する。
+ *
+ * @param model 対象のモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ * @return true 更新されている
+ * @return false 更新されていない
+ */
+ public updateMotion(model: CubismModel, deltaTimeSeconds: number): boolean {
+ this._userTimeSeconds += deltaTimeSeconds;
+
+ const updated: boolean = super.doUpdateMotion(model, this._userTimeSeconds);
+
+ if (this.isFinished()) {
+ this._currentPriority = 0; // 再生中のモーションの優先度を解除
+ }
+
+ return updated;
+ }
+
+ /**
+ * モーションを予約する。
+ *
+ * @param priority 優先度
+ * @return true 予約できた
+ * @return false 予約できなかった
+ */
+ public reserveMotion(priority: number): boolean {
+ if (
+ priority <= this._reservePriority ||
+ priority <= this._currentPriority
+ ) {
+ return false;
+ }
+
+ this._reservePriority = priority;
+
+ return true;
+ }
+
+ _currentPriority: number; // 現在再生中のモーションの優先度
+ _reservePriority: number; // 再生予定のモーションの優先度。再生中は0になる。モーションファイルを別スレッドで読み込むときの機能。
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotionmanager';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotionManager = $.CubismMotionManager;
+ export type CubismMotionManager = $.CubismMotionManager;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueueentry.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueueentry.ts
new file mode 100644
index 000000000..3fa75d572
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueueentry.ts
@@ -0,0 +1,253 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { ACubismMotion } from './acubismmotion';
+import { CubismMotionQueueEntryHandle } from './cubismmotionqueuemanager';
+
+/**
+ * CubismMotionQueueManagerで再生している各モーションの管理クラス。
+ */
+export class CubismMotionQueueEntry {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._autoDelete = false;
+ this._motion = null;
+ this._available = true;
+ this._finished = false;
+ this._started = false;
+ this._startTimeSeconds = -1.0;
+ this._fadeInStartTimeSeconds = 0.0;
+ this._endTimeSeconds = -1.0;
+ this._stateTimeSeconds = 0.0;
+ this._stateWeight = 0.0;
+ this._lastEventCheckSeconds = 0.0;
+ this._motionQueueEntryHandle = this;
+ this._fadeOutSeconds = 0.0;
+ this._isTriggeredFadeOut = false;
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ if (this._autoDelete && this._motion) {
+ ACubismMotion.delete(this._motion); //
+ }
+ }
+
+ /**
+ * フェードアウト時間と開始判定の設定
+ * @param fadeOutSeconds フェードアウトにかかる時間[秒]
+ */
+ public setFadeOut(fadeOutSeconds: number): void {
+ this._fadeOutSeconds = fadeOutSeconds;
+ this._isTriggeredFadeOut = true;
+ }
+
+ /**
+ * フェードアウトの開始
+ * @param fadeOutSeconds フェードアウトにかかる時間[秒]
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ */
+ public startFadeOut(fadeOutSeconds: number, userTimeSeconds: number): void {
+ const newEndTimeSeconds: number = userTimeSeconds + fadeOutSeconds;
+ this._isTriggeredFadeOut = true;
+
+ if (
+ this._endTimeSeconds < 0.0 ||
+ newEndTimeSeconds < this._endTimeSeconds
+ ) {
+ this._endTimeSeconds = newEndTimeSeconds;
+ }
+ }
+
+ /**
+ * モーションの終了の確認
+ *
+ * @return true モーションが終了した
+ * @return false 終了していない
+ */
+ public isFinished(): boolean {
+ return this._finished;
+ }
+
+ /**
+ * モーションの開始の確認
+ * @return true モーションが開始した
+ * @return false 開始していない
+ */
+ public isStarted(): boolean {
+ return this._started;
+ }
+
+ /**
+ * モーションの開始時刻の取得
+ * @return モーションの開始時刻[秒]
+ */
+ public getStartTime(): number {
+ return this._startTimeSeconds;
+ }
+
+ /**
+ * フェードインの開始時刻の取得
+ * @return フェードインの開始時刻[秒]
+ */
+ public getFadeInStartTime(): number {
+ return this._fadeInStartTimeSeconds;
+ }
+
+ /**
+ * フェードインの終了時刻の取得
+ * @return フェードインの終了時刻の取得
+ */
+ public getEndTime(): number {
+ return this._endTimeSeconds;
+ }
+
+ /**
+ * モーションの開始時刻の設定
+ * @param startTime モーションの開始時刻
+ */
+ public setStartTime(startTime: number): void {
+ this._startTimeSeconds = startTime;
+ }
+
+ /**
+ * フェードインの開始時刻の設定
+ * @param startTime フェードインの開始時刻[秒]
+ */
+ public setFadeInStartTime(startTime: number): void {
+ this._fadeInStartTimeSeconds = startTime;
+ }
+
+ /**
+ * フェードインの終了時刻の設定
+ * @param endTime フェードインの終了時刻[秒]
+ */
+ public setEndTime(endTime: number): void {
+ this._endTimeSeconds = endTime;
+ }
+
+ /**
+ * モーションの終了の設定
+ * @param f trueならモーションの終了
+ */
+ public setIsFinished(f: boolean): void {
+ this._finished = f;
+ }
+
+ /**
+ * モーション開始の設定
+ * @param f trueならモーションの開始
+ */
+ public setIsStarted(f: boolean): void {
+ this._started = f;
+ }
+
+ /**
+ * モーションの有効性の確認
+ * @return true モーションは有効
+ * @return false モーションは無効
+ */
+ public isAvailable(): boolean {
+ return this._available;
+ }
+
+ /**
+ * モーションの有効性の設定
+ * @param v trueならモーションは有効
+ */
+ public setIsAvailable(v: boolean): void {
+ this._available = v;
+ }
+
+ /**
+ * モーションの状態の設定
+ * @param timeSeconds 現在時刻[秒]
+ * @param weight モーション尾重み
+ */
+ public setState(timeSeconds: number, weight: number): void {
+ this._stateTimeSeconds = timeSeconds;
+ this._stateWeight = weight;
+ }
+
+ /**
+ * モーションの現在時刻の取得
+ * @return モーションの現在時刻[秒]
+ */
+ public getStateTime(): number {
+ return this._stateTimeSeconds;
+ }
+
+ /**
+ * モーションの重みの取得
+ * @return モーションの重み
+ */
+ public getStateWeight(): number {
+ return this._stateWeight;
+ }
+
+ /**
+ * 最後にイベントの発火をチェックした時間を取得
+ *
+ * @return 最後にイベントの発火をチェックした時間[秒]
+ */
+ public getLastCheckEventSeconds(): number {
+ return this._lastEventCheckSeconds;
+ }
+
+ /**
+ * 最後にイベントをチェックした時間を設定
+ * @param checkSeconds 最後にイベントをチェックした時間[秒]
+ */
+ public setLastCheckEventSeconds(checkSeconds: number): void {
+ this._lastEventCheckSeconds = checkSeconds;
+ }
+
+ /**
+ * フェードアウト開始判定の取得
+ * @return フェードアウト開始するかどうか
+ */
+ public isTriggeredFadeOut(): boolean {
+ return this._isTriggeredFadeOut;
+ }
+
+ /**
+ * フェードアウト時間の取得
+ * @return フェードアウト時間[秒]
+ */
+ public getFadeOutSeconds(): number {
+ return this._fadeOutSeconds;
+ }
+
+ _autoDelete: boolean; // 自動削除
+ _motion: ACubismMotion; // モーション
+
+ _available: boolean; // 有効化フラグ
+ _finished: boolean; // 終了フラグ
+ _started: boolean; // 開始フラグ
+ _startTimeSeconds: number; // モーション再生開始時刻[秒]
+ _fadeInStartTimeSeconds: number; // フェードイン開始時刻(ループの時は初回のみ)[秒]
+ _endTimeSeconds: number; // 終了予定時刻[秒]
+ _stateTimeSeconds: number; // 時刻の状態[秒]
+ _stateWeight: number; // 重みの状態
+ _lastEventCheckSeconds: number; // 最終のMotion側のチェックした時間
+ private _fadeOutSeconds: number; // フェードアウト時間[秒]
+ private _isTriggeredFadeOut: boolean; // フェードアウト開始フラグ
+
+ _motionQueueEntryHandle: CubismMotionQueueEntryHandle; // インスタンスごとに一意の値を持つ識別番号
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotionqueueentry';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotionQueueEntry = $.CubismMotionQueueEntry;
+ export type CubismMotionQueueEntry = $.CubismMotionQueueEntry;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueuemanager.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueuemanager.ts
new file mode 100644
index 000000000..2aa5565b6
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/motion/cubismmotionqueuemanager.ts
@@ -0,0 +1,342 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { ACubismMotion } from './acubismmotion';
+import { CubismMotionQueueEntry } from './cubismmotionqueueentry';
+import { csmVector, iterator } from '../type/csmvector';
+import { CubismModel } from '../model/cubismmodel';
+import { csmString } from '../type/csmstring';
+
+/**
+ * モーション再生の管理
+ *
+ * モーション再生の管理用クラス。CubismMotionモーションなどACubismMotionのサブクラスを再生するために使用する。
+ *
+ * @note 再生中に別のモーションが StartMotion()された場合は、新しいモーションに滑らかに変化し旧モーションは中断する。
+ * 表情用モーション、体用モーションなどを分けてモーション化した場合など、
+ * 複数のモーションを同時に再生させる場合は、複数のCubismMotionQueueManagerインスタンスを使用する。
+ */
+export class CubismMotionQueueManager {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._userTimeSeconds = 0.0;
+ this._eventCallBack = null;
+ this._eventCustomData = null;
+ this._motions = new csmVector();
+ }
+
+ /**
+ * デストラクタ
+ */
+ public release(): void {
+ for (let i = 0; i < this._motions.getSize(); ++i) {
+ if (this._motions.at(i)) {
+ this._motions.at(i).release();
+ this._motions.set(i, null);
+ }
+ }
+
+ this._motions = null;
+ }
+
+ /**
+ * 指定したモーションの開始
+ *
+ * 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。
+ *
+ * @param motion 開始するモーション
+ * @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」
+ */
+ public startMotion(
+ motion: ACubismMotion,
+ autoDelete: boolean,
+ userTimeSeconds: number
+ ): CubismMotionQueueEntryHandle {
+ if (motion == null) {
+ return InvalidMotionQueueEntryHandleValue;
+ }
+
+ let motionQueueEntry: CubismMotionQueueEntry = null;
+
+ // 既にモーションがあれば終了フラグを立てる
+ for (let i = 0; i < this._motions.getSize(); ++i) {
+ motionQueueEntry = this._motions.at(i);
+ if (motionQueueEntry == null) {
+ continue;
+ }
+
+ motionQueueEntry.setFadeOut(motionQueueEntry._motion.getFadeOutTime()); // フェードアウト設定
+ }
+
+ motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する
+ motionQueueEntry._autoDelete = autoDelete;
+ motionQueueEntry._motion = motion;
+
+ this._motions.pushBack(motionQueueEntry);
+
+ return motionQueueEntry._motionQueueEntryHandle;
+ }
+
+ /**
+ * 全てのモーションの終了の確認
+ * @return true 全て終了している
+ * @return false 終了していない
+ */
+ public isFinished(): boolean {
+ // ------- 処理を行う -------
+ // 既にモーションがあれば終了フラグを立てる
+
+ for (
+ let ite: iterator = this._motions.begin();
+ ite.notEqual(this._motions.end());
+
+ ) {
+ let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
+
+ if (motionQueueEntry == null) {
+ ite = this._motions.erase(ite); // 削除
+ continue;
+ }
+
+ const motion: ACubismMotion = motionQueueEntry._motion;
+
+ if (motion == null) {
+ motionQueueEntry.release();
+ motionQueueEntry = null;
+ ite = this._motions.erase(ite); // 削除
+ continue;
+ }
+
+ // ----- 終了済みの処理があれば削除する ------
+ if (!motionQueueEntry.isFinished()) {
+ return false;
+ } else {
+ ite.preIncrement();
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * 指定したモーションの終了の確認
+ * @param motionQueueEntryNumber モーションの識別番号
+ * @return true 全て終了している
+ * @return false 終了していない
+ */
+ public isFinishedByHandle(
+ motionQueueEntryNumber: CubismMotionQueueEntryHandle
+ ): boolean {
+ for (
+ let ite: iterator = this._motions.begin();
+ ite.notEqual(this._motions.end());
+ ite.increment()
+ ) {
+ const motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
+
+ if (motionQueueEntry == null) {
+ continue;
+ }
+
+ if (
+ motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber &&
+ !motionQueueEntry.isFinished()
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 全てのモーションを停止する
+ */
+ public stopAllMotions(): void {
+ // ------- 処理を行う -------
+ // 既にモーションがあれば終了フラグを立てる
+
+ for (
+ let ite: iterator = this._motions.begin();
+ ite.notEqual(this._motions.end());
+
+ ) {
+ let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
+
+ if (motionQueueEntry == null) {
+ ite = this._motions.erase(ite);
+
+ continue;
+ }
+
+ // ----- 終了済みの処理があれば削除する ------
+ motionQueueEntry.release();
+ motionQueueEntry = null;
+ ite = this._motions.erase(ite); // 削除
+ }
+ }
+
+ /**
+ * 指定したCubismMotionQueueEntryの取得
+
+ * @param motionQueueEntryNumber モーションの識別番号
+ * @return 指定したCubismMotionQueueEntry
+ * @return null 見つからなかった
+ */
+ public getCubismMotionQueueEntry(
+ motionQueueEntryNumber: any
+ ): CubismMotionQueueEntry {
+ //------- 処理を行う -------
+ for (
+ let ite: iterator = this._motions.begin();
+ ite.notEqual(this._motions.end());
+ ite.preIncrement()
+ ) {
+ const motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
+
+ if (motionQueueEntry == null) {
+ continue;
+ }
+
+ if (motionQueueEntry._motionQueueEntryHandle == motionQueueEntryNumber) {
+ return motionQueueEntry;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * イベントを受け取るCallbackの登録
+ *
+ * @param callback コールバック関数
+ * @param customData コールバックに返されるデータ
+ */
+ public setEventCallback(
+ callback: CubismMotionEventFunction,
+ customData: any = null
+ ): void {
+ this._eventCallBack = callback;
+ this._eventCustomData = customData;
+ }
+
+ /**
+ * モーションを更新して、モデルにパラメータ値を反映する。
+ *
+ * @param model 対象のモデル
+ * @param userTimeSeconds デルタ時間の積算値[秒]
+ * @return true モデルへパラメータ値の反映あり
+ * @return false モデルへパラメータ値の反映なし(モーションの変化なし)
+ */
+ public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean {
+ let updated = false;
+
+ // ------- 処理を行う --------
+ // 既にモーションがあれば終了フラグを立てる
+
+ for (
+ let ite: iterator = this._motions.begin();
+ ite.notEqual(this._motions.end());
+
+ ) {
+ let motionQueueEntry: CubismMotionQueueEntry = ite.ptr();
+
+ if (motionQueueEntry == null) {
+ ite = this._motions.erase(ite); // 削除
+ continue;
+ }
+
+ const motion: ACubismMotion = motionQueueEntry._motion;
+
+ if (motion == null) {
+ motionQueueEntry.release();
+ motionQueueEntry = null;
+ ite = this._motions.erase(ite); // 削除
+
+ continue;
+ }
+
+ // ------ 値を反映する ------
+ motion.updateParameters(model, motionQueueEntry, userTimeSeconds);
+ updated = true;
+
+ // ------ ユーザトリガーイベントを検査する ----
+ const firedList: csmVector = motion.getFiredEvent(
+ motionQueueEntry.getLastCheckEventSeconds() -
+ motionQueueEntry.getStartTime(),
+ userTimeSeconds - motionQueueEntry.getStartTime()
+ );
+
+ for (let i = 0; i < firedList.getSize(); ++i) {
+ this._eventCallBack(this, firedList.at(i), this._eventCustomData);
+ }
+
+ motionQueueEntry.setLastCheckEventSeconds(userTimeSeconds);
+
+ // ------ 終了済みの処理があれば削除する ------
+ if (motionQueueEntry.isFinished()) {
+ motionQueueEntry.release();
+ motionQueueEntry = null;
+ ite = this._motions.erase(ite); // 削除
+ } else {
+ if (motionQueueEntry.isTriggeredFadeOut()) {
+ motionQueueEntry.startFadeOut(
+ motionQueueEntry.getFadeOutSeconds(),
+ userTimeSeconds
+ );
+ }
+ ite.preIncrement();
+ }
+ }
+
+ return updated;
+ }
+ _userTimeSeconds: number; // デルタ時間の積算値[秒]
+
+ _motions: csmVector; // モーション
+ _eventCallBack: CubismMotionEventFunction; // コールバック関数
+ _eventCustomData: any; // コールバックに戻されるデータ
+}
+
+/**
+ * イベントのコールバック関数を定義
+ *
+ * イベントのコールバックに登録できる関数の型情報
+ * @param caller 発火したイベントを再生させたCubismMotionQueueManager
+ * @param eventValue 発火したイベントの文字列データ
+ * @param customData コールバックに返される登録時に指定されたデータ
+ */
+export interface CubismMotionEventFunction {
+ (
+ caller: CubismMotionQueueManager,
+ eventValue: csmString,
+ customData: any
+ ): void;
+}
+
+/**
+ * モーションの識別番号
+ *
+ * モーションの識別番号の定義
+ */
+export declare type CubismMotionQueueEntryHandle = any;
+export const InvalidMotionQueueEntryHandleValue: CubismMotionQueueEntryHandle = -1;
+
+// Namespace definition for compatibility.
+import * as $ from './cubismmotionqueuemanager';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismMotionQueueManager = $.CubismMotionQueueManager;
+ export type CubismMotionQueueManager = $.CubismMotionQueueManager;
+ export const InvalidMotionQueueEntryHandleValue =
+ $.InvalidMotionQueueEntryHandleValue;
+ export type CubismMotionQueueEntryHandle = $.CubismMotionQueueEntryHandle;
+ export type CubismMotionEventFunction = $.CubismMotionEventFunction;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysics.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysics.ts
new file mode 100644
index 000000000..9fe9a5029
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysics.ts
@@ -0,0 +1,934 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismMath } from '../math/cubismmath';
+import { CubismVector2 } from '../math/cubismvector2';
+import { CubismModel } from '../model/cubismmodel';
+import {
+ CubismPhysicsInput,
+ CubismPhysicsNormalization,
+ CubismPhysicsOutput,
+ CubismPhysicsParticle,
+ CubismPhysicsRig,
+ CubismPhysicsSource,
+ CubismPhysicsSubRig,
+ CubismPhysicsTargetType
+} from './cubismphysicsinternal';
+import { CubismPhysicsJson } from './cubismphysicsjson';
+
+// physics types tags.
+const PhysicsTypeTagX = 'X';
+const PhysicsTypeTagY = 'Y';
+const PhysicsTypeTagAngle = 'Angle';
+
+// Constant of air resistance.
+const AirResistance = 5.0;
+
+// Constant of maximum weight of input and output ratio.
+const MaximumWeight = 100.0;
+
+// Constant of threshold of movement.
+const MovementThreshold = 0.001;
+
+/**
+ * 物理演算クラス
+ */
+export class CubismPhysics {
+ /**
+ * インスタンスの作成
+ * @param buffer physics3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ * @return 作成されたインスタンス
+ */
+ public static create(buffer: ArrayBuffer, size: number): CubismPhysics {
+ const ret: CubismPhysics = new CubismPhysics();
+
+ ret.parse(buffer, size);
+ ret._physicsRig.gravity.y = 0;
+
+ return ret;
+ }
+
+ /**
+ * インスタンスを破棄する
+ * @param physics 破棄するインスタンス
+ */
+ public static delete(physics: CubismPhysics): void {
+ if (physics != null) {
+ physics.release();
+ physics = null;
+ }
+ }
+
+ /**
+ * 物理演算の評価
+ * @param model 物理演算の結果を適用するモデル
+ * @param deltaTimeSeconds デルタ時間[秒]
+ */
+ public evaluate(model: CubismModel, deltaTimeSeconds: number): void {
+ let totalAngle: { angle: number };
+ let weight: number;
+ let radAngle: number;
+ let outputValue: number;
+ const totalTranslation: CubismVector2 = new CubismVector2();
+ let currentSetting: CubismPhysicsSubRig;
+ let currentInput: CubismPhysicsInput[];
+ let currentOutput: CubismPhysicsOutput[];
+ let currentParticles: CubismPhysicsParticle[];
+
+ let parameterValue: Float32Array;
+ let parameterMaximumValue: Float32Array;
+ let parameterMinimumValue: Float32Array;
+ let parameterDefaultValue: Float32Array;
+
+ parameterValue = model.getModel().parameters.values;
+ parameterMaximumValue = model.getModel().parameters.maximumValues;
+ parameterMinimumValue = model.getModel().parameters.minimumValues;
+ parameterDefaultValue = model.getModel().parameters.defaultValues;
+
+ for (
+ let settingIndex = 0;
+ settingIndex < this._physicsRig.subRigCount;
+ ++settingIndex
+ ) {
+ totalAngle = { angle: 0.0 };
+ totalTranslation.x = 0.0;
+ totalTranslation.y = 0.0;
+ currentSetting = this._physicsRig.settings.at(settingIndex);
+ currentInput = this._physicsRig.inputs.get(currentSetting.baseInputIndex);
+ currentOutput = this._physicsRig.outputs.get(
+ currentSetting.baseOutputIndex
+ );
+ currentParticles = this._physicsRig.particles.get(
+ currentSetting.baseParticleIndex
+ );
+
+ // Load input parameters
+ for (let i = 0; i < currentSetting.inputCount; ++i) {
+ weight = currentInput[i].weight / MaximumWeight;
+
+ if (currentInput[i].sourceParameterIndex == -1) {
+ currentInput[i].sourceParameterIndex = model.getParameterIndex(
+ currentInput[i].source.id
+ );
+ }
+
+ currentInput[i].getNormalizedParameterValue(
+ totalTranslation,
+ totalAngle,
+ parameterValue[currentInput[i].sourceParameterIndex],
+ parameterMinimumValue[currentInput[i].sourceParameterIndex],
+ parameterMaximumValue[currentInput[i].sourceParameterIndex],
+ parameterDefaultValue[currentInput[i].sourceParameterIndex],
+ currentSetting.normalizationPosition,
+ currentSetting.normalizationAngle,
+ currentInput[i].reflect,
+ weight
+ );
+ }
+
+ radAngle = CubismMath.degreesToRadian(-totalAngle.angle);
+
+ totalTranslation.x =
+ totalTranslation.x * CubismMath.cos(radAngle) -
+ totalTranslation.y * CubismMath.sin(radAngle);
+ totalTranslation.y =
+ totalTranslation.x * CubismMath.sin(radAngle) +
+ totalTranslation.y * CubismMath.cos(radAngle);
+
+ // Calculate particles position.
+ updateParticles(
+ currentParticles,
+ currentSetting.particleCount,
+ totalTranslation,
+ totalAngle.angle,
+ this._options.wind,
+ MovementThreshold * currentSetting.normalizationPosition.maximum,
+ deltaTimeSeconds,
+ AirResistance
+ );
+
+ // Update output parameters.
+ for (let i = 0; i < currentSetting.outputCount; ++i) {
+ const particleIndex = currentOutput[i].vertexIndex;
+
+ if (
+ particleIndex < 1 ||
+ particleIndex >= currentSetting.particleCount
+ ) {
+ break;
+ }
+
+ if (currentOutput[i].destinationParameterIndex == -1) {
+ currentOutput[i].destinationParameterIndex = model.getParameterIndex(
+ currentOutput[i].destination.id
+ );
+ }
+
+ const translation: CubismVector2 = new CubismVector2();
+ translation.x =
+ currentParticles[particleIndex].position.x -
+ currentParticles[particleIndex - 1].position.x;
+ translation.y =
+ currentParticles[particleIndex].position.y -
+ currentParticles[particleIndex - 1].position.y;
+
+ outputValue = currentOutput[i].getValue(
+ translation,
+ currentParticles,
+ particleIndex,
+ currentOutput[i].reflect,
+ this._options.gravity
+ );
+
+ const destinationParameterIndex: number =
+ currentOutput[i].destinationParameterIndex;
+ const outParameterValue: Float32Array =
+ !Float32Array.prototype.slice && 'subarray' in Float32Array.prototype
+ ? JSON.parse(
+ JSON.stringify(
+ parameterValue.subarray(destinationParameterIndex)
+ )
+ ) // 値渡しするため、JSON.parse, JSON.stringify
+ : parameterValue.slice(destinationParameterIndex);
+
+ updateOutputParameterValue(
+ outParameterValue,
+ parameterMinimumValue[destinationParameterIndex],
+ parameterMaximumValue[destinationParameterIndex],
+ outputValue,
+ currentOutput[i]
+ );
+
+ // 値を反映
+ for (
+ let offset: number = destinationParameterIndex, outParamIndex = 0;
+ offset < parameterValue.length;
+ offset++, outParamIndex++
+ ) {
+ parameterValue[offset] = outParameterValue[outParamIndex];
+ }
+ }
+ }
+ }
+
+ /**
+ * オプションの設定
+ * @param options オプション
+ */
+ public setOptions(options: Options): void {
+ this._options = options;
+ }
+
+ /**
+ * オプションの取得
+ * @return オプション
+ */
+ public getOption(): Options {
+ return this._options;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._physicsRig = null;
+
+ // set default options
+ this._options = new Options();
+ this._options.gravity.y = -1.0;
+ this._options.gravity.x = 0;
+ this._options.wind.x = 0;
+ this._options.wind.y = 0;
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this._physicsRig = void 0;
+ this._physicsRig = null;
+ }
+
+ /**
+ * physics3.jsonをパースする。
+ * @param physicsJson physics3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public parse(physicsJson: ArrayBuffer, size: number): void {
+ this._physicsRig = new CubismPhysicsRig();
+
+ let json: CubismPhysicsJson = new CubismPhysicsJson(physicsJson, size);
+
+ this._physicsRig.gravity = json.getGravity();
+ this._physicsRig.wind = json.getWind();
+ this._physicsRig.subRigCount = json.getSubRigCount();
+
+ this._physicsRig.settings.updateSize(
+ this._physicsRig.subRigCount,
+ CubismPhysicsSubRig,
+ true
+ );
+ this._physicsRig.inputs.updateSize(
+ json.getTotalInputCount(),
+ CubismPhysicsInput,
+ true
+ );
+ this._physicsRig.outputs.updateSize(
+ json.getTotalOutputCount(),
+ CubismPhysicsOutput,
+ true
+ );
+ this._physicsRig.particles.updateSize(
+ json.getVertexCount(),
+ CubismPhysicsParticle,
+ true
+ );
+
+ let inputIndex = 0,
+ outputIndex = 0,
+ particleIndex = 0;
+
+ for (let i = 0; i < this._physicsRig.settings.getSize(); ++i) {
+ this._physicsRig.settings.at(
+ i
+ ).normalizationPosition.minimum = json.getNormalizationPositionMinimumValue(
+ i
+ );
+ this._physicsRig.settings.at(
+ i
+ ).normalizationPosition.maximum = json.getNormalizationPositionMaximumValue(
+ i
+ );
+ this._physicsRig.settings.at(
+ i
+ ).normalizationPosition.defalut = json.getNormalizationPositionDefaultValue(
+ i
+ );
+
+ this._physicsRig.settings.at(
+ i
+ ).normalizationAngle.minimum = json.getNormalizationAngleMinimumValue(i);
+ this._physicsRig.settings.at(
+ i
+ ).normalizationAngle.maximum = json.getNormalizationAngleMaximumValue(i);
+ this._physicsRig.settings.at(
+ i
+ ).normalizationAngle.defalut = json.getNormalizationAngleDefaultValue(i);
+
+ // Input
+ this._physicsRig.settings.at(i).inputCount = json.getInputCount(i);
+ this._physicsRig.settings.at(i).baseInputIndex = inputIndex;
+
+ for (let j = 0; j < this._physicsRig.settings.at(i).inputCount; ++j) {
+ this._physicsRig.inputs.at(inputIndex + j).sourceParameterIndex = -1;
+ this._physicsRig.inputs.at(inputIndex + j).weight = json.getInputWeight(
+ i,
+ j
+ );
+ this._physicsRig.inputs.at(
+ inputIndex + j
+ ).reflect = json.getInputReflect(i, j);
+
+ if (json.getInputType(i, j) == PhysicsTypeTagX) {
+ this._physicsRig.inputs.at(inputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_X;
+ this._physicsRig.inputs.at(
+ inputIndex + j
+ ).getNormalizedParameterValue = getInputTranslationXFromNormalizedParameterValue;
+ } else if (json.getInputType(i, j) == PhysicsTypeTagY) {
+ this._physicsRig.inputs.at(inputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_Y;
+ this._physicsRig.inputs.at(
+ inputIndex + j
+ ).getNormalizedParameterValue = getInputTranslationYFromNormalizedParamterValue;
+ } else if (json.getInputType(i, j) == PhysicsTypeTagAngle) {
+ this._physicsRig.inputs.at(inputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_Angle;
+ this._physicsRig.inputs.at(
+ inputIndex + j
+ ).getNormalizedParameterValue = getInputAngleFromNormalizedParameterValue;
+ }
+
+ this._physicsRig.inputs.at(inputIndex + j).source.targetType =
+ CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter;
+ this._physicsRig.inputs.at(
+ inputIndex + j
+ ).source.id = json.getInputSourceId(i, j);
+ }
+ inputIndex += this._physicsRig.settings.at(i).inputCount;
+
+ // Output
+ this._physicsRig.settings.at(i).outputCount = json.getOutputCount(i);
+ this._physicsRig.settings.at(i).baseOutputIndex = outputIndex;
+
+ for (let j = 0; j < this._physicsRig.settings.at(i).outputCount; ++j) {
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).destinationParameterIndex = -1;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).vertexIndex = json.getOutputVertexIndex(i, j);
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).angleScale = json.getOutputAngleScale(i, j);
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).weight = json.getOutputWeight(i, j);
+ this._physicsRig.outputs.at(outputIndex + j).destination.targetType =
+ CubismPhysicsTargetType.CubismPhysicsTargetType_Parameter;
+
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).destination.id = json.getOutputDestinationId(i, j);
+
+ if (json.getOutputType(i, j) == PhysicsTypeTagX) {
+ this._physicsRig.outputs.at(outputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_X;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getValue = getOutputTranslationX;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getScale = getOutputScaleTranslationX;
+ } else if (json.getOutputType(i, j) == PhysicsTypeTagY) {
+ this._physicsRig.outputs.at(outputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_Y;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getValue = getOutputTranslationY;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getScale = getOutputScaleTranslationY;
+ } else if (json.getOutputType(i, j) == PhysicsTypeTagAngle) {
+ this._physicsRig.outputs.at(outputIndex + j).type =
+ CubismPhysicsSource.CubismPhysicsSource_Angle;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getValue = getOutputAngle;
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).getScale = getOutputScaleAngle;
+ }
+
+ this._physicsRig.outputs.at(
+ outputIndex + j
+ ).reflect = json.getOutputReflect(i, j);
+ }
+ outputIndex += this._physicsRig.settings.at(i).outputCount;
+
+ // Particle
+ this._physicsRig.settings.at(i).particleCount = json.getParticleCount(i);
+ this._physicsRig.settings.at(i).baseParticleIndex = particleIndex;
+
+ for (let j = 0; j < this._physicsRig.settings.at(i).particleCount; ++j) {
+ this._physicsRig.particles.at(
+ particleIndex + j
+ ).mobility = json.getParticleMobility(i, j);
+ this._physicsRig.particles.at(
+ particleIndex + j
+ ).delay = json.getParticleDelay(i, j);
+ this._physicsRig.particles.at(
+ particleIndex + j
+ ).acceleration = json.getParticleAcceleration(i, j);
+ this._physicsRig.particles.at(
+ particleIndex + j
+ ).radius = json.getParticleRadius(i, j);
+ this._physicsRig.particles.at(
+ particleIndex + j
+ ).position = json.getParticlePosition(i, j);
+ }
+
+ particleIndex += this._physicsRig.settings.at(i).particleCount;
+ }
+
+ this.initialize();
+
+ json.release();
+ json = void 0;
+ json = null;
+ }
+
+ /**
+ * 初期化する
+ */
+ public initialize(): void {
+ let strand: CubismPhysicsParticle[];
+ let currentSetting: CubismPhysicsSubRig;
+ let radius: CubismVector2;
+
+ for (
+ let settingIndex = 0;
+ settingIndex < this._physicsRig.subRigCount;
+ ++settingIndex
+ ) {
+ currentSetting = this._physicsRig.settings.at(settingIndex);
+ strand = this._physicsRig.particles.get(currentSetting.baseParticleIndex);
+
+ // Initialize the top of particle.
+ strand[0].initialPosition = new CubismVector2(0.0, 0.0);
+ strand[0].lastPosition = new CubismVector2(
+ strand[0].initialPosition.x,
+ strand[0].initialPosition.y
+ );
+ strand[0].lastGravity = new CubismVector2(0.0, -1.0);
+ strand[0].lastGravity.y *= -1.0;
+ strand[0].velocity = new CubismVector2(0.0, 0.0);
+ strand[0].force = new CubismVector2(0.0, 0.0);
+
+ // Initialize paritcles.
+ for (let i = 1; i < currentSetting.particleCount; ++i) {
+ radius = new CubismVector2(0.0, 0.0);
+ radius.y = strand[i].radius;
+ strand[i].initialPosition = new CubismVector2(
+ strand[i - 1].initialPosition.x + radius.x,
+ strand[i - 1].initialPosition.y + radius.y
+ );
+ strand[i].position = new CubismVector2(
+ strand[i].initialPosition.x,
+ strand[i].initialPosition.y
+ );
+ strand[i].lastPosition = new CubismVector2(
+ strand[i].initialPosition.x,
+ strand[i].initialPosition.y
+ );
+ strand[i].lastGravity = new CubismVector2(0.0, -1.0);
+ strand[i].lastGravity.y *= -1.0;
+ strand[i].velocity = new CubismVector2(0.0, 0.0);
+ strand[i].force = new CubismVector2(0.0, 0.0);
+ }
+ }
+ }
+
+ _physicsRig: CubismPhysicsRig; // 物理演算のデータ
+ _options: Options; // オプション
+}
+
+/**
+ * 物理演算のオプション
+ */
+export class Options {
+ constructor() {
+ this.gravity = new CubismVector2(0, 0);
+ this.wind = new CubismVector2(0, 0);
+ }
+
+ gravity: CubismVector2; // 重力方向
+ wind: CubismVector2; // 風の方向
+}
+
+/**
+ * Gets sign.
+ *
+ * @param value Evaluation target value.
+ *
+ * @return Sign of value.
+ */
+function sign(value: number): number {
+ let ret = 0;
+
+ if (value > 0.0) {
+ ret = 1;
+ } else if (value < 0.0) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+function getInputTranslationXFromNormalizedParameterValue(
+ targetTranslation: CubismVector2,
+ targetAngle: { angle: number },
+ value: number,
+ parameterMinimumValue: number,
+ parameterMaximumValue: number,
+ parameterDefaultValue: number,
+ normalizationPosition: CubismPhysicsNormalization,
+ normalizationAngle: CubismPhysicsNormalization,
+ isInverted: boolean,
+ weight: number
+): void {
+ targetTranslation.x +=
+ normalizeParameterValue(
+ value,
+ parameterMinimumValue,
+ parameterMaximumValue,
+ parameterDefaultValue,
+ normalizationPosition.minimum,
+ normalizationPosition.maximum,
+ normalizationPosition.defalut,
+ isInverted
+ ) * weight;
+}
+
+function getInputTranslationYFromNormalizedParamterValue(
+ targetTranslation: CubismVector2,
+ targetAngle: { angle: number },
+ value: number,
+ parameterMinimumValue: number,
+ parameterMaximumValue: number,
+ parameterDefaultValue: number,
+ normalizationPosition: CubismPhysicsNormalization,
+ normalizationAngle: CubismPhysicsNormalization,
+ isInverted: boolean,
+ weight: number
+): void {
+ targetTranslation.y +=
+ normalizeParameterValue(
+ value,
+ parameterMinimumValue,
+ parameterMaximumValue,
+ parameterDefaultValue,
+ normalizationPosition.minimum,
+ normalizationPosition.maximum,
+ normalizationPosition.defalut,
+ isInverted
+ ) * weight;
+}
+
+function getInputAngleFromNormalizedParameterValue(
+ targetTranslation: CubismVector2,
+ targetAngle: { angle: number },
+ value: number,
+ parameterMinimumValue: number,
+ parameterMaximumValue: number,
+ parameterDefaultValue: number,
+ normalizaitionPosition: CubismPhysicsNormalization,
+ normalizationAngle: CubismPhysicsNormalization,
+ isInverted: boolean,
+ weight: number
+): void {
+ targetAngle.angle +=
+ normalizeParameterValue(
+ value,
+ parameterMinimumValue,
+ parameterMaximumValue,
+ parameterDefaultValue,
+ normalizationAngle.minimum,
+ normalizationAngle.maximum,
+ normalizationAngle.defalut,
+ isInverted
+ ) * weight;
+}
+
+function getOutputTranslationX(
+ translation: CubismVector2,
+ particles: CubismPhysicsParticle[],
+ particleIndex: number,
+ isInverted: boolean,
+ parentGravity: CubismVector2
+): number {
+ let outputValue: number = translation.x;
+
+ if (isInverted) {
+ outputValue *= -1.0;
+ }
+
+ return outputValue;
+}
+
+function getOutputTranslationY(
+ translation: CubismVector2,
+ particles: CubismPhysicsParticle[],
+ particleIndex: number,
+ isInverted: boolean,
+ parentGravity: CubismVector2
+): number {
+ let outputValue: number = translation.y;
+
+ if (isInverted) {
+ outputValue *= -1.0;
+ }
+ return outputValue;
+}
+
+function getOutputAngle(
+ translation: CubismVector2,
+ particles: CubismPhysicsParticle[],
+ particleIndex: number,
+ isInverted: boolean,
+ parentGravity: CubismVector2
+): number {
+ let outputValue: number;
+
+ if (particleIndex >= 2) {
+ parentGravity = particles[particleIndex - 1].position.substract(
+ particles[particleIndex - 2].position
+ );
+ } else {
+ parentGravity = parentGravity.multiplyByScaler(-1.0);
+ }
+
+ outputValue = CubismMath.directionToRadian(parentGravity, translation);
+
+ if (isInverted) {
+ outputValue *= -1.0;
+ }
+
+ return outputValue;
+}
+
+function getRangeValue(min: number, max: number): number {
+ const maxValue: number = CubismMath.max(min, max);
+ const minValue: number = CubismMath.min(min, max);
+
+ return CubismMath.abs(maxValue - minValue);
+}
+
+function getDefaultValue(min: number, max: number): number {
+ const minValue: number = CubismMath.min(min, max);
+ return minValue + getRangeValue(min, max) / 2.0;
+}
+
+function getOutputScaleTranslationX(
+ translationScale: CubismVector2,
+ angleScale: number
+): number {
+ return JSON.parse(JSON.stringify(translationScale.x));
+}
+
+function getOutputScaleTranslationY(
+ translationScale: CubismVector2,
+ angleScale: number
+): number {
+ return JSON.parse(JSON.stringify(translationScale.y));
+}
+
+function getOutputScaleAngle(
+ translationScale: CubismVector2,
+ angleScale: number
+): number {
+ return JSON.parse(JSON.stringify(angleScale));
+}
+
+/**
+ * Updates particles.
+ *
+ * @param strand Target array of particle.
+ * @param strandCount Count of particle.
+ * @param totalTranslation Total translation value.
+ * @param totalAngle Total angle.
+ * @param windDirection Direction of Wind.
+ * @param thresholdValue Threshold of movement.
+ * @param deltaTimeSeconds Delta time.
+ * @param airResistance Air resistance.
+ */
+function updateParticles(
+ strand: CubismPhysicsParticle[],
+ strandCount: number,
+ totalTranslation: CubismVector2,
+ totalAngle: number,
+ windDirection: CubismVector2,
+ thresholdValue: number,
+ deltaTimeSeconds: number,
+ airResistance: number
+) {
+ let totalRadian: number;
+ let delay: number;
+ let radian: number;
+ let currentGravity: CubismVector2;
+ let direction: CubismVector2 = new CubismVector2(0.0, 0.0);
+ let velocity: CubismVector2 = new CubismVector2(0.0, 0.0);
+ let force: CubismVector2 = new CubismVector2(0.0, 0.0);
+ let newDirection: CubismVector2 = new CubismVector2(0.0, 0.0);
+
+ strand[0].position = new CubismVector2(
+ totalTranslation.x,
+ totalTranslation.y
+ );
+
+ totalRadian = CubismMath.degreesToRadian(totalAngle);
+ currentGravity = CubismMath.radianToDirection(totalRadian);
+ currentGravity.normalize();
+
+ for (let i = 1; i < strandCount; ++i) {
+ strand[i].force = currentGravity
+ .multiplyByScaler(strand[i].acceleration)
+ .add(windDirection);
+
+ strand[i].lastPosition = new CubismVector2(
+ strand[i].position.x,
+ strand[i].position.y
+ );
+
+ delay = strand[i].delay * deltaTimeSeconds * 30.0;
+
+ direction = strand[i].position.substract(strand[i - 1].position);
+
+ radian =
+ CubismMath.directionToRadian(strand[i].lastGravity, currentGravity) /
+ airResistance;
+
+ direction.x =
+ CubismMath.cos(radian) * direction.x -
+ direction.y * CubismMath.sin(radian);
+ direction.y =
+ CubismMath.sin(radian) * direction.x +
+ direction.y * CubismMath.cos(radian);
+
+ strand[i].position = strand[i - 1].position.add(direction);
+
+ velocity = strand[i].velocity.multiplyByScaler(delay);
+ force = strand[i].force.multiplyByScaler(delay).multiplyByScaler(delay);
+
+ strand[i].position = strand[i].position.add(velocity).add(force);
+
+ newDirection = strand[i].position.substract(strand[i - 1].position);
+ newDirection.normalize();
+
+ strand[i].position = strand[i - 1].position.add(
+ newDirection.multiplyByScaler(strand[i].radius)
+ );
+
+ if (CubismMath.abs(strand[i].position.x) < thresholdValue) {
+ strand[i].position.x = 0.0;
+ }
+
+ if (delay != 0.0) {
+ strand[i].velocity = strand[i].position.substract(strand[i].lastPosition);
+ strand[i].velocity = strand[i].velocity.divisionByScalar(delay);
+ strand[i].velocity = strand[i].velocity.multiplyByScaler(
+ strand[i].mobility
+ );
+ }
+
+ strand[i].force = new CubismVector2(0.0, 0.0);
+ strand[i].lastGravity = new CubismVector2(
+ currentGravity.x,
+ currentGravity.y
+ );
+ }
+}
+
+/**
+ * Updates output parameter value.
+ * @param parameterValue Target parameter value.
+ * @param parameterValueMinimum Minimum of parameter value.
+ * @param parameterValueMaximum Maximum of parameter value.
+ * @param translation Translation value.
+ */
+function updateOutputParameterValue(
+ parameterValue: Float32Array,
+ parameterValueMinimum: number,
+ parameterValueMaximum: number,
+ translation: number,
+ output: CubismPhysicsOutput
+): void {
+ let outputScale: number;
+ let value: number;
+ let weight: number;
+
+ outputScale = output.getScale(output.translationScale, output.angleScale);
+
+ value = translation * outputScale;
+
+ if (value < parameterValueMinimum) {
+ if (value < output.valueBelowMinimum) {
+ output.valueBelowMinimum = value;
+ }
+
+ value = parameterValueMinimum;
+ } else if (value > parameterValueMaximum) {
+ if (value > output.valueExceededMaximum) {
+ output.valueExceededMaximum = value;
+ }
+
+ value = parameterValueMaximum;
+ }
+
+ weight = output.weight / MaximumWeight;
+
+ if (weight >= 1.0) {
+ parameterValue[0] = value;
+ } else {
+ value = parameterValue[0] * (1.0 - weight) + value * weight;
+ parameterValue[0] = value;
+ }
+}
+
+function normalizeParameterValue(
+ value: number,
+ parameterMinimum: number,
+ parameterMaximum: number,
+ parameterDefault: number,
+ normalizedMinimum: number,
+ normalizedMaximum: number,
+ normalizedDefault: number,
+ isInverted: boolean
+) {
+ let result = 0.0;
+
+ const maxValue: number = CubismMath.max(parameterMaximum, parameterMinimum);
+
+ if (maxValue < value) {
+ value = maxValue;
+ }
+
+ const minValue: number = CubismMath.min(parameterMaximum, parameterMinimum);
+
+ if (minValue > value) {
+ value = minValue;
+ }
+
+ const minNormValue: number = CubismMath.min(
+ normalizedMinimum,
+ normalizedMaximum
+ );
+ const maxNormValue: number = CubismMath.max(
+ normalizedMinimum,
+ normalizedMaximum
+ );
+ const middleNormValue: number = normalizedDefault;
+
+ const middleValue: number = getDefaultValue(minValue, maxValue);
+ const paramValue: number = value - middleValue;
+
+ switch (sign(paramValue)) {
+ case 1: {
+ const nLength: number = maxNormValue - middleNormValue;
+ const pLength: number = maxValue - middleValue;
+
+ if (pLength != 0.0) {
+ result = paramValue * (nLength / pLength);
+ result += middleNormValue;
+ }
+
+ break;
+ }
+ case -1: {
+ const nLength: number = minNormValue - middleNormValue;
+ const pLength: number = minValue - middleValue;
+
+ if (pLength != 0.0) {
+ result = paramValue * (nLength / pLength);
+ result += middleNormValue;
+ }
+
+ break;
+ }
+ case 0: {
+ result = middleNormValue;
+
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return isInverted ? result : result * -1.0;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismphysics';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismPhysics = $.CubismPhysics;
+ export type CubismPhysics = $.CubismPhysics;
+ export const Options = $.Options;
+ export type Options = $.Options;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsinternal.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsinternal.ts
new file mode 100644
index 000000000..8c7372246
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsinternal.ts
@@ -0,0 +1,249 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismVector2 } from '../math/cubismvector2';
+import { csmVector } from '../type/csmvector';
+
+/**
+ * 物理演算の適用先の種類
+ */
+export enum CubismPhysicsTargetType {
+ CubismPhysicsTargetType_Parameter // パラメータに対して適用
+}
+
+/**
+ * 物理演算の入力の種類
+ */
+export enum CubismPhysicsSource {
+ CubismPhysicsSource_X, // X軸の位置から
+ CubismPhysicsSource_Y, // Y軸の位置から
+ CubismPhysicsSource_Angle // 角度から
+}
+
+/**
+ * @brief 物理演算で使用する外部の力
+ *
+ * 物理演算で使用する外部の力。
+ */
+export class PhysicsJsonEffectiveForces {
+ constructor() {
+ this.gravity = new CubismVector2(0, 0);
+ this.wind = new CubismVector2(0, 0);
+ }
+ gravity: CubismVector2; // 重力
+ wind: CubismVector2; // 風
+}
+
+/**
+ * 物理演算のパラメータ情報
+ */
+export class CubismPhysicsParameter {
+ id: CubismIdHandle; // パラメータ
+ targetType: CubismPhysicsTargetType; // 適用先の種類
+}
+
+/**
+ * 物理演算の正規化情報
+ */
+export class CubismPhysicsNormalization {
+ minimum: number; // 最大値
+ maximum: number; // 最小値
+ defalut: number; // デフォルト値
+}
+
+/**
+ * 物理演算の演算委使用する物理点の情報
+ */
+export class CubismPhysicsParticle {
+ constructor() {
+ this.initialPosition = new CubismVector2(0, 0);
+ this.position = new CubismVector2(0, 0);
+ this.lastPosition = new CubismVector2(0, 0);
+ this.lastGravity = new CubismVector2(0, 0);
+ this.force = new CubismVector2(0, 0);
+ this.velocity = new CubismVector2(0, 0);
+ }
+
+ initialPosition: CubismVector2; // 初期位置
+ mobility: number; // 動きやすさ
+ delay: number; // 遅れ
+ acceleration: number; // 加速度
+ radius: number; // 距離
+ position: CubismVector2; // 現在の位置
+ lastPosition: CubismVector2; // 最後の位置
+ lastGravity: CubismVector2; // 最後の重力
+ force: CubismVector2; // 現在かかっている力
+ velocity: CubismVector2; // 現在の速度
+}
+
+/**
+ * 物理演算の物理点の管理
+ */
+export class CubismPhysicsSubRig {
+ constructor() {
+ this.normalizationPosition = new CubismPhysicsNormalization();
+ this.normalizationAngle = new CubismPhysicsNormalization();
+ }
+ inputCount: number; // 入力の個数
+ outputCount: number; // 出力の個数
+ particleCount: number; // 物理点の個数
+ baseInputIndex: number; // 入力の最初のインデックス
+ baseOutputIndex: number; // 出力の最初のインデックス
+ baseParticleIndex: number; // 物理点の最初のインデックス
+ normalizationPosition: CubismPhysicsNormalization; // 正規化された位置
+ normalizationAngle: CubismPhysicsNormalization; // 正規化された角度
+}
+
+/**
+ * 正規化されたパラメータの取得関数の宣言
+ * @param targetTranslation // 演算結果の移動値
+ * @param targetAngle // 演算結果の角度
+ * @param value // パラメータの値
+ * @param parameterMinimunValue // パラメータの最小値
+ * @param parameterMaximumValue // パラメータの最大値
+ * @param parameterDefaultValue // パラメータのデフォルト値
+ * @param normalizationPosition // 正規化された位置
+ * @param normalizationAngle // 正規化された角度
+ * @param isInverted // 値が反転されているか?
+ * @param weight // 重み
+ */
+export interface normalizedPhysicsParameterValueGetter {
+ (
+ targetTranslation: CubismVector2,
+ targetAngle: { angle: number },
+ value: number,
+ parameterMinimunValue: number,
+ parameterMaximumValue: number,
+ parameterDefaultValue: number,
+ normalizationPosition: CubismPhysicsNormalization,
+ normalizationAngle: CubismPhysicsNormalization,
+ isInverted: boolean,
+ weight: number
+ ): void;
+}
+
+/**
+ * 物理演算の値の取得関数の宣言
+ * @param translation 移動値
+ * @param particles 物理点のリスト
+ * @param isInverted 値が反映されているか
+ * @param parentGravity 重力
+ * @return 値
+ */
+export interface physicsValueGetter {
+ (
+ translation: CubismVector2,
+ particles: CubismPhysicsParticle[],
+ particleIndex: number,
+ isInverted: boolean,
+ parentGravity: CubismVector2
+ ): number;
+}
+
+/**
+ * 物理演算のスケールの取得関数の宣言
+ * @param translationScale 移動値のスケール
+ * @param angleScale 角度のスケール
+ * @return スケール値
+ */
+export interface physicsScaleGetter {
+ (translationScale: CubismVector2, angleScale: number): number;
+}
+
+/**
+ * 物理演算の入力情報
+ */
+export class CubismPhysicsInput {
+ constructor() {
+ this.source = new CubismPhysicsParameter();
+ }
+ source: CubismPhysicsParameter; // 入力元のパラメータ
+ sourceParameterIndex: number; // 入力元のパラメータのインデックス
+ weight: number; // 重み
+ type: number; // 入力の種類
+ reflect: boolean; // 値が反転されているかどうか
+ getNormalizedParameterValue: normalizedPhysicsParameterValueGetter; // 正規化されたパラメータ値の取得関数
+}
+
+/**
+ * @brief 物理演算の出力情報
+ *
+ * 物理演算の出力情報。
+ */
+export class CubismPhysicsOutput {
+ constructor() {
+ this.destination = new CubismPhysicsParameter();
+ this.translationScale = new CubismVector2(0, 0);
+ }
+
+ destination: CubismPhysicsParameter; // 出力先のパラメータ
+ destinationParameterIndex: number; // 出力先のパラメータのインデックス
+ vertexIndex: number; // 振り子のインデックス
+ translationScale: CubismVector2; // 移動値のスケール
+ angleScale: number; // 角度のスケール
+ weight: number; // 重み
+ type: CubismPhysicsSource; // 出力の種類
+ reflect: boolean; // 値が反転されているかどうか
+ valueBelowMinimum: number; // 最小値を下回った時の値
+ valueExceededMaximum: number; // 最大値をこえた時の値
+ getValue: physicsValueGetter; // 物理演算の値の取得関数
+ getScale: physicsScaleGetter; // 物理演算のスケール値の取得関数
+}
+
+/**
+ * @brief 物理演算のデータ
+ *
+ * 物理演算のデータ。
+ */
+export class CubismPhysicsRig {
+ constructor() {
+ this.settings = new csmVector();
+ this.inputs = new csmVector();
+ this.outputs = new csmVector();
+ this.particles = new csmVector();
+ this.gravity = new CubismVector2(0, 0);
+ this.wind = new CubismVector2(0, 0);
+ }
+
+ subRigCount: number; // 物理演算の物理点の個数
+ settings: csmVector; // 物理演算の物理点の管理のリスト
+ inputs: csmVector; // 物理演算の入力のリスト
+ outputs: csmVector; // 物理演算の出力のリスト
+ particles: csmVector; // 物理演算の物理点のリスト
+ gravity: CubismVector2; // 重力
+ wind: CubismVector2; // 風
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismphysicsinternal';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismPhysicsInput = $.CubismPhysicsInput;
+ export type CubismPhysicsInput = $.CubismPhysicsInput;
+ export const CubismPhysicsNormalization = $.CubismPhysicsNormalization;
+ export type CubismPhysicsNormalization = $.CubismPhysicsNormalization;
+ export const CubismPhysicsOutput = $.CubismPhysicsOutput;
+ export type CubismPhysicsOutput = $.CubismPhysicsOutput;
+ export const CubismPhysicsParameter = $.CubismPhysicsParameter;
+ export type CubismPhysicsParameter = $.CubismPhysicsParameter;
+ export const CubismPhysicsParticle = $.CubismPhysicsParticle;
+ export type CubismPhysicsParticle = $.CubismPhysicsParticle;
+ export const CubismPhysicsRig = $.CubismPhysicsRig;
+ export type CubismPhysicsRig = $.CubismPhysicsRig;
+ export const CubismPhysicsSource = $.CubismPhysicsSource;
+ export type CubismPhysicsSource = $.CubismPhysicsSource;
+ export const CubismPhysicsSubRig = $.CubismPhysicsSubRig;
+ export type CubismPhysicsSubRig = $.CubismPhysicsSubRig;
+ export const CubismPhysicsTargetType = $.CubismPhysicsTargetType;
+ export type CubismPhysicsTargetType = $.CubismPhysicsTargetType;
+ export const PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
+ export type PhysicsJsonEffectiveForces = $.PhysicsJsonEffectiveForces;
+ export type normalizedPhysicsParameterValueGetter = $.normalizedPhysicsParameterValueGetter;
+ export type physicsScaleGetter = $.physicsScaleGetter;
+ export type physicsValueGetter = $.physicsValueGetter;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsjson.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsjson.ts
new file mode 100644
index 000000000..35c4529b4
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/physics/cubismphysicsjson.ts
@@ -0,0 +1,648 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismIdHandle } from '../id/cubismid';
+import { CubismFramework } from '../live2dcubismframework';
+import { CubismVector2 } from '../math/cubismvector2';
+import { CubismJson } from '../utils/cubismjson';
+
+// JSON keys
+const Position = 'Position';
+const X = 'X';
+const Y = 'Y';
+const Angle = 'Angle';
+const Type = 'Type';
+const Id = 'Id';
+
+// Meta
+const Meta = 'Meta';
+const EffectiveForces = 'EffectiveForces';
+const TotalInputCount = 'TotalInputCount';
+const TotalOutputCount = 'TotalOutputCount';
+const PhysicsSettingCount = 'PhysicsSettingCount';
+const Gravity = 'Gravity';
+const Wind = 'Wind';
+const VertexCount = 'VertexCount';
+
+// PhysicsSettings
+const PhysicsSettings = 'PhysicsSettings';
+const Normalization = 'Normalization';
+const Minimum = 'Minimum';
+const Maximum = 'Maximum';
+const Default = 'Default';
+const Reflect = 'Reflect';
+const Weight = 'Weight';
+
+// Input
+const Input = 'Input';
+const Source = 'Source';
+
+// Output
+const Output = 'Output';
+const Scale = 'Scale';
+const VertexIndex = 'VertexIndex';
+const Destination = 'Destination';
+
+// Particle
+const Vertices = 'Vertices';
+const Mobility = 'Mobility';
+const Delay = 'Delay';
+const Radius = 'Radius';
+const Acceleration = 'Acceleration';
+
+/**
+ * physics3.jsonのコンテナ。
+ */
+export class CubismPhysicsJson {
+ /**
+ * コンストラクタ
+ * @param buffer physics3.jsonが読み込まれているバッファ
+ * @param size バッファのサイズ
+ */
+ public constructor(buffer: ArrayBuffer, size: number) {
+ this._json = CubismJson.create(buffer, size);
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ CubismJson.delete(this._json);
+ }
+
+ /**
+ * 重力の取得
+ * @return 重力
+ */
+ public getGravity(): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0, 0);
+ ret.x = this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(EffectiveForces)
+ .getValueByString(Gravity)
+ .getValueByString(X)
+ .toFloat();
+ ret.y = this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(EffectiveForces)
+ .getValueByString(Gravity)
+ .getValueByString(Y)
+ .toFloat();
+ return ret;
+ }
+
+ /**
+ * 風の取得
+ * @return 風
+ */
+ public getWind(): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0, 0);
+ ret.x = this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(EffectiveForces)
+ .getValueByString(Wind)
+ .getValueByString(X)
+ .toFloat();
+ ret.y = this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(EffectiveForces)
+ .getValueByString(Wind)
+ .getValueByString(Y)
+ .toFloat();
+ return ret;
+ }
+
+ /**
+ * 物理店の管理の個数の取得
+ * @return 物理店の管理の個数
+ */
+ public getSubRigCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(PhysicsSettingCount)
+ .toInt();
+ }
+
+ /**
+ * 入力の総合計の取得
+ * @return 入力の総合計
+ */
+ public getTotalInputCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalInputCount)
+ .toInt();
+ }
+
+ /**
+ * 出力の総合計の取得
+ * @return 出力の総合計
+ */
+ public getTotalOutputCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(TotalOutputCount)
+ .toInt();
+ }
+
+ /**
+ * 物理点の個数の取得
+ * @return 物理点の個数
+ */
+ public getVertexCount(): number {
+ return this._json
+ .getRoot()
+ .getValueByString(Meta)
+ .getValueByString(VertexCount)
+ .toInt();
+ }
+
+ /**
+ * 正規化された位置の最小値の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 正規化された位置の最小値
+ */
+ public getNormalizationPositionMinimumValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Position)
+ .getValueByString(Minimum)
+ .toFloat();
+ }
+
+ /**
+ * 正規化された位置の最大値の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 正規化された位置の最大値
+ */
+ public getNormalizationPositionMaximumValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Position)
+ .getValueByString(Maximum)
+ .toFloat();
+ }
+
+ /**
+ * 正規化された位置のデフォルト値の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 正規化された位置のデフォルト値
+ */
+ public getNormalizationPositionDefaultValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Position)
+ .getValueByString(Default)
+ .toFloat();
+ }
+
+ /**
+ * 正規化された角度の最小値の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 正規化された角度の最小値
+ */
+ public getNormalizationAngleMinimumValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Angle)
+ .getValueByString(Minimum)
+ .toFloat();
+ }
+
+ /**
+ * 正規化された角度の最大値の取得
+ * @param physicsSettingIndex
+ * @return 正規化された角度の最大値
+ */
+ public getNormalizationAngleMaximumValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Angle)
+ .getValueByString(Maximum)
+ .toFloat();
+ }
+
+ /**
+ * 正規化された角度のデフォルト値の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 正規化された角度のデフォルト値
+ */
+ public getNormalizationAngleDefaultValue(
+ physicsSettingIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Normalization)
+ .getValueByString(Angle)
+ .getValueByString(Default)
+ .toFloat();
+ }
+
+ /**
+ * 入力の個数の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 入力の個数
+ */
+ public getInputCount(physicsSettingIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Input)
+ .getVector()
+ .getSize();
+ }
+
+ /**
+ * 入力の重みの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param inputIndex 入力のインデックス
+ * @return 入力の重み
+ */
+ public getInputWeight(
+ physicsSettingIndex: number,
+ inputIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Input)
+ .getValueByIndex(inputIndex)
+ .getValueByString(Weight)
+ .toFloat();
+ }
+
+ /**
+ * 入力の反転の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param inputIndex 入力のインデックス
+ * @return 入力の反転
+ */
+ public getInputReflect(
+ physicsSettingIndex: number,
+ inputIndex: number
+ ): boolean {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Input)
+ .getValueByIndex(inputIndex)
+ .getValueByString(Reflect)
+ .toBoolean();
+ }
+
+ /**
+ * 入力の種類の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param inputIndex 入力のインデックス
+ * @return 入力の種類
+ */
+ public getInputType(physicsSettingIndex: number, inputIndex: number): string {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Input)
+ .getValueByIndex(inputIndex)
+ .getValueByString(Type)
+ .getRawString();
+ }
+
+ /**
+ * 入力元のIDの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param inputIndex 入力のインデックス
+ * @return 入力元のID
+ */
+ public getInputSourceId(
+ physicsSettingIndex: number,
+ inputIndex: number
+ ): CubismIdHandle {
+ return CubismFramework.getIdManager().getId(
+ this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Input)
+ .getValueByIndex(inputIndex)
+ .getValueByString(Source)
+ .getValueByString(Id)
+ .getRawString()
+ );
+ }
+
+ /**
+ * 出力の個数の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @return 出力の個数
+ */
+ public getOutputCount(physicsSettingIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getVector()
+ .getSize();
+ }
+
+ /**
+ * 出力の物理点のインデックスの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力の物理点のインデックス
+ */
+ public getOutputVertexIndex(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(VertexIndex)
+ .toInt();
+ }
+
+ /**
+ * 出力の角度のスケールを取得する
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力の角度のスケール
+ */
+ public getOutputAngleScale(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(Scale)
+ .toFloat();
+ }
+
+ /**
+ * 出力の重みの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力の重み
+ */
+ public getOutputWeight(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(Weight)
+ .toFloat();
+ }
+
+ /**
+ * 出力先のIDの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力先のID
+ */
+ public getOutputDestinationId(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): CubismIdHandle {
+ return CubismFramework.getIdManager().getId(
+ this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(Destination)
+ .getValueByString(Id)
+ .getRawString()
+ );
+ }
+
+ /**
+ * 出力の種類の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力の種類
+ */
+ public getOutputType(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): string {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(Type)
+ .getRawString();
+ }
+
+ /**
+ * 出力の反転の取得
+ * @param physicsSettingIndex 物理演算のインデックス
+ * @param outputIndex 出力のインデックス
+ * @return 出力の反転
+ */
+ public getOutputReflect(
+ physicsSettingIndex: number,
+ outputIndex: number
+ ): boolean {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Output)
+ .getValueByIndex(outputIndex)
+ .getValueByString(Reflect)
+ .toBoolean();
+ }
+
+ /**
+ * 物理点の個数の取得
+ * @param physicsSettingIndex 物理演算男設定のインデックス
+ * @return 物理点の個数
+ */
+ public getParticleCount(physicsSettingIndex: number): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getVector()
+ .getSize();
+ }
+
+ /**
+ * 物理点の動きやすさの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param vertexIndex 物理点のインデックス
+ * @return 物理点の動きやすさ
+ */
+ public getParticleMobility(
+ physicsSettingIndex: number,
+ vertexIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Mobility)
+ .toFloat();
+ }
+
+ /**
+ * 物理点の遅れの取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param vertexIndex 物理点のインデックス
+ * @return 物理点の遅れ
+ */
+ public getParticleDelay(
+ physicsSettingIndex: number,
+ vertexIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Delay)
+ .toFloat();
+ }
+
+ /**
+ * 物理点の加速度の取得
+ * @param physicsSettingIndex 物理演算の設定
+ * @param vertexIndex 物理点のインデックス
+ * @return 物理点の加速度
+ */
+ public getParticleAcceleration(
+ physicsSettingIndex: number,
+ vertexIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Acceleration)
+ .toFloat();
+ }
+
+ /**
+ * 物理点の距離の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param vertexIndex 物理点のインデックス
+ * @return 物理点の距離
+ */
+ public getParticleRadius(
+ physicsSettingIndex: number,
+ vertexIndex: number
+ ): number {
+ return this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Radius)
+ .toFloat();
+ }
+
+ /**
+ * 物理点の位置の取得
+ * @param physicsSettingIndex 物理演算の設定のインデックス
+ * @param vertexInde 物理点のインデックス
+ * @return 物理点の位置
+ */
+ public getParticlePosition(
+ physicsSettingIndex: number,
+ vertexIndex: number
+ ): CubismVector2 {
+ const ret: CubismVector2 = new CubismVector2(0, 0);
+ ret.x = this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Position)
+ .getValueByString(X)
+ .toFloat();
+ ret.y = this._json
+ .getRoot()
+ .getValueByString(PhysicsSettings)
+ .getValueByIndex(physicsSettingIndex)
+ .getValueByString(Vertices)
+ .getValueByIndex(vertexIndex)
+ .getValueByString(Position)
+ .getValueByString(Y)
+ .toFloat();
+ return ret;
+ }
+
+ _json: CubismJson; // physics3.jsonデータ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismphysicsjson';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismPhysicsJson = $.CubismPhysicsJson;
+ export type CubismPhysicsJson = $.CubismPhysicsJson;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer.ts
new file mode 100644
index 000000000..c69aaadec
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer.ts
@@ -0,0 +1,275 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismMatrix44 } from '../math/cubismmatrix44';
+import { CubismModel } from '../model/cubismmodel';
+
+/**
+ * モデル描画を処理するレンダラ
+ *
+ * サブクラスに環境依存の描画命令を記述する。
+ */
+export abstract class CubismRenderer {
+ /**
+ * レンダラのインスタンスを生成して取得する
+ *
+ * @return レンダラのインスタンス
+ */
+ public static create(): CubismRenderer {
+ return null;
+ }
+
+ /**
+ * レンダラのインスタンスを解放する
+ */
+ public static delete(renderer: CubismRenderer): void {
+ renderer = null;
+ }
+
+ /**
+ * レンダラの初期化処理を実行する
+ * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる
+ * @param model モデルのインスタンス
+ */
+ public initialize(model: CubismModel): void {
+ this._model = model;
+ }
+
+ /**
+ * モデルを描画する
+ */
+ public drawModel(): void {
+ if (this.getModel() == null) return;
+
+ this.doDrawModel();
+ }
+
+ /**
+ * Model-View-Projection 行列をセットする
+ * 配列は複製されるので、元の配列は外で破棄して良い
+ * @param matrix44 Model-View-Projection 行列
+ */
+ public setMvpMatrix(matrix44: CubismMatrix44): void {
+ this._mvpMatrix4x4.setMatrix(matrix44.getArray());
+ }
+
+ /**
+ * Model-View-Projection 行列を取得する
+ * @return Model-View-Projection 行列
+ */
+ public getMvpMatrix(): CubismMatrix44 {
+ return this._mvpMatrix4x4;
+ }
+
+ /**
+ * モデルの色をセットする
+ * 各色0.0~1.0の間で指定する(1.0が標準の状態)
+ * @param red 赤チャンネルの値
+ * @param green 緑チャンネルの値
+ * @param blue 青チャンネルの値
+ * @param alpha αチャンネルの値
+ */
+ public setModelColor(
+ red: number,
+ green: number,
+ blue: number,
+ alpha: number
+ ): void {
+ if (red < 0.0) {
+ red = 0.0;
+ } else if (red > 1.0) {
+ red = 1.0;
+ }
+
+ if (green < 0.0) {
+ green = 0.0;
+ } else if (green > 1.0) {
+ green = 1.0;
+ }
+
+ if (blue < 0.0) {
+ blue = 0.0;
+ } else if (blue > 1.0) {
+ blue = 1.0;
+ }
+
+ if (alpha < 0.0) {
+ alpha = 0.0;
+ } else if (alpha > 1.0) {
+ alpha = 1.0;
+ }
+
+ this._modelColor.R = red;
+ this._modelColor.G = green;
+ this._modelColor.B = blue;
+ this._modelColor.A = alpha;
+ }
+
+ /**
+ * モデルの色を取得する
+ * 各色0.0~1.0の間で指定する(1.0が標準の状態)
+ *
+ * @return RGBAのカラー情報
+ */
+ public getModelColor(): CubismTextureColor {
+ return JSON.parse(JSON.stringify(this._modelColor));
+ }
+
+ /**
+ * 乗算済みαの有効・無効をセットする
+ * 有効にするならtrue、無効にするならfalseをセットする
+ */
+ public setIsPremultipliedAlpha(enable: boolean): void {
+ this._isPremultipliedAlpha = enable;
+ }
+
+ /**
+ * 乗算済みαの有効・無効を取得する
+ * @return true 乗算済みのα有効
+ * @return false 乗算済みのα無効
+ */
+ public isPremultipliedAlpha(): boolean {
+ return this._isPremultipliedAlpha;
+ }
+
+ /**
+ * カリング(片面描画)の有効・無効をセットする。
+ * 有効にするならtrue、無効にするならfalseをセットする
+ */
+ public setIsCulling(culling: boolean): void {
+ this._isCulling = culling;
+ }
+
+ /**
+ * カリング(片面描画)の有効・無効を取得する。
+ * @return true カリング有効
+ * @return false カリング無効
+ */
+ public isCulling(): boolean {
+ return this._isCulling;
+ }
+
+ /**
+ * テクスチャの異方性フィルタリングのパラメータをセットする
+ * パラメータ値の影響度はレンダラの実装に依存する
+ * @param n パラメータの値
+ */
+ public setAnisotropy(n: number): void {
+ this._anisortopy = n;
+ }
+
+ /**
+ * テクスチャの異方性フィルタリングのパラメータをセットする
+ * @return 異方性フィルタリングのパラメータ
+ */
+ public getAnisotropy(): number {
+ return this._anisortopy;
+ }
+
+ /**
+ * レンダリングするモデルを取得する
+ * @return レンダリングするモデル
+ */
+ public getModel(): CubismModel {
+ return this._model;
+ }
+
+ /**
+ * コンストラクタ
+ */
+ protected constructor() {
+ this._isCulling = false;
+ this._isPremultipliedAlpha = false;
+ this._anisortopy = 0.0;
+ this._model = null;
+ this._modelColor = new CubismTextureColor();
+
+ // 単位行列に初期化
+ this._mvpMatrix4x4 = new CubismMatrix44();
+ this._mvpMatrix4x4.loadIdentity();
+ }
+
+ /**
+ * モデル描画の実装
+ */
+ public abstract doDrawModel(): void;
+
+ /**
+ * 描画オブジェクト(アートメッシュ)を描画する
+ * ポリゴンメッシュとテクスチャ番号をセットで渡す。
+ * @param textureNo 描画するテクスチャ番号
+ * @param indexCount 描画オブジェクトのインデックス値
+ * @param vertexCount ポリゴンメッシュの頂点数
+ * @param indexArray ポリゴンメッシュ頂点のインデックス配列
+ * @param vertexArray ポリゴンメッシュの頂点配列
+ * @param uvArray uv配列
+ * @param opacity 不透明度
+ * @param colorBlendMode カラーブレンディングのタイプ
+ * @param invertedMask マスク使用時のマスクの反転使用
+ */
+ public abstract drawMesh(
+ textureNo: number,
+ indexCount: number,
+ vertexCount: number,
+ indexArray: Uint16Array,
+ vertexArray: Float32Array,
+ uvArray: Float32Array,
+ opacity: number,
+ colorBlendMode: CubismBlendMode,
+ invertedMask: boolean
+ ): void;
+
+ /**
+ * レンダラが保持する静的なリソースを開放する
+ */
+ public static staticRelease: Function;
+
+ protected _mvpMatrix4x4: CubismMatrix44; // Model-View-Projection 行列
+ protected _modelColor: CubismTextureColor; // モデル自体のカラー(RGBA)
+ protected _isCulling: boolean; // カリングが有効ならtrue
+ protected _isPremultipliedAlpha: boolean; // 乗算済みαならtrue
+ protected _anisortopy: any; // テクスチャの異方性フィルタリングのパラメータ
+ protected _model: CubismModel; // レンダリング対象のモデル
+}
+
+export enum CubismBlendMode {
+ CubismBlendMode_Normal = 0, // 通常
+ CubismBlendMode_Additive = 1, // 加算
+ CubismBlendMode_Multiplicative = 2 // 乗算
+}
+
+/**
+ * テクスチャの色をRGBAで扱うためのクラス
+ */
+export class CubismTextureColor {
+ /**
+ * コンストラクタ
+ */
+ constructor() {
+ this.R = 1.0;
+ this.G = 1.0;
+ this.B = 1.0;
+ this.A = 1.0;
+ }
+
+ R: number; // 赤チャンネル
+ G: number; // 緑チャンネル
+ B: number; // 青チャンネル
+ A: number; // αチャンネル
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismrenderer';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismBlendMode = $.CubismBlendMode;
+ export type CubismBlendMode = $.CubismBlendMode;
+ export const CubismRenderer = $.CubismRenderer;
+ export type CubismRenderer = $.CubismRenderer;
+ export const CubismTextureColor = $.CubismTextureColor;
+ export type CubismTextureColor = $.CubismTextureColor;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer_webgl.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer_webgl.ts
new file mode 100644
index 000000000..51290a19e
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/rendering/cubismrenderer_webgl.ts
@@ -0,0 +1,2222 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { Constant } from '../live2dcubismframework';
+import { CubismMatrix44 } from '../math/cubismmatrix44';
+import { CubismModel } from '../model/cubismmodel';
+import { csmMap } from '../type/csmmap';
+import { csmRect } from '../type/csmrectf';
+import { csmVector } from '../type/csmvector';
+import { CubismLogError } from '../utils/cubismdebug';
+import {
+ CubismBlendMode,
+ CubismRenderer,
+ CubismTextureColor
+} from './cubismrenderer';
+
+const ColorChannelCount = 4; // 実験時に1チャンネルの場合は1、RGBだけの場合は3、アルファも含める場合は4
+
+const shaderCount = 10; // シェーダーの数 = マスク生成用 + (通常用 + 加算 + 乗算) * (マスク無の乗算済アルファ対応版 + マスク有の乗算済アルファ対応版 + マスク有反転の乗算済アルファ対応版)
+let s_instance: CubismShader_WebGL;
+let s_viewport: number[];
+let s_fbo: WebGLFramebuffer;
+
+/**
+ * クリッピングマスクの処理を実行するクラス
+ */
+export class CubismClippingManager_WebGL {
+ /**
+ * カラーチャンネル(RGBA)のフラグを取得する
+ * @param channelNo カラーチャンネル(RGBA)の番号(0:R, 1:G, 2:B, 3:A)
+ */
+ public getChannelFlagAsColor(channelNo: number): CubismTextureColor {
+ return this._channelColors.at(channelNo);
+ }
+
+ /**
+ * テンポラリのレンダーテクスチャのアドレスを取得する
+ * FrameBufferObjectが存在しない場合、新しく生成する
+ *
+ * @return レンダーテクスチャのアドレス
+ */
+ public getMaskRenderTexture(): WebGLFramebuffer {
+ let ret: WebGLFramebuffer = 0;
+
+ // テンポラリのRenderTextureを取得する
+ if (this._maskTexture && this._maskTexture.texture != 0) {
+ // 前回使ったものを返す
+ this._maskTexture.frameNo = this._currentFrameNo;
+ ret = this._maskTexture.texture;
+ }
+
+ if (ret == 0) {
+ // FrameBufferObjectが存在しない場合、新しく生成する
+
+ // クリッピングバッファサイズを取得
+ const size: number = this._clippingMaskBufferSize;
+
+ this._colorBuffer = this.gl.createTexture();
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this._colorBuffer);
+ this.gl.texImage2D(
+ this.gl.TEXTURE_2D,
+ 0,
+ this.gl.RGBA,
+ size,
+ size,
+ 0,
+ this.gl.RGBA,
+ this.gl.UNSIGNED_BYTE,
+ null
+ );
+ this.gl.texParameteri(
+ this.gl.TEXTURE_2D,
+ this.gl.TEXTURE_WRAP_S,
+ this.gl.CLAMP_TO_EDGE
+ );
+ this.gl.texParameteri(
+ this.gl.TEXTURE_2D,
+ this.gl.TEXTURE_WRAP_T,
+ this.gl.CLAMP_TO_EDGE
+ );
+ this.gl.texParameteri(
+ this.gl.TEXTURE_2D,
+ this.gl.TEXTURE_MIN_FILTER,
+ this.gl.LINEAR
+ );
+ this.gl.texParameteri(
+ this.gl.TEXTURE_2D,
+ this.gl.TEXTURE_MAG_FILTER,
+ this.gl.LINEAR
+ );
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
+
+ ret = this.gl.createFramebuffer();
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, ret);
+ this.gl.framebufferTexture2D(
+ this.gl.FRAMEBUFFER,
+ this.gl.COLOR_ATTACHMENT0,
+ this.gl.TEXTURE_2D,
+ this._colorBuffer,
+ 0
+ );
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo);
+
+ this._maskTexture = new CubismRenderTextureResource(
+ this._currentFrameNo,
+ ret
+ );
+ }
+
+ return ret;
+ }
+
+ /**
+ * WebGLレンダリングコンテキストを設定する
+ * @param gl WebGLレンダリングコンテキスト
+ */
+ public setGL(gl: WebGLRenderingContext): void {
+ this.gl = gl;
+ }
+
+ /**
+ * マスクされる描画オブジェクト群全体を囲む矩形(モデル座標系)を計算する
+ * @param model モデルのインスタンス
+ * @param clippingContext クリッピングマスクのコンテキスト
+ */
+ public calcClippedDrawTotalBounds(
+ model: CubismModel,
+ clippingContext: CubismClippingContext
+ ): void {
+ // 被クリッピングマスク(マスクされる描画オブジェクト)の全体の矩形
+ let clippedDrawTotalMinX: number = Number.MAX_VALUE;
+ let clippedDrawTotalMinY: number = Number.MAX_VALUE;
+ let clippedDrawTotalMaxX: number = Number.MIN_VALUE;
+ let clippedDrawTotalMaxY: number = Number.MIN_VALUE;
+
+ // このマスクが実際に必要か判定する
+ // このクリッピングを利用する「描画オブジェクト」がひとつでも使用可能であればマスクを生成する必要がある
+ const clippedDrawCount: number =
+ clippingContext._clippedDrawableIndexList.length;
+
+ for (
+ let clippedDrawableIndex = 0;
+ clippedDrawableIndex < clippedDrawCount;
+ clippedDrawableIndex++
+ ) {
+ // マスクを使用する描画オブジェクトの描画される矩形を求める
+ const drawableIndex: number =
+ clippingContext._clippedDrawableIndexList[clippedDrawableIndex];
+
+ const drawableVertexCount: number = model.getDrawableVertexCount(
+ drawableIndex
+ );
+ const drawableVertexes: Float32Array = model.getDrawableVertices(
+ drawableIndex
+ );
+
+ let minX: number = Number.MAX_VALUE;
+ let minY: number = Number.MAX_VALUE;
+ let maxX: number = Number.MIN_VALUE;
+ let maxY: number = Number.MIN_VALUE;
+
+ const loop: number = drawableVertexCount * Constant.vertexStep;
+ for (
+ let pi: number = Constant.vertexOffset;
+ pi < loop;
+ pi += Constant.vertexStep
+ ) {
+ const x: number = drawableVertexes[pi];
+ const y: number = drawableVertexes[pi + 1];
+
+ if (x < minX) {
+ minX = x;
+ }
+ if (x > maxX) {
+ maxX = x;
+ }
+ if (y < minY) {
+ minY = y;
+ }
+ if (y > maxY) {
+ maxY = y;
+ }
+ }
+
+ // 有効な点が一つも取れなかったのでスキップ
+ if (minX == Number.MAX_VALUE) {
+ continue;
+ }
+
+ // 全体の矩形に反映
+ if (minX < clippedDrawTotalMinX) {
+ clippedDrawTotalMinX = minX;
+ }
+ if (minY < clippedDrawTotalMinY) {
+ clippedDrawTotalMinY = minY;
+ }
+ if (maxX > clippedDrawTotalMaxX) {
+ clippedDrawTotalMaxX = maxX;
+ }
+ if (maxY > clippedDrawTotalMaxY) {
+ clippedDrawTotalMaxY = maxY;
+ }
+
+ if (clippedDrawTotalMinX == Number.MAX_VALUE) {
+ clippingContext._allClippedDrawRect.x = 0.0;
+ clippingContext._allClippedDrawRect.y = 0.0;
+ clippingContext._allClippedDrawRect.width = 0.0;
+ clippingContext._allClippedDrawRect.height = 0.0;
+ clippingContext._isUsing = false;
+ } else {
+ clippingContext._isUsing = true;
+ const w: number = clippedDrawTotalMaxX - clippedDrawTotalMinX;
+ const h: number = clippedDrawTotalMaxY - clippedDrawTotalMinY;
+ clippingContext._allClippedDrawRect.x = clippedDrawTotalMinX;
+ clippingContext._allClippedDrawRect.y = clippedDrawTotalMinY;
+ clippingContext._allClippedDrawRect.width = w;
+ clippingContext._allClippedDrawRect.height = h;
+ }
+ }
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ this._maskRenderTexture = null;
+ this._colorBuffer = null;
+ this._currentFrameNo = 0;
+ this._clippingMaskBufferSize = 256;
+ this._clippingContextListForMask = new csmVector();
+ this._clippingContextListForDraw = new csmVector();
+ this._channelColors = new csmVector();
+ this._tmpBoundsOnModel = new csmRect();
+ this._tmpMatrix = new CubismMatrix44();
+ this._tmpMatrixForMask = new CubismMatrix44();
+ this._tmpMatrixForDraw = new CubismMatrix44();
+ this._maskTexture = null;
+
+ let tmp: CubismTextureColor = new CubismTextureColor();
+ tmp.R = 1.0;
+ tmp.G = 0.0;
+ tmp.B = 0.0;
+ tmp.A = 0.0;
+ this._channelColors.pushBack(tmp);
+
+ tmp = new CubismTextureColor();
+ tmp.R = 0.0;
+ tmp.G = 1.0;
+ tmp.B = 0.0;
+ tmp.A = 0.0;
+ this._channelColors.pushBack(tmp);
+
+ tmp = new CubismTextureColor();
+ tmp.R = 0.0;
+ tmp.G = 0.0;
+ tmp.B = 1.0;
+ tmp.A = 0.0;
+ this._channelColors.pushBack(tmp);
+
+ tmp = new CubismTextureColor();
+ tmp.R = 0.0;
+ tmp.G = 0.0;
+ tmp.B = 0.0;
+ tmp.A = 1.0;
+ this._channelColors.pushBack(tmp);
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) {
+ if (this._clippingContextListForMask.at(i)) {
+ this._clippingContextListForMask.at(i).release();
+ this._clippingContextListForMask.set(i, void 0);
+ }
+ this._clippingContextListForMask.set(i, null);
+ }
+ this._clippingContextListForMask = null;
+
+ // _clippingContextListForDrawは_clippingContextListForMaskにあるインスタンスを指している。上記の処理により要素ごとのDELETEは不要。
+ for (let i = 0; i < this._clippingContextListForDraw.getSize(); i++) {
+ this._clippingContextListForDraw.set(i, null);
+ }
+ this._clippingContextListForDraw = null;
+
+ if (this._maskTexture) {
+ this.gl.deleteFramebuffer(this._maskTexture.texture);
+ this._maskTexture = null;
+ }
+
+ for (let i = 0; i < this._channelColors.getSize(); i++) {
+ this._channelColors.set(i, null);
+ }
+
+ this._channelColors = null;
+
+ // テクスチャ解放
+ this.gl.deleteTexture(this._colorBuffer);
+ this._colorBuffer = null;
+ }
+
+ /**
+ * マネージャの初期化処理
+ * クリッピングマスクを使う描画オブジェクトの登録を行う
+ * @param model モデルのインスタンス
+ * @param drawableCount 描画オブジェクトの数
+ * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのインデックスのリスト
+ * @param drawableCounts 描画オブジェクトをマスクする描画オブジェクトの数
+ */
+ public initialize(
+ model: CubismModel,
+ drawableCount: number,
+ drawableMasks: Int32Array[],
+ drawableMaskCounts: Int32Array
+ ): void {
+ // クリッピングマスクを使う描画オブジェクトをすべて登録する
+ // クリッピングマスクは、通常数個程度に限定して使うものとする
+ for (let i = 0; i < drawableCount; i++) {
+ if (drawableMaskCounts[i] <= 0) {
+ // クリッピングマスクが使用されていないアートメッシュ(多くの場合使用しない)
+ this._clippingContextListForDraw.pushBack(null);
+ continue;
+ }
+
+ // 既にあるClipContextと同じかチェックする
+ let clippingContext: CubismClippingContext = this.findSameClip(
+ drawableMasks[i],
+ drawableMaskCounts[i]
+ );
+ if (clippingContext == null) {
+ // 同一のマスクが存在していない場合は生成する
+ clippingContext = new CubismClippingContext(
+ this,
+ drawableMasks[i],
+ drawableMaskCounts[i]
+ );
+ this._clippingContextListForMask.pushBack(clippingContext);
+ }
+
+ clippingContext.addClippedDrawable(i);
+
+ this._clippingContextListForDraw.pushBack(clippingContext);
+ }
+ }
+
+ /**
+ * クリッピングコンテキストを作成する。モデル描画時に実行する。
+ * @param model モデルのインスタンス
+ * @param renderer レンダラのインスタンス
+ */
+ public setupClippingContext(
+ model: CubismModel,
+ renderer: CubismRenderer_WebGL
+ ): void {
+ this._currentFrameNo++;
+
+ // 全てのクリッピングを用意する
+ // 同じクリップ(複数の場合はまとめて一つのクリップ)を使う場合は1度だけ設定する
+ let usingClipCount = 0;
+ for (
+ let clipIndex = 0;
+ clipIndex < this._clippingContextListForMask.getSize();
+ clipIndex++
+ ) {
+ // 1つのクリッピングマスクに関して
+ const cc: CubismClippingContext = this._clippingContextListForMask.at(
+ clipIndex
+ );
+
+ // このクリップを利用する描画オブジェクト群全体を囲む矩形を計算
+ this.calcClippedDrawTotalBounds(model, cc);
+
+ if (cc._isUsing) {
+ usingClipCount++; // 使用中としてカウント
+ }
+ }
+
+ // マスク作成処理
+ if (usingClipCount > 0) {
+ // 生成したFrameBufferと同じサイズでビューポートを設定
+ this.gl.viewport(
+ 0,
+ 0,
+ this._clippingMaskBufferSize,
+ this._clippingMaskBufferSize
+ );
+
+ // マスクをactiveにする
+ this._maskRenderTexture = this.getMaskRenderTexture();
+
+ // モデル描画時にDrawMeshNowに渡される変換(モデルtoワールド座標変換)
+ const modelToWorldF: CubismMatrix44 = renderer.getMvpMatrix();
+
+ renderer.preDraw(); // バッファをクリアする
+
+ // 各マスクのレイアウトを決定していく
+ this.setupLayoutBounds(usingClipCount);
+
+ // ---------- マスク描画処理 ----------
+ // マスク用RenderTextureをactiveにセット
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this._maskRenderTexture);
+
+ // マスクをクリアする
+ // (仮仕様) 1が無効(描かれない)領域、0が有効(描かれる)領域。(シェーダーCd*Csで0に近い値をかけてマスクを作る。1をかけると何も起こらない)
+ this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
+
+ // 実際にマスクを生成する
+ // 全てのマスクをどのようにレイアウトして描くかを決定し、ClipContext, ClippedDrawContextに記憶する
+ for (
+ let clipIndex = 0;
+ clipIndex < this._clippingContextListForMask.getSize();
+ clipIndex++
+ ) {
+ // --- 実際に1つのマスクを描く ---
+ const clipContext: CubismClippingContext = this._clippingContextListForMask.at(
+ clipIndex
+ );
+ const allClipedDrawRect: csmRect = clipContext._allClippedDrawRect; // このマスクを使う、すべての描画オブジェクトの論理座標上の囲み矩形
+ const layoutBoundsOnTex01: csmRect = clipContext._layoutBounds; // この中にマスクを収める
+
+ // モデル座標上の矩形を、適宜マージンを付けて使う
+ const MARGIN = 0.05;
+ this._tmpBoundsOnModel.setRect(allClipedDrawRect);
+ this._tmpBoundsOnModel.expand(
+ allClipedDrawRect.width * MARGIN,
+ allClipedDrawRect.height * MARGIN
+ );
+ //########## 本来は割り当てられた領域の全体を使わず必要最低限のサイズがよい
+
+ // シェーダ用の計算式を求める。回転を考慮しない場合は以下のとおり
+ // movePeriod' = movePeriod * scaleX + offX [[ movePeriod' = (movePeriod - tmpBoundsOnModel.movePeriod)*scale + layoutBoundsOnTex01.movePeriod ]]
+ const scaleX: number =
+ layoutBoundsOnTex01.width / this._tmpBoundsOnModel.width;
+ const scaleY: number =
+ layoutBoundsOnTex01.height / this._tmpBoundsOnModel.height;
+
+ // マスク生成時に使う行列を求める
+ {
+ // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる)
+ this._tmpMatrix.loadIdentity();
+ {
+ // layout0..1 を -1..1に変換
+ this._tmpMatrix.translateRelative(-1.0, -1.0);
+ this._tmpMatrix.scaleRelative(2.0, 2.0);
+ }
+ {
+ // view to layout0..1
+ this._tmpMatrix.translateRelative(
+ layoutBoundsOnTex01.x,
+ layoutBoundsOnTex01.y
+ );
+ this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale]
+ this._tmpMatrix.translateRelative(
+ -this._tmpBoundsOnModel.x,
+ -this._tmpBoundsOnModel.y
+ );
+ // new = [translate][scale][translate]
+ }
+ // tmpMatrixForMaskが計算結果
+ this._tmpMatrixForMask.setMatrix(this._tmpMatrix.getArray());
+ }
+
+ //--------- draw時の mask 参照用行列を計算
+ {
+ // シェーダに渡す行列を求める <<<<<<<<<<<<<<<<<<<<<<<< 要最適化(逆順に計算すればシンプルにできる)
+ this._tmpMatrix.loadIdentity();
+ {
+ this._tmpMatrix.translateRelative(
+ layoutBoundsOnTex01.x,
+ layoutBoundsOnTex01.y
+ );
+ this._tmpMatrix.scaleRelative(scaleX, scaleY); // new = [translate][scale]
+ this._tmpMatrix.translateRelative(
+ -this._tmpBoundsOnModel.x,
+ -this._tmpBoundsOnModel.y
+ );
+ // new = [translate][scale][translate]
+ }
+ this._tmpMatrixForDraw.setMatrix(this._tmpMatrix.getArray());
+ }
+ clipContext._matrixForMask.setMatrix(this._tmpMatrixForMask.getArray());
+ clipContext._matrixForDraw.setMatrix(this._tmpMatrixForDraw.getArray());
+
+ const clipDrawCount: number = clipContext._clippingIdCount;
+ for (let i = 0; i < clipDrawCount; i++) {
+ const clipDrawIndex: number = clipContext._clippingIdList[i];
+
+ // 頂点情報が更新されておらず、信頼性がない場合は描画をパスする
+ if (
+ !model.getDrawableDynamicFlagVertexPositionsDidChange(clipDrawIndex)
+ ) {
+ continue;
+ }
+
+ renderer.setIsCulling(
+ model.getDrawableCulling(clipDrawIndex) != false
+ );
+
+ // 今回専用の変換を適用して描く
+ // チャンネルも切り替える必要がある(A,R,G,B)
+ renderer.setClippingContextBufferForMask(clipContext);
+ renderer.drawMesh(
+ model.getDrawableTextureIndices(clipDrawIndex),
+ model.getDrawableVertexIndexCount(clipDrawIndex),
+ model.getDrawableVertexCount(clipDrawIndex),
+ model.getDrawableVertexIndices(clipDrawIndex),
+ model.getDrawableVertices(clipDrawIndex),
+ model.getDrawableVertexUvs(clipDrawIndex),
+ model.getDrawableOpacity(clipDrawIndex),
+ CubismBlendMode.CubismBlendMode_Normal, // クリッピングは通常描画を強制
+ false // マスク生成時はクリッピングの反転使用は全く関係がない
+ );
+ }
+ }
+
+ // --- 後処理 ---
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, s_fbo); // 描画対象を戻す
+ renderer.setClippingContextBufferForMask(null);
+
+ this.gl.viewport(
+ s_viewport[0],
+ s_viewport[1],
+ s_viewport[2],
+ s_viewport[3]
+ );
+ }
+ }
+
+ /**
+ * 既にマスクを作っているかを確認
+ * 作っている様であれば該当するクリッピングマスクのインスタンスを返す
+ * 作っていなければNULLを返す
+ * @param drawableMasks 描画オブジェクトをマスクする描画オブジェクトのリスト
+ * @param drawableMaskCounts 描画オブジェクトをマスクする描画オブジェクトの数
+ * @return 該当するクリッピングマスクが存在すればインスタンスを返し、なければNULLを返す
+ */
+ public findSameClip(
+ drawableMasks: Int32Array,
+ drawableMaskCounts: number
+ ): CubismClippingContext {
+ // 作成済みClippingContextと一致するか確認
+ for (let i = 0; i < this._clippingContextListForMask.getSize(); i++) {
+ const clippingContext: CubismClippingContext = this._clippingContextListForMask.at(
+ i
+ );
+ const count: number = clippingContext._clippingIdCount;
+
+ // 個数が違う場合は別物
+ if (count != drawableMaskCounts) {
+ continue;
+ }
+
+ let sameCount = 0;
+
+ // 同じIDを持つか確認。配列の数が同じなので、一致した個数が同じなら同じ物を持つとする
+ for (let j = 0; j < count; j++) {
+ const clipId: number = clippingContext._clippingIdList[j];
+
+ for (let k = 0; k < count; k++) {
+ if (drawableMasks[k] == clipId) {
+ sameCount++;
+ break;
+ }
+ }
+ }
+
+ if (sameCount == count) {
+ return clippingContext;
+ }
+ }
+
+ return null; // 見つからなかった
+ }
+
+ /**
+ * クリッピングコンテキストを配置するレイアウト
+ * 一つのレンダーテクスチャを極力いっぱいに使ってマスクをレイアウトする
+ * マスクグループの数が4以下ならRGBA各チャンネルに一つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する。
+ *
+ * @param usingClipCount 配置するクリッピングコンテキストの数
+ */
+ public setupLayoutBounds(usingClipCount: number): void {
+ // ひとつのRenderTextureを極力いっぱいに使ってマスクをレイアウトする
+ // マスクグループの数が4以下ならRGBA各チャンネルに1つずつマスクを配置し、5以上6以下ならRGBAを2,2,1,1と配置する
+
+ // RGBAを順番に使っていく
+ let div: number = usingClipCount / ColorChannelCount; // 1チャンネルに配置する基本のマスク
+ let mod: number = usingClipCount % ColorChannelCount; // 余り、この番号のチャンネルまでに一つずつ配分する
+
+ // 小数点は切り捨てる
+ div = ~~div;
+ mod = ~~mod;
+
+ // RGBAそれぞれのチャンネルを用意していく(0:R, 1:G, 2:B, 3:A)
+ let curClipIndex = 0; // 順番に設定していく
+
+ for (let channelNo = 0; channelNo < ColorChannelCount; channelNo++) {
+ // このチャンネルにレイアウトする数
+ const layoutCount: number = div + (channelNo < mod ? 1 : 0);
+
+ // 分割方法を決定する
+ if (layoutCount == 0) {
+ // 何もしない
+ } else if (layoutCount == 1) {
+ // 全てをそのまま使う
+ const clipContext: CubismClippingContext = this._clippingContextListForMask.at(
+ curClipIndex++
+ );
+ clipContext._layoutChannelNo = channelNo;
+ clipContext._layoutBounds.x = 0.0;
+ clipContext._layoutBounds.y = 0.0;
+ clipContext._layoutBounds.width = 1.0;
+ clipContext._layoutBounds.height = 1.0;
+ } else if (layoutCount == 2) {
+ for (let i = 0; i < layoutCount; i++) {
+ let xpos: number = i % 2;
+
+ // 小数点は切り捨てる
+ xpos = ~~xpos;
+
+ const cc: CubismClippingContext = this._clippingContextListForMask.at(
+ curClipIndex++
+ );
+ cc._layoutChannelNo = channelNo;
+
+ cc._layoutBounds.x = xpos * 0.5;
+ cc._layoutBounds.y = 0.0;
+ cc._layoutBounds.width = 0.5;
+ cc._layoutBounds.height = 1.0;
+ // UVを2つに分解して使う
+ }
+ } else if (layoutCount <= 4) {
+ // 4分割して使う
+ for (let i = 0; i < layoutCount; i++) {
+ let xpos: number = i % 2;
+ let ypos: number = i / 2;
+
+ // 小数点は切り捨てる
+ xpos = ~~xpos;
+ ypos = ~~ypos;
+
+ const cc = this._clippingContextListForMask.at(curClipIndex++);
+ cc._layoutChannelNo = channelNo;
+
+ cc._layoutBounds.x = xpos * 0.5;
+ cc._layoutBounds.y = ypos * 0.5;
+ cc._layoutBounds.width = 0.5;
+ cc._layoutBounds.height = 0.5;
+ }
+ } else if (layoutCount <= 9) {
+ // 9分割して使う
+ for (let i = 0; i < layoutCount; i++) {
+ let xpos = i % 3;
+ let ypos = i / 3;
+
+ // 小数点は切り捨てる
+ xpos = ~~xpos;
+ ypos = ~~ypos;
+
+ const cc: CubismClippingContext = this._clippingContextListForMask.at(
+ curClipIndex++
+ );
+ cc._layoutChannelNo = channelNo;
+
+ cc._layoutBounds.x = xpos / 3.0;
+ cc._layoutBounds.y = ypos / 3.0;
+ cc._layoutBounds.width = 1.0 / 3.0;
+ cc._layoutBounds.height = 1.0 / 3.0;
+ }
+ } else {
+ CubismLogError('not supported mask count : {0}', layoutCount);
+ }
+ }
+ }
+
+ /**
+ * カラーバッファを取得する
+ * @return カラーバッファ
+ */
+ public getColorBuffer(): WebGLTexture {
+ return this._colorBuffer;
+ }
+
+ /**
+ * 画面描画に使用するクリッピングマスクのリストを取得する
+ * @return 画面描画に使用するクリッピングマスクのリスト
+ */
+ public getClippingContextListForDraw(): csmVector {
+ return this._clippingContextListForDraw;
+ }
+
+ /**
+ * クリッピングマスクバッファのサイズを設定する
+ * @param size クリッピングマスクバッファのサイズ
+ */
+ public setClippingMaskBufferSize(size: number): void {
+ this._clippingMaskBufferSize = size;
+ }
+
+ /**
+ * クリッピングマスクバッファのサイズを取得する
+ * @return クリッピングマスクバッファのサイズ
+ */
+ public getClippingMaskBufferSize(): number {
+ return this._clippingMaskBufferSize;
+ }
+
+ public _maskRenderTexture: WebGLFramebuffer; // マスク用レンダーテクスチャのアドレス
+ public _colorBuffer: WebGLTexture; // マスク用カラーバッファーのアドレス
+ public _currentFrameNo: number; // マスクテクスチャに与えるフレーム番号
+
+ public _channelColors: csmVector;
+ public _maskTexture: CubismRenderTextureResource; // マスク用のテクスチャリソースのリスト
+ public _clippingContextListForMask: csmVector; // マスク用クリッピングコンテキストのリスト
+ public _clippingContextListForDraw: csmVector; // 描画用クリッピングコンテキストのリスト
+ public _clippingMaskBufferSize: number; // クリッピングマスクのバッファサイズ(初期値:256)
+
+ private _tmpMatrix: CubismMatrix44; // マスク計算用の行列
+ private _tmpMatrixForMask: CubismMatrix44; // マスク計算用の行列
+ private _tmpMatrixForDraw: CubismMatrix44; // マスク計算用の行列
+ private _tmpBoundsOnModel: csmRect; // マスク配置計算用の矩形
+
+ gl: WebGLRenderingContext; // WebGLレンダリングコンテキスト
+}
+
+/**
+ * レンダーテクスチャのリソースを定義する構造体
+ * クリッピングマスクで使用する
+ */
+export class CubismRenderTextureResource {
+ /**
+ * 引数付きコンストラクタ
+ * @param frameNo レンダラーのフレーム番号
+ * @param texture テクスチャのアドレス
+ */
+ public constructor(frameNo: number, texture: WebGLFramebuffer) {
+ this.frameNo = frameNo;
+ this.texture = texture;
+ }
+
+ public frameNo: number; // レンダラのフレーム番号
+ public texture: WebGLFramebuffer; // テクスチャのアドレス
+}
+
+/**
+ * クリッピングマスクのコンテキスト
+ */
+export class CubismClippingContext {
+ /**
+ * 引数付きコンストラクタ
+ */
+ public constructor(
+ manager: CubismClippingManager_WebGL,
+ clippingDrawableIndices: Int32Array,
+ clipCount: number
+ ) {
+ this._owner = manager;
+
+ // クリップしている(=マスク用の)Drawableのインデックスリスト
+ this._clippingIdList = clippingDrawableIndices;
+
+ // マスクの数
+ this._clippingIdCount = clipCount;
+
+ this._allClippedDrawRect = new csmRect();
+ this._layoutBounds = new csmRect();
+
+ this._clippedDrawableIndexList = [];
+
+ this._matrixForMask = new CubismMatrix44();
+ this._matrixForDraw = new CubismMatrix44();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ if (this._layoutBounds != null) {
+ this._layoutBounds = null;
+ }
+
+ if (this._allClippedDrawRect != null) {
+ this._allClippedDrawRect = null;
+ }
+
+ if (this._clippedDrawableIndexList != null) {
+ this._clippedDrawableIndexList = null;
+ }
+ }
+
+ /**
+ * このマスクにクリップされる描画オブジェクトを追加する
+ *
+ * @param drawableIndex クリッピング対象に追加する描画オブジェクトのインデックス
+ */
+ public addClippedDrawable(drawableIndex: number) {
+ this._clippedDrawableIndexList.push(drawableIndex);
+ }
+
+ /**
+ * このマスクを管理するマネージャのインスタンスを取得する
+ * @return クリッピングマネージャのインスタンス
+ */
+ public getClippingManager(): CubismClippingManager_WebGL {
+ return this._owner;
+ }
+
+ public setGl(gl: WebGLRenderingContext): void {
+ this._owner.setGL(gl);
+ }
+
+ public _isUsing: boolean; // 現在の描画状態でマスクの準備が必要ならtrue
+ public readonly _clippingIdList: Int32Array; // クリッピングマスクのIDリスト
+ public _clippingIdCount: number; // クリッピングマスクの数
+ public _layoutChannelNo: number; // RGBAのいずれのチャンネルにこのクリップを配置するか(0:R, 1:G, 2:B, 3:A)
+ public _layoutBounds: csmRect; // マスク用チャンネルのどの領域にマスクを入れるか(View座標-1~1, UVは0~1に直す)
+ public _allClippedDrawRect: csmRect; // このクリッピングで、クリッピングされるすべての描画オブジェクトの囲み矩形(毎回更新)
+ public _matrixForMask: CubismMatrix44; // マスクの位置計算結果を保持する行列
+ public _matrixForDraw: CubismMatrix44; // 描画オブジェクトの位置計算結果を保持する行列
+ public _clippedDrawableIndexList: number[]; // このマスクにクリップされる描画オブジェクトのリスト
+
+ private _owner: CubismClippingManager_WebGL; // このマスクを管理しているマネージャのインスタンス
+}
+
+/**
+ * WebGL用のシェーダープログラムを生成・破棄するクラス
+ * シングルトンなクラスであり、CubismShader_WebGL.getInstanceからアクセスする。
+ */
+export class CubismShader_WebGL {
+ /**
+ * インスタンスを取得する(シングルトン)
+ * @return インスタンス
+ */
+ public static getInstance(): CubismShader_WebGL {
+ if (s_instance == null) {
+ s_instance = new CubismShader_WebGL();
+
+ return s_instance;
+ }
+ return s_instance;
+ }
+
+ /**
+ * インスタンスを開放する(シングルトン)
+ */
+ public static deleteInstance(): void {
+ if (s_instance) {
+ s_instance.release();
+ s_instance = null;
+ }
+ }
+
+ /**
+ * privateなコンストラクタ
+ */
+ private constructor() {
+ this._shaderSets = new csmVector();
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this.releaseShaderProgram();
+ }
+
+ /**
+ * シェーダープログラムの一連のセットアップを実行する
+ * @param renderer レンダラのインスタンス
+ * @param textureId GPUのテクスチャID
+ * @param vertexCount ポリゴンメッシュの頂点数
+ * @param vertexArray ポリゴンメッシュの頂点配列
+ * @param indexArray インデックスバッファの頂点配列
+ * @param uvArray uv配列
+ * @param opacity 不透明度
+ * @param colorBlendMode カラーブレンディングのタイプ
+ * @param baseColor ベースカラー
+ * @param isPremultipliedAlpha 乗算済みアルファかどうか
+ * @param matrix4x4 Model-View-Projection行列
+ * @param invertedMask マスクを反転して使用するフラグ
+ */
+ public setupShaderProgram(
+ renderer: CubismRenderer_WebGL,
+ textureId: WebGLTexture,
+ vertexCount: number,
+ vertexArray: Float32Array,
+ indexArray: Uint16Array,
+ uvArray: Float32Array,
+ bufferData: {
+ vertex: WebGLBuffer;
+ uv: WebGLBuffer;
+ index: WebGLBuffer;
+ },
+ opacity: number,
+ colorBlendMode: CubismBlendMode,
+ baseColor: CubismTextureColor,
+ isPremultipliedAlpha: boolean,
+ matrix4x4: CubismMatrix44,
+ invertedMask: boolean
+ ): void {
+ if (!isPremultipliedAlpha) {
+ CubismLogError('NoPremultipliedAlpha is not allowed');
+ }
+
+ if (this._shaderSets.getSize() == 0) {
+ this.generateShaders();
+ }
+
+ // Blending
+ let SRC_COLOR: number;
+ let DST_COLOR: number;
+ let SRC_ALPHA: number;
+ let DST_ALPHA: number;
+
+ if (renderer.getClippingContextBufferForMask() != null) {
+ // マスク生成時
+ const shaderSet: CubismShaderSet = this._shaderSets.at(
+ ShaderNames.ShaderNames_SetupMask
+ );
+ this.gl.useProgram(shaderSet.shaderProgram);
+
+ // テクスチャ設定
+ this.gl.activeTexture(this.gl.TEXTURE0);
+ this.gl.bindTexture(this.gl.TEXTURE_2D, textureId);
+ this.gl.uniform1i(shaderSet.samplerTexture0Location, 0);
+
+ // 頂点配列の設定(VBO)
+ if (bufferData.vertex == null) {
+ bufferData.vertex = this.gl.createBuffer();
+ }
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex);
+ this.gl.bufferData(
+ this.gl.ARRAY_BUFFER,
+ vertexArray,
+ this.gl.DYNAMIC_DRAW
+ );
+ this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation);
+ this.gl.vertexAttribPointer(
+ shaderSet.attributePositionLocation,
+ 2,
+ this.gl.FLOAT,
+ false,
+ 0,
+ 0
+ );
+
+ // テクスチャ頂点の設定
+ if (bufferData.uv == null) {
+ bufferData.uv = this.gl.createBuffer();
+ }
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv);
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW);
+ this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation);
+ this.gl.vertexAttribPointer(
+ shaderSet.attributeTexCoordLocation,
+ 2,
+ this.gl.FLOAT,
+ false,
+ 0,
+ 0
+ );
+
+ // チャンネル
+ const channelNo: number = renderer.getClippingContextBufferForMask()
+ ._layoutChannelNo;
+ const colorChannel: CubismTextureColor = renderer
+ .getClippingContextBufferForMask()
+ .getClippingManager()
+ .getChannelFlagAsColor(channelNo);
+ this.gl.uniform4f(
+ shaderSet.uniformChannelFlagLocation,
+ colorChannel.R,
+ colorChannel.G,
+ colorChannel.B,
+ colorChannel.A
+ );
+
+ this.gl.uniformMatrix4fv(
+ shaderSet.uniformClipMatrixLocation,
+ false,
+ renderer.getClippingContextBufferForMask()._matrixForMask.getArray()
+ );
+
+ const rect: csmRect = renderer.getClippingContextBufferForMask()
+ ._layoutBounds;
+
+ this.gl.uniform4f(
+ shaderSet.uniformBaseColorLocation,
+ rect.x * 2.0 - 1.0,
+ rect.y * 2.0 - 1.0,
+ rect.getRight() * 2.0 - 1.0,
+ rect.getBottom() * 2.0 - 1.0
+ );
+
+ SRC_COLOR = this.gl.ZERO;
+ DST_COLOR = this.gl.ONE_MINUS_SRC_COLOR;
+ SRC_ALPHA = this.gl.ZERO;
+ DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA;
+ } // マスク生成以外の場合
+ else {
+ const masked: boolean =
+ renderer.getClippingContextBufferForDraw() != null; // この描画オブジェクトはマスク対象か
+ const offset: number = masked ? (invertedMask ? 2 : 1) : 0;
+
+ let shaderSet: CubismShaderSet = new CubismShaderSet();
+
+ switch (colorBlendMode) {
+ case CubismBlendMode.CubismBlendMode_Normal:
+ default:
+ shaderSet = this._shaderSets.at(
+ ShaderNames.ShaderNames_NormalPremultipliedAlpha + offset
+ );
+ SRC_COLOR = this.gl.ONE;
+ DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA;
+ SRC_ALPHA = this.gl.ONE;
+ DST_ALPHA = this.gl.ONE_MINUS_SRC_ALPHA;
+ break;
+
+ case CubismBlendMode.CubismBlendMode_Additive:
+ shaderSet = this._shaderSets.at(
+ ShaderNames.ShaderNames_AddPremultipliedAlpha + offset
+ );
+ SRC_COLOR = this.gl.ONE;
+ DST_COLOR = this.gl.ONE;
+ SRC_ALPHA = this.gl.ZERO;
+ DST_ALPHA = this.gl.ONE;
+ break;
+
+ case CubismBlendMode.CubismBlendMode_Multiplicative:
+ shaderSet = this._shaderSets.at(
+ ShaderNames.ShaderNames_MultPremultipliedAlpha + offset
+ );
+ SRC_COLOR = this.gl.DST_COLOR;
+ DST_COLOR = this.gl.ONE_MINUS_SRC_ALPHA;
+ SRC_ALPHA = this.gl.ZERO;
+ DST_ALPHA = this.gl.ONE;
+ break;
+ }
+
+ this.gl.useProgram(shaderSet.shaderProgram);
+
+ // 頂点配列の設定
+ if (bufferData.vertex == null) {
+ bufferData.vertex = this.gl.createBuffer();
+ }
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.vertex);
+ this.gl.bufferData(
+ this.gl.ARRAY_BUFFER,
+ vertexArray,
+ this.gl.DYNAMIC_DRAW
+ );
+ this.gl.enableVertexAttribArray(shaderSet.attributePositionLocation);
+ this.gl.vertexAttribPointer(
+ shaderSet.attributePositionLocation,
+ 2,
+ this.gl.FLOAT,
+ false,
+ 0,
+ 0
+ );
+
+ // テクスチャ頂点の設定
+ if (bufferData.uv == null) {
+ bufferData.uv = this.gl.createBuffer();
+ }
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, bufferData.uv);
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, uvArray, this.gl.DYNAMIC_DRAW);
+ this.gl.enableVertexAttribArray(shaderSet.attributeTexCoordLocation);
+ this.gl.vertexAttribPointer(
+ shaderSet.attributeTexCoordLocation,
+ 2,
+ this.gl.FLOAT,
+ false,
+ 0,
+ 0
+ );
+
+ if (masked) {
+ this.gl.activeTexture(this.gl.TEXTURE1);
+ const tex: WebGLTexture = renderer
+ .getClippingContextBufferForDraw()
+ .getClippingManager()
+ .getColorBuffer();
+ this.gl.bindTexture(this.gl.TEXTURE_2D, tex);
+ this.gl.uniform1i(shaderSet.samplerTexture1Location, 1);
+
+ // view座標をClippingContextの座標に変換するための行列を設定
+ this.gl.uniformMatrix4fv(
+ shaderSet.uniformClipMatrixLocation,
+ false,
+ renderer.getClippingContextBufferForDraw()._matrixForDraw.getArray()
+ );
+
+ // 使用するカラーチャンネルを設定
+ const channelNo: number = renderer.getClippingContextBufferForDraw()
+ ._layoutChannelNo;
+ const colorChannel: CubismTextureColor = renderer
+ .getClippingContextBufferForDraw()
+ .getClippingManager()
+ .getChannelFlagAsColor(channelNo);
+ this.gl.uniform4f(
+ shaderSet.uniformChannelFlagLocation,
+ colorChannel.R,
+ colorChannel.G,
+ colorChannel.B,
+ colorChannel.A
+ );
+ }
+
+ // テクスチャ設定
+ this.gl.activeTexture(this.gl.TEXTURE0);
+ this.gl.bindTexture(this.gl.TEXTURE_2D, textureId);
+ this.gl.uniform1i(shaderSet.samplerTexture0Location, 0);
+
+ // 座標変換
+ this.gl.uniformMatrix4fv(
+ shaderSet.uniformMatrixLocation,
+ false,
+ matrix4x4.getArray()
+ );
+
+ this.gl.uniform4f(
+ shaderSet.uniformBaseColorLocation,
+ baseColor.R,
+ baseColor.G,
+ baseColor.B,
+ baseColor.A
+ );
+ }
+
+ // IBOを作成し、データを転送
+ if (bufferData.index == null) {
+ bufferData.index = this.gl.createBuffer();
+ }
+ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, bufferData.index);
+ this.gl.bufferData(
+ this.gl.ELEMENT_ARRAY_BUFFER,
+ indexArray,
+ this.gl.DYNAMIC_DRAW
+ );
+ this.gl.blendFuncSeparate(SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA);
+ }
+
+ /**
+ * シェーダープログラムを解放する
+ */
+ public releaseShaderProgram(): void {
+ for (let i = 0; i < this._shaderSets.getSize(); i++) {
+ this.gl.deleteProgram(this._shaderSets.at(i).shaderProgram);
+ this._shaderSets.at(i).shaderProgram = 0;
+ this._shaderSets.set(i, void 0);
+ this._shaderSets.set(i, null);
+ }
+ }
+
+ /**
+ * シェーダープログラムを初期化する
+ * @param vertShaderSrc 頂点シェーダのソース
+ * @param fragShaderSrc フラグメントシェーダのソース
+ */
+ public generateShaders(): void {
+ for (let i = 0; i < shaderCount; i++) {
+ this._shaderSets.pushBack(new CubismShaderSet());
+ }
+
+ this._shaderSets.at(0).shaderProgram = this.loadShaderProgram(
+ vertexShaderSrcSetupMask,
+ fragmentShaderSrcsetupMask
+ );
+
+ this._shaderSets.at(1).shaderProgram = this.loadShaderProgram(
+ vertexShaderSrc,
+ fragmentShaderSrcPremultipliedAlpha
+ );
+ this._shaderSets.at(2).shaderProgram = this.loadShaderProgram(
+ vertexShaderSrcMasked,
+ fragmentShaderSrcMaskPremultipliedAlpha
+ );
+ this._shaderSets.at(3).shaderProgram = this.loadShaderProgram(
+ vertexShaderSrcMasked,
+ fragmentShaderSrcMaskInvertedPremultipliedAlpha
+ );
+
+ // 加算も通常と同じシェーダーを利用する
+ this._shaderSets.at(4).shaderProgram = this._shaderSets.at(1).shaderProgram;
+ this._shaderSets.at(5).shaderProgram = this._shaderSets.at(2).shaderProgram;
+ this._shaderSets.at(6).shaderProgram = this._shaderSets.at(3).shaderProgram;
+
+ // 乗算も通常と同じシェーダーを利用する
+ this._shaderSets.at(7).shaderProgram = this._shaderSets.at(1).shaderProgram;
+ this._shaderSets.at(8).shaderProgram = this._shaderSets.at(2).shaderProgram;
+ this._shaderSets.at(9).shaderProgram = this._shaderSets.at(3).shaderProgram;
+
+ // SetupMask
+ this._shaderSets.at(
+ 0
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 0
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(0).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(
+ 0
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 0
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 0
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(0).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 通常(PremultipliedAlpha)
+ this._shaderSets.at(
+ 1
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(1).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 1
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(1).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(1).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(1).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(1).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(1).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 1
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(1).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 通常(クリッピング、PremultipliedAlpha)
+ this._shaderSets.at(
+ 2
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 2
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(2).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(2).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(2).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 2
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 2
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 2
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(2).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 通常(クリッピング・反転, PremultipliedAlpha)
+ this._shaderSets.at(
+ 3
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 3
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(3).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(3).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(3).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 3
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 3
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 3
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(3).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 加算(PremultipliedAlpha)
+ this._shaderSets.at(
+ 4
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(4).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 4
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(4).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(4).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(4).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(4).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(4).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 4
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(4).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 加算(クリッピング、PremultipliedAlpha)
+ this._shaderSets.at(
+ 5
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 5
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(5).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(5).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(5).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 5
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 5
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 5
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(5).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 加算(クリッピング・反転、PremultipliedAlpha)
+ this._shaderSets.at(
+ 6
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 6
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(6).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(6).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(6).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 6
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 6
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 6
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(6).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 乗算(PremultipliedAlpha)
+ this._shaderSets.at(
+ 7
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(7).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 7
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(7).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(7).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(7).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(7).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(7).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 7
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(7).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 乗算(クリッピング、PremultipliedAlpha)
+ this._shaderSets.at(
+ 8
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 8
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(8).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(8).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(8).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 8
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 8
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 8
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(8).shaderProgram,
+ 'u_baseColor'
+ );
+
+ // 乗算(クリッピング・反転、PremultipliedAlpha)
+ this._shaderSets.at(
+ 9
+ ).attributePositionLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'a_position'
+ );
+ this._shaderSets.at(
+ 9
+ ).attributeTexCoordLocation = this.gl.getAttribLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'a_texCoord'
+ );
+ this._shaderSets.at(9).samplerTexture0Location = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 's_texture0'
+ );
+ this._shaderSets.at(9).samplerTexture1Location = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 's_texture1'
+ );
+ this._shaderSets.at(9).uniformMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'u_matrix'
+ );
+ this._shaderSets.at(
+ 9
+ ).uniformClipMatrixLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'u_clipMatrix'
+ );
+ this._shaderSets.at(
+ 9
+ ).uniformChannelFlagLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'u_channelFlag'
+ );
+ this._shaderSets.at(
+ 9
+ ).uniformBaseColorLocation = this.gl.getUniformLocation(
+ this._shaderSets.at(9).shaderProgram,
+ 'u_baseColor'
+ );
+ }
+
+ /**
+ * シェーダプログラムをロードしてアドレスを返す
+ * @param vertexShaderSource 頂点シェーダのソース
+ * @param fragmentShaderSource フラグメントシェーダのソース
+ * @return シェーダプログラムのアドレス
+ */
+ public loadShaderProgram(
+ vertexShaderSource: string,
+ fragmentShaderSource: string
+ ): WebGLProgram {
+ // Create Shader Program
+ let shaderProgram: WebGLProgram = this.gl.createProgram();
+
+ let vertShader = this.compileShaderSource(
+ this.gl.VERTEX_SHADER,
+ vertexShaderSource
+ );
+
+ if (!vertShader) {
+ CubismLogError('Vertex shader compile error!');
+ return 0;
+ }
+
+ let fragShader = this.compileShaderSource(
+ this.gl.FRAGMENT_SHADER,
+ fragmentShaderSource
+ );
+ if (!fragShader) {
+ CubismLogError('Vertex shader compile error!');
+ return 0;
+ }
+
+ // Attach vertex shader to program
+ this.gl.attachShader(shaderProgram, vertShader);
+
+ // Attach fragment shader to program
+ this.gl.attachShader(shaderProgram, fragShader);
+
+ // link program
+ this.gl.linkProgram(shaderProgram);
+ const linkStatus = this.gl.getProgramParameter(
+ shaderProgram,
+ this.gl.LINK_STATUS
+ );
+
+ // リンクに失敗したらシェーダーを削除
+ if (!linkStatus) {
+ CubismLogError('Failed to link program: {0}', shaderProgram);
+
+ this.gl.deleteShader(vertShader);
+ vertShader = 0;
+
+ this.gl.deleteShader(fragShader);
+ fragShader = 0;
+
+ if (shaderProgram) {
+ this.gl.deleteProgram(shaderProgram);
+ shaderProgram = 0;
+ }
+
+ return 0;
+ }
+
+ // Release vertex and fragment shaders.
+ this.gl.deleteShader(vertShader);
+ this.gl.deleteShader(fragShader);
+
+ return shaderProgram;
+ }
+
+ /**
+ * シェーダープログラムをコンパイルする
+ * @param shaderType シェーダタイプ(Vertex/Fragment)
+ * @param shaderSource シェーダソースコード
+ *
+ * @return コンパイルされたシェーダープログラム
+ */
+ public compileShaderSource(
+ shaderType: GLenum,
+ shaderSource: string
+ ): WebGLProgram {
+ const source: string = shaderSource;
+
+ const shader: WebGLProgram = this.gl.createShader(shaderType);
+ this.gl.shaderSource(shader, source);
+ this.gl.compileShader(shader);
+
+ if (!shader) {
+ const log: string = this.gl.getShaderInfoLog(shader);
+ CubismLogError('Shader compile log: {0} ', log);
+ }
+
+ const status: any = this.gl.getShaderParameter(
+ shader,
+ this.gl.COMPILE_STATUS
+ );
+ if (!status) {
+ this.gl.deleteShader(shader);
+ return null;
+ }
+
+ return shader;
+ }
+
+ public setGl(gl: WebGLRenderingContext): void {
+ this.gl = gl;
+ }
+
+ _shaderSets: csmVector; // ロードしたシェーダープログラムを保持する変数
+ gl: WebGLRenderingContext; // webglコンテキスト
+}
+
+/**
+ * CubismShader_WebGLのインナークラス
+ */
+export class CubismShaderSet {
+ shaderProgram: WebGLProgram; // シェーダープログラムのアドレス
+ attributePositionLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(Position)
+ attributeTexCoordLocation: GLuint; // シェーダープログラムに渡す変数のアドレス(TexCoord)
+ uniformMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Matrix)
+ uniformClipMatrixLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ClipMatrix)
+ samplerTexture0Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture0)
+ samplerTexture1Location: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(Texture1)
+ uniformBaseColorLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(BaseColor)
+ uniformChannelFlagLocation: WebGLUniformLocation; // シェーダープログラムに渡す変数のアドレス(ChannelFlag)
+}
+
+export enum ShaderNames {
+ // SetupMask
+ ShaderNames_SetupMask,
+
+ // Normal
+ ShaderNames_NormalPremultipliedAlpha,
+ ShaderNames_NormalMaskedPremultipliedAlpha,
+ ShaderNames_NomralMaskedInvertedPremultipliedAlpha,
+
+ // Add
+ ShaderNames_AddPremultipliedAlpha,
+ ShaderNames_AddMaskedPremultipliedAlpha,
+ ShaderNames_AddMaskedPremultipliedAlphaInverted,
+
+ // Mult
+ ShaderNames_MultPremultipliedAlpha,
+ ShaderNames_MultMaskedPremultipliedAlpha,
+ ShaderNames_MultMaskedPremultipliedAlphaInverted
+}
+
+export const vertexShaderSrcSetupMask =
+ 'attribute vec4 a_position;' +
+ 'attribute vec2 a_texCoord;' +
+ 'varying vec2 v_texCoord;' +
+ 'varying vec4 v_myPos;' +
+ 'uniform mat4 u_clipMatrix;' +
+ 'void main()' +
+ '{' +
+ ' gl_Position = u_clipMatrix * a_position;' +
+ ' v_myPos = u_clipMatrix * a_position;' +
+ ' v_texCoord = a_texCoord;' +
+ ' v_texCoord.y = 1.0 - v_texCoord.y;' +
+ '}';
+export const fragmentShaderSrcsetupMask =
+ 'precision mediump float;' +
+ 'varying vec2 v_texCoord;' +
+ 'varying vec4 v_myPos;' +
+ 'uniform vec4 u_baseColor;' +
+ 'uniform vec4 u_channelFlag;' +
+ 'uniform sampler2D s_texture0;' +
+ 'void main()' +
+ '{' +
+ ' float isInside = ' +
+ ' step(u_baseColor.x, v_myPos.x/v_myPos.w)' +
+ ' * step(u_baseColor.y, v_myPos.y/v_myPos.w)' +
+ ' * step(v_myPos.x/v_myPos.w, u_baseColor.z)' +
+ ' * step(v_myPos.y/v_myPos.w, u_baseColor.w);' +
+ ' gl_FragColor = u_channelFlag * texture2D(s_texture0, v_texCoord).a * isInside;' +
+ '}';
+
+//----- バーテックスシェーダプログラム -----
+// Normal & Add & Mult 共通
+export const vertexShaderSrc =
+ 'attribute vec4 a_position;' + //v.vertex
+ 'attribute vec2 a_texCoord;' + //v.texcoord
+ 'varying vec2 v_texCoord;' + //v2f.texcoord
+ 'uniform mat4 u_matrix;' +
+ 'void main()' +
+ '{' +
+ ' gl_Position = u_matrix * a_position;' +
+ ' v_texCoord = a_texCoord;' +
+ ' v_texCoord.y = 1.0 - v_texCoord.y;' +
+ '}';
+
+// Normal & Add & Mult 共通(クリッピングされたものの描画用)
+export const vertexShaderSrcMasked =
+ 'attribute vec4 a_position;' +
+ 'attribute vec2 a_texCoord;' +
+ 'varying vec2 v_texCoord;' +
+ 'varying vec4 v_clipPos;' +
+ 'uniform mat4 u_matrix;' +
+ 'uniform mat4 u_clipMatrix;' +
+ 'void main()' +
+ '{' +
+ ' gl_Position = u_matrix * a_position;' +
+ ' v_clipPos = u_clipMatrix * a_position;' +
+ ' v_texCoord = a_texCoord;' +
+ ' v_texCoord.y = 1.0 - v_texCoord.y;' +
+ '}';
+
+//----- フラグメントシェーダプログラム -----
+// Normal & Add & Mult 共通 (PremultipliedAlpha)
+export const fragmentShaderSrcPremultipliedAlpha =
+ 'precision mediump float;' +
+ 'varying vec2 v_texCoord;' + //v2f.texcoord
+ 'uniform vec4 u_baseColor;' +
+ 'uniform sampler2D s_texture0;' + //_MainTex
+ 'void main()' +
+ '{' +
+ ' gl_FragColor = texture2D(s_texture0 , v_texCoord) * u_baseColor;' +
+ '}';
+
+// Normal (クリッピングされたものの描画用、PremultipliedAlpha兼用)
+export const fragmentShaderSrcMaskPremultipliedAlpha =
+ 'precision mediump float;' +
+ 'varying vec2 v_texCoord;' +
+ 'varying vec4 v_clipPos;' +
+ 'uniform vec4 u_baseColor;' +
+ 'uniform vec4 u_channelFlag;' +
+ 'uniform sampler2D s_texture0;' +
+ 'uniform sampler2D s_texture1;' +
+ 'void main()' +
+ '{' +
+ ' vec4 col_formask = texture2D(s_texture0 , v_texCoord) * u_baseColor;' +
+ ' vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' +
+ ' float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' +
+ ' col_formask = col_formask * maskVal;' +
+ ' gl_FragColor = col_formask;' +
+ '}';
+
+// Normal & Add & Mult 共通(クリッピングされて反転使用の描画用、PremultipliedAlphaの場合)
+export const fragmentShaderSrcMaskInvertedPremultipliedAlpha =
+ 'precision mediump float;' +
+ 'varying vec2 v_texCoord;' +
+ 'varying vec4 v_clipPos;' +
+ 'uniform sampler2D s_texture0;' +
+ 'uniform sampler2D s_texture1;' +
+ 'uniform vec4 u_channelFlag;' +
+ 'uniform vec4 u_baseColor;' +
+ 'void main()' +
+ '{' +
+ 'vec4 col_formask = texture2D(s_texture0, v_texCoord) * u_baseColor;' +
+ 'vec4 clipMask = (1.0 - texture2D(s_texture1, v_clipPos.xy / v_clipPos.w)) * u_channelFlag;' +
+ 'float maskVal = clipMask.r + clipMask.g + clipMask.b + clipMask.a;' +
+ 'col_formask = col_formask * (1.0 - maskVal);' +
+ 'gl_FragColor = col_formask;' +
+ '}';
+
+/**
+ * WebGL用の描画命令を実装したクラス
+ */
+export class CubismRenderer_WebGL extends CubismRenderer {
+ /**
+ * レンダラの初期化処理を実行する
+ * 引数に渡したモデルからレンダラの初期化処理に必要な情報を取り出すことができる
+ *
+ * @param model モデルのインスタンス
+ */
+ public initialize(model: CubismModel): void {
+ if (model.isUsingMasking()) {
+ this._clippingManager = new CubismClippingManager_WebGL(); // クリッピングマスク・バッファ前処理方式を初期化
+ this._clippingManager.initialize(
+ model,
+ model.getDrawableCount(),
+ model.getDrawableMasks(),
+ model.getDrawableMaskCounts()
+ );
+ }
+
+ this._sortedDrawableIndexList.resize(model.getDrawableCount(), 0);
+
+ super.initialize(model); // 親クラスの処理を呼ぶ
+ }
+
+ /**
+ * WebGLテクスチャのバインド処理
+ * CubismRendererにテクスチャを設定し、CubismRenderer内でその画像を参照するためのIndex値を戻り値とする
+ * @param modelTextureNo セットするモデルテクスチャの番号
+ * @param glTextureNo WebGLテクスチャの番号
+ */
+ public bindTexture(modelTextureNo: number, glTexture: WebGLTexture): void {
+ this._textures.setValue(modelTextureNo, glTexture);
+ }
+
+ /**
+ * WebGLにバインドされたテクスチャのリストを取得する
+ * @return テクスチャのリスト
+ */
+ public getBindedTextures(): csmMap {
+ return this._textures;
+ }
+
+ /**
+ * クリッピングマスクバッファのサイズを設定する
+ * マスク用のFrameBufferを破棄、再作成する為処理コストは高い
+ * @param size クリッピングマスクバッファのサイズ
+ */
+ public setClippingMaskBufferSize(size: number) {
+ // FrameBufferのサイズを変更するためにインスタンスを破棄・再作成する
+ this._clippingManager.release();
+ this._clippingManager = void 0;
+ this._clippingManager = null;
+
+ this._clippingManager = new CubismClippingManager_WebGL();
+
+ this._clippingManager.setClippingMaskBufferSize(size);
+
+ this._clippingManager.initialize(
+ this.getModel(),
+ this.getModel().getDrawableCount(),
+ this.getModel().getDrawableMasks(),
+ this.getModel().getDrawableMaskCounts()
+ );
+ }
+
+ /**
+ * クリッピングマスクバッファのサイズを取得する
+ * @return クリッピングマスクバッファのサイズ
+ */
+ public getClippingMaskBufferSize(): number {
+ return this._clippingManager.getClippingMaskBufferSize();
+ }
+
+ /**
+ * コンストラクタ
+ */
+ public constructor() {
+ super();
+ this._clippingContextBufferForMask = null;
+ this._clippingContextBufferForDraw = null;
+ this._clippingManager = new CubismClippingManager_WebGL();
+ this.firstDraw = true;
+ this._textures = new csmMap();
+ this._sortedDrawableIndexList = new csmVector();
+ this._bufferData = {
+ vertex: WebGLBuffer = null,
+ uv: WebGLBuffer = null,
+ index: WebGLBuffer = null
+ };
+
+ // テクスチャ対応マップの容量を確保しておく
+ this._textures.prepareCapacity(32, true);
+ }
+
+ /**
+ * デストラクタ相当の処理
+ */
+ public release(): void {
+ this._clippingManager.release();
+ this._clippingManager = void 0;
+ this._clippingManager = null;
+
+ this.gl.deleteBuffer(this._bufferData.vertex);
+ this._bufferData.vertex = null;
+ this.gl.deleteBuffer(this._bufferData.uv);
+ this._bufferData.uv = null;
+ this.gl.deleteBuffer(this._bufferData.index);
+ this._bufferData.index = null;
+ this._bufferData = null;
+
+ this._textures = null;
+ }
+
+ /**
+ * モデルを描画する実際の処理
+ */
+ public doDrawModel(): void {
+ //------------ クリッピングマスク・バッファ前処理方式の場合 ------------
+ if (this._clippingManager != null) {
+ this.preDraw();
+ this._clippingManager.setupClippingContext(this.getModel(), this);
+ }
+
+ // 上記クリッピング処理内でも一度PreDrawを呼ぶので注意!!
+ this.preDraw();
+
+ const drawableCount: number = this.getModel().getDrawableCount();
+ const renderOrder: Int32Array = this.getModel().getDrawableRenderOrders();
+
+ // インデックスを描画順でソート
+ for (let i = 0; i < drawableCount; ++i) {
+ const order: number = renderOrder[i];
+ this._sortedDrawableIndexList.set(order, i);
+ }
+
+ // 描画
+ for (let i = 0; i < drawableCount; ++i) {
+ const drawableIndex: number = this._sortedDrawableIndexList.at(i);
+
+ // Drawableが表示状態でなければ処理をパスする
+ if (!this.getModel().getDrawableDynamicFlagIsVisible(drawableIndex)) {
+ continue;
+ }
+
+ // クリッピングマスクをセットする
+ this.setClippingContextBufferForDraw(
+ this._clippingManager != null
+ ? this._clippingManager
+ .getClippingContextListForDraw()
+ .at(drawableIndex)
+ : null
+ );
+
+ this.setIsCulling(this.getModel().getDrawableCulling(drawableIndex));
+
+ this.drawMesh(
+ this.getModel().getDrawableTextureIndices(drawableIndex),
+ this.getModel().getDrawableVertexIndexCount(drawableIndex),
+ this.getModel().getDrawableVertexCount(drawableIndex),
+ this.getModel().getDrawableVertexIndices(drawableIndex),
+ this.getModel().getDrawableVertices(drawableIndex),
+ this.getModel().getDrawableVertexUvs(drawableIndex),
+ this.getModel().getDrawableOpacity(drawableIndex),
+ this.getModel().getDrawableBlendMode(drawableIndex),
+ this.getModel().getDrawableInvertedMaskBit(drawableIndex)
+ );
+ }
+ }
+
+ /**
+ * [オーバーライド]
+ * 描画オブジェクト(アートメッシュ)を描画する。
+ * ポリゴンメッシュとテクスチャ番号をセットで渡す。
+ * @param textureNo 描画するテクスチャ番号
+ * @param indexCount 描画オブジェクトのインデックス値
+ * @param vertexCount ポリゴンメッシュの頂点数
+ * @param indexArray ポリゴンメッシュのインデックス配列
+ * @param vertexArray ポリゴンメッシュの頂点配列
+ * @param uvArray uv配列
+ * @param opacity 不透明度
+ * @param colorBlendMode カラー合成タイプ
+ * @param invertedMask マスク使用時のマスクの反転使用
+ */
+ public drawMesh(
+ textureNo: number,
+ indexCount: number,
+ vertexCount: number,
+ indexArray: Uint16Array,
+ vertexArray: Float32Array,
+ uvArray: Float32Array,
+ opacity: number,
+ colorBlendMode: CubismBlendMode,
+ invertedMask: boolean
+ ): void {
+ // 裏面描画の有効・無効
+ if (this.isCulling()) {
+ this.gl.enable(this.gl.CULL_FACE);
+ } else {
+ this.gl.disable(this.gl.CULL_FACE);
+ }
+
+ this.gl.frontFace(this.gl.CCW); // Cubism SDK OpenGLはマスク・アートメッシュ共にCCWが表面
+
+ const modelColorRGBA: CubismTextureColor = this.getModelColor();
+
+ if (this.getClippingContextBufferForMask() == null) {
+ // マスク生成時以外
+ modelColorRGBA.A *= opacity;
+ if (this.isPremultipliedAlpha()) {
+ modelColorRGBA.R *= modelColorRGBA.A;
+ modelColorRGBA.G *= modelColorRGBA.A;
+ modelColorRGBA.B *= modelColorRGBA.A;
+ }
+ }
+
+ let drawtexture: WebGLTexture; // シェーダに渡すテクスチャ
+
+ // テクスチャマップからバインド済みテクスチャIDを取得
+ // バインドされていなければダミーのテクスチャIDをセットする
+ if (this._textures.getValue(textureNo) != null) {
+ drawtexture = this._textures.getValue(textureNo);
+ } else {
+ drawtexture = null;
+ }
+
+ CubismShader_WebGL.getInstance().setupShaderProgram(
+ this,
+ drawtexture,
+ vertexCount,
+ vertexArray,
+ indexArray,
+ uvArray,
+ this._bufferData,
+ opacity,
+ colorBlendMode,
+ modelColorRGBA,
+ this.isPremultipliedAlpha(),
+ this.getMvpMatrix(),
+ invertedMask
+ );
+
+ // ポリゴンメッシュを描画する
+ this.gl.drawElements(
+ this.gl.TRIANGLES,
+ indexCount,
+ this.gl.UNSIGNED_SHORT,
+ 0
+ );
+
+ // 後処理
+ this.gl.useProgram(null);
+ this.setClippingContextBufferForDraw(null);
+ this.setClippingContextBufferForMask(null);
+ }
+
+ /**
+ * レンダラが保持する静的なリソースを解放する
+ * WebGLの静的なシェーダープログラムを解放する
+ */
+ public static doStaticRelease(): void {
+ CubismShader_WebGL.deleteInstance();
+ }
+
+ /**
+ * レンダーステートを設定する
+ * @param fbo アプリケーション側で指定しているフレームバッファ
+ * @param viewport ビューポート
+ */
+ public setRenderState(fbo: WebGLFramebuffer, viewport: number[]): void {
+ s_fbo = fbo;
+ s_viewport = viewport;
+ }
+
+ /**
+ * 描画開始時の追加処理
+ * モデルを描画する前にクリッピングマスクに必要な処理を実装している
+ */
+ public preDraw(): void {
+ if (this.firstDraw) {
+ this.firstDraw = false;
+
+ // 拡張機能を有効にする
+ this._anisortopy =
+ this.gl.getExtension('EXT_texture_filter_anisotropic') ||
+ this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') ||
+ this.gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
+ }
+
+ this.gl.disable(this.gl.SCISSOR_TEST);
+ this.gl.disable(this.gl.STENCIL_TEST);
+ this.gl.disable(this.gl.DEPTH_TEST);
+
+ // カリング(1.0beta3)
+ this.gl.frontFace(this.gl.CW);
+
+ this.gl.enable(this.gl.BLEND);
+ this.gl.colorMask(true, true, true, true);
+
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); // 前にバッファがバインドされていたら破棄する必要がある
+ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
+ }
+
+ /**
+ * マスクテクスチャに描画するクリッピングコンテキストをセットする
+ */
+ public setClippingContextBufferForMask(clip: CubismClippingContext) {
+ this._clippingContextBufferForMask = clip;
+ }
+
+ /**
+ * マスクテクスチャに描画するクリッピングコンテキストを取得する
+ * @return マスクテクスチャに描画するクリッピングコンテキスト
+ */
+ public getClippingContextBufferForMask(): CubismClippingContext {
+ return this._clippingContextBufferForMask;
+ }
+
+ /**
+ * 画面上に描画するクリッピングコンテキストをセットする
+ */
+ public setClippingContextBufferForDraw(clip: CubismClippingContext): void {
+ this._clippingContextBufferForDraw = clip;
+ }
+
+ /**
+ * 画面上に描画するクリッピングコンテキストを取得する
+ * @return 画面上に描画するクリッピングコンテキスト
+ */
+ public getClippingContextBufferForDraw(): CubismClippingContext {
+ return this._clippingContextBufferForDraw;
+ }
+
+ /**
+ * glの設定
+ */
+ public startUp(gl: WebGLRenderingContext): void {
+ this.gl = gl;
+ this._clippingManager.setGL(gl);
+ CubismShader_WebGL.getInstance().setGl(gl);
+ }
+
+ _textures: csmMap; // モデルが参照するテクスチャとレンダラでバインドしているテクスチャとのマップ
+ _sortedDrawableIndexList: csmVector; // 描画オブジェクトのインデックスを描画順に並べたリスト
+ _clippingManager: CubismClippingManager_WebGL; // クリッピングマスク管理オブジェクト
+ _clippingContextBufferForMask: CubismClippingContext; // マスクテクスチャに描画するためのクリッピングコンテキスト
+ _clippingContextBufferForDraw: CubismClippingContext; // 画面上描画するためのクリッピングコンテキスト
+ firstDraw: boolean;
+ _bufferData: {
+ vertex: WebGLBuffer;
+ uv: WebGLBuffer;
+ index: WebGLBuffer;
+ }; // 頂点バッファデータ
+ gl: WebGLRenderingContext; // webglコンテキスト
+}
+
+/**
+ * レンダラが保持する静的なリソースを開放する
+ */
+CubismRenderer.staticRelease = (): void => {
+ CubismRenderer_WebGL.doStaticRelease();
+};
+
+// Namespace definition for compatibility.
+import * as $ from './cubismrenderer_webgl';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismClippingContext = $.CubismClippingContext;
+ export type CubismClippingContext = $.CubismClippingContext;
+ export const CubismClippingManager_WebGL = $.CubismClippingManager_WebGL;
+ export type CubismClippingManager_WebGL = $.CubismClippingManager_WebGL;
+ export const CubismRenderTextureResource = $.CubismRenderTextureResource;
+ export type CubismRenderTextureResource = $.CubismRenderTextureResource;
+ export const CubismRenderer_WebGL = $.CubismRenderer_WebGL;
+ export type CubismRenderer_WebGL = $.CubismRenderer_WebGL;
+ export const CubismShaderSet = $.CubismShaderSet;
+ export type CubismShaderSet = $.CubismShaderSet;
+ export const CubismShader_WebGL = $.CubismShader_WebGL;
+ export type CubismShader_WebGL = $.CubismShader_WebGL;
+ export const ShaderNames = $.ShaderNames;
+ export type ShaderNames = $.ShaderNames;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmmap.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmmap.ts
new file mode 100644
index 000000000..ba7d46721
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmmap.ts
@@ -0,0 +1,315 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { CubismLogDebug } from '../utils/cubismdebug';
+
+/**
+ * Key-Valueのペアを定義するクラス
+ * csmMapクラスの内部データで使用する。
+ */
+export class csmPair<_KeyT, _ValT> {
+ /**
+ * コンストラクタ
+ * @param key Keyとしてセットする値
+ * @param value Valueとしてセットする値
+ */
+ public constructor(key?: _KeyT, value?: _ValT) {
+ this.first = key == undefined ? null : key;
+
+ this.second = value == undefined ? null : value;
+ }
+
+ public first: _KeyT; // keyとして用いる変数
+ public second: _ValT; // valueとして用いる変数
+}
+
+/**
+ * マップ型
+ */
+export class csmMap<_KeyT, _ValT> {
+ /**
+ * 引数付きコンストラクタ
+ * @param size 初期化時点で確保するサイズ
+ */
+ public constructor(size?: number) {
+ if (size != undefined) {
+ if (size < 1) {
+ this._keyValues = [];
+ this._dummyValue = null;
+ this._size = 0;
+ } else {
+ this._keyValues = new Array(size);
+ this._size = size;
+ }
+ } else {
+ this._keyValues = [];
+ this._dummyValue = null;
+ this._size = 0;
+ }
+ }
+
+ /**
+ * デストラクタ
+ */
+ public release() {
+ this.clear();
+ }
+
+ /**
+ * キーを追加する
+ * @param key 新たに追加するキー
+ */
+ public appendKey(key: _KeyT): void {
+ // 新しくKey/Valueのペアを作る
+ this.prepareCapacity(this._size + 1, false); // 1つ以上入る隙間を作る
+ // 新しいkey/valueのインデックスは_size
+
+ this._keyValues[this._size] = new csmPair<_KeyT, _ValT>(key);
+ this._size += 1;
+ }
+
+ /**
+ * 添字演算子[key]のオーバーロード(get)
+ * @param key 添字から特定されるValue値
+ */
+ public getValue(key: _KeyT): _ValT {
+ let found = -1;
+
+ for (let i = 0; i < this._size; i++) {
+ if (this._keyValues[i].first == key) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found >= 0) {
+ return this._keyValues[found].second;
+ } else {
+ this.appendKey(key); // 新規キーを追加
+ return this._keyValues[this._size - 1].second;
+ }
+ }
+
+ /**
+ * 添字演算子[key]のオーバーロード(set)
+ * @param key 添字から特定されるValue値
+ * @param value 代入するValue値
+ */
+ public setValue(key: _KeyT, value: _ValT): void {
+ let found = -1;
+
+ for (let i = 0; i < this._size; i++) {
+ if (this._keyValues[i].first == key) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found >= 0) {
+ this._keyValues[found].second = value;
+ } else {
+ this.appendKey(key); // 新規キーを追加
+ this._keyValues[this._size - 1].second = value;
+ }
+ }
+
+ /**
+ * 引数で渡したKeyを持つ要素が存在するか
+ * @param key 存在を確認するkey
+ * @return true 引数で渡したkeyを持つ要素が存在する
+ * @return false 引数で渡したkeyを持つ要素が存在しない
+ */
+ public isExist(key: _KeyT): boolean {
+ for (let i = 0; i < this._size; i++) {
+ if (this._keyValues[i].first == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * keyValueのポインタを全て解放する
+ */
+ public clear(): void {
+ this._keyValues = void 0;
+ this._keyValues = null;
+ this._keyValues = [];
+
+ this._size = 0;
+ }
+
+ /**
+ * コンテナのサイズを取得する
+ *
+ * @return コンテナのサイズ
+ */
+ public getSize(): number {
+ return this._size;
+ }
+
+ /**
+ * コンテナのキャパシティを確保する
+ * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない。
+ * @param fitToSize trueなら指定したサイズに合わせる。falseならサイズを2倍確保しておく。
+ */
+ public prepareCapacity(newSize: number, fitToSize: boolean): void {
+ if (newSize > this._keyValues.length) {
+ if (this._keyValues.length == 0) {
+ if (!fitToSize && newSize < csmMap.DefaultSize)
+ newSize = csmMap.DefaultSize;
+ this._keyValues.length = newSize;
+ } else {
+ if (!fitToSize && newSize < this._keyValues.length * 2)
+ newSize = this._keyValues.length * 2;
+ this._keyValues.length = newSize;
+ }
+ }
+ }
+
+ /**
+ * コンテナの先頭要素を返す
+ */
+ public begin(): iterator<_KeyT, _ValT> {
+ const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>(this, 0);
+ return ite;
+ }
+
+ /**
+ * コンテナの終端要素を返す
+ */
+ public end(): iterator<_KeyT, _ValT> {
+ const ite: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>(
+ this,
+ this._size
+ ); // 終了
+ return ite;
+ }
+
+ /**
+ * コンテナから要素を削除する
+ *
+ * @param ite 削除する要素
+ */
+ public erase(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> {
+ const index: number = ite._index;
+ if (index < 0 || this._size <= index) {
+ return ite; // 削除範囲外
+ }
+
+ // 削除
+ this._keyValues.splice(index, 1);
+ --this._size;
+
+ const ite2: iterator<_KeyT, _ValT> = new iterator<_KeyT, _ValT>(
+ this,
+ index
+ ); // 終了
+ return ite2;
+ }
+
+ /**
+ * コンテナの値を32ビット符号付き整数型でダンプする
+ */
+ public dumpAsInt() {
+ for (let i = 0; i < this._size; i++) {
+ CubismLogDebug('{0} ,', this._keyValues[i]);
+ CubismLogDebug('\n');
+ }
+ }
+
+ public static readonly DefaultSize = 10; // コンテナの初期化のデフォルトサイズ
+ public _keyValues: csmPair<_KeyT, _ValT>[]; // key-valueペアの配列
+ public _dummyValue: _ValT; // 空の値を返す為のダミー
+ public _size: number; // コンテナの要素数
+}
+
+/**
+ * csmMapのイテレータ
+ */
+export class iterator<_KeyT, _ValT> {
+ /**
+ * コンストラクタ
+ */
+ constructor(v?: csmMap<_KeyT, _ValT>, idx?: number) {
+ this._map = v != undefined ? v : new csmMap<_KeyT, _ValT>();
+
+ this._index = idx != undefined ? idx : 0;
+ }
+
+ /**
+ * =演算子のオーバーロード
+ */
+ public set(ite: iterator<_KeyT, _ValT>): iterator<_KeyT, _ValT> {
+ this._index = ite._index;
+ this._map = ite._map;
+ return this;
+ }
+
+ /**
+ * 前置き++演算子のオーバーロード
+ */
+ public preIncrement(): iterator<_KeyT, _ValT> {
+ ++this._index;
+ return this;
+ }
+
+ /**
+ * 前置き--演算子のオーバーロード
+ */
+ public preDecrement(): iterator<_KeyT, _ValT> {
+ --this._index;
+ return this;
+ }
+
+ /**
+ * 後置き++演算子のオーバーロード
+ */
+ public increment(): iterator<_KeyT, _ValT> {
+ const iteold = new iterator<_KeyT, _ValT>(this._map, this._index++); // 古い値を保存
+ return iteold;
+ }
+
+ /**
+ * 後置き--演算子のオーバーロード
+ */
+ public decrement(): iterator<_KeyT, _ValT> {
+ const iteold = new iterator<_KeyT, _ValT>(this._map, this._index); // 古い値を保存
+ this._map = iteold._map;
+ this._index = iteold._index;
+ return this;
+ }
+
+ /**
+ * *演算子のオーバーロード
+ */
+ public ptr(): csmPair<_KeyT, _ValT> {
+ return this._map._keyValues[this._index];
+ }
+
+ /**
+ * !=演算
+ */
+ public notEqual(ite: iterator<_KeyT, _ValT>): boolean {
+ return this._index != ite._index || this._map != ite._map;
+ }
+
+ _index: number; // コンテナのインデックス値
+ _map: csmMap<_KeyT, _ValT>; // コンテナ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './csmmap';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const csmMap = $.csmMap;
+ export type csmMap = $.csmMap;
+ export const csmPair = $.csmPair;
+ export type csmPair = $.csmPair;
+ export const iterator = $.iterator;
+ export type iterator = $.iterator;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmrectf.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmrectf.ts
new file mode 100644
index 000000000..074d72787
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmrectf.ts
@@ -0,0 +1,89 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * 矩形形状(座標・長さはfloat値)を定義するクラス
+ */
+export class csmRect {
+ /**
+ * コンストラクタ
+ * @param x 左端X座標
+ * @param y 上端Y座標
+ * @param w 幅
+ * @param h 高さ
+ */
+ public constructor(x?: number, y?: number, w?: number, h?: number) {
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ }
+
+ /**
+ * 矩形中央のX座標を取得する
+ */
+ public getCenterX(): number {
+ return this.x + 0.5 * this.width;
+ }
+
+ /**
+ * 矩形中央のY座標を取得する
+ */
+ public getCenterY(): number {
+ return this.y + 0.5 * this.height;
+ }
+
+ /**
+ * 右側のX座標を取得する
+ */
+ public getRight(): number {
+ return this.x + this.width;
+ }
+
+ /**
+ * 下端のY座標を取得する
+ */
+ public getBottom(): number {
+ return this.y + this.height;
+ }
+
+ /**
+ * 矩形に値をセットする
+ * @param r 矩形のインスタンス
+ */
+ public setRect(r: csmRect): void {
+ this.x = r.x;
+ this.y = r.y;
+ this.width = r.width;
+ this.height = r.height;
+ }
+
+ /**
+ * 矩形中央を軸にして縦横を拡縮する
+ * @param w 幅方向に拡縮する量
+ * @param h 高さ方向に拡縮する量
+ */
+ public expand(w: number, h: number) {
+ this.x -= w;
+ this.y -= h;
+ this.width += w * 2.0;
+ this.height += h * 2.0;
+ }
+
+ public x: number; // 左端X座標
+ public y: number; // 上端Y座標
+ public width: number; // 幅
+ public height: number; // 高さ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './csmrectf';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const csmRect = $.csmRect;
+ export type csmRect = $.csmRect;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmstring.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmstring.ts
new file mode 100644
index 000000000..df735e9f8
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmstring.ts
@@ -0,0 +1,107 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * 文字列クラス。
+ */
+export class csmString {
+ /**
+ * 文字列を後方に追加する
+ *
+ * @param c 追加する文字列
+ * @return 更新された文字列
+ */
+ public append(c: string, length?: number): csmString {
+ this.s += length !== undefined ? c.substr(0, length) : c;
+
+ return this;
+ }
+
+ /**
+ * 文字サイズを拡張して文字を埋める
+ * @param length 拡張する文字数
+ * @param v 埋める文字
+ * @return 更新された文字列
+ */
+ public expansion(length: number, v: string): csmString {
+ for (let i = 0; i < length; i++) {
+ this.append(v);
+ }
+
+ return this;
+ }
+
+ /**
+ * 文字列の長さをバイト数で取得する
+ */
+ public getBytes(): number {
+ return encodeURIComponent(this.s).replace(/%../g, 'x').length;
+ }
+
+ /**
+ * 文字列の長さを返す
+ */
+ public getLength(): number {
+ return this.s.length;
+ }
+
+ /**
+ * 文字列比較 <
+ * @param s 比較する文字列
+ * @return true: 比較する文字列より小さい
+ * @return false: 比較する文字列より大きい
+ */
+ public isLess(s: csmString): boolean {
+ return this.s < s.s;
+ }
+
+ /**
+ * 文字列比較 >
+ * @param s 比較する文字列
+ * @return true: 比較する文字列より大きい
+ * @return false: 比較する文字列より小さい
+ */
+ public isGreat(s: csmString): boolean {
+ return this.s > s.s;
+ }
+
+ /**
+ * 文字列比較 ==
+ * @param s 比較する文字列
+ * @return true: 比較する文字列と等しい
+ * @return false: 比較する文字列と異なる
+ */
+ public isEqual(s: string): boolean {
+ return this.s == s;
+ }
+
+ /**
+ * 文字列が空かどうか
+ * @return true: 空の文字列
+ * @return false: 値が設定されている
+ */
+ public isEmpty(): boolean {
+ return this.s.length == 0;
+ }
+
+ /**
+ * 引数付きコンストラクタ
+ */
+ public constructor(s: string) {
+ this.s = s;
+ }
+
+ s: string;
+}
+
+// Namespace definition for compatibility.
+import * as $ from './csmstring';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const csmString = $.csmString;
+ export type csmString = $.csmString;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmvector.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmvector.ts
new file mode 100644
index 000000000..a054f87ee
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/type/csmvector.ts
@@ -0,0 +1,352 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+/**
+ * ベクター型(可変配列型)
+ */
+export class csmVector {
+ /**
+ * 引数付きコンストラクタ
+ * @param iniitalCapacity 初期化後のキャパシティ。データサイズは_capacity * sizeof(T)
+ * @param zeroClear trueなら初期化時に確保した領域を0で埋める
+ */
+ constructor(initialCapacity = 0) {
+ if (initialCapacity < 1) {
+ this._ptr = [];
+ this._capacity = 0;
+ this._size = 0;
+ } else {
+ this._ptr = new Array(initialCapacity);
+ this._capacity = initialCapacity;
+ this._size = 0;
+ }
+ }
+
+ /**
+ * インデックスで指定した要素を返す
+ */
+ public at(index: number): T {
+ return this._ptr[index];
+ }
+
+ /**
+ * 要素をセット
+ * @param index 要素をセットするインデックス
+ * @param value セットする要素
+ */
+ public set(index: number, value: T): void {
+ this._ptr[index] = value;
+ }
+
+ /**
+ * コンテナを取得する
+ */
+ public get(offset = 0): T[] {
+ const ret: T[] = new Array();
+ for (let i = offset; i < this._size; i++) {
+ ret.push(this._ptr[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * pushBack処理、コンテナに新たな要素を追加する
+ * @param value PushBack処理で追加する値
+ */
+ public pushBack(value: T): void {
+ if (this._size >= this._capacity) {
+ this.prepareCapacity(
+ this._capacity == 0 ? csmVector.s_defaultSize : this._capacity * 2
+ );
+ }
+
+ this._ptr[this._size++] = value;
+ }
+
+ /**
+ * コンテナの全要素を解放する
+ */
+ public clear(): void {
+ this._ptr.length = 0;
+ this._size = 0;
+ }
+
+ /**
+ * コンテナの要素数を返す
+ * @return コンテナの要素数
+ */
+ public getSize(): number {
+ return this._size;
+ }
+
+ /**
+ * コンテナの全要素に対して代入処理を行う
+ * @param newSize 代入処理後のサイズ
+ * @param value 要素に代入する値
+ */
+ public assign(newSize: number, value: T): void {
+ const curSize = this._size;
+
+ if (curSize < newSize) {
+ this.prepareCapacity(newSize); // capacity更新
+ }
+
+ for (let i = 0; i < newSize; i++) {
+ this._ptr[i] = value;
+ }
+
+ this._size = newSize;
+ }
+
+ /**
+ * サイズ変更
+ */
+ public resize(newSize: number, value: T = null): void {
+ this.updateSize(newSize, value, true);
+ }
+
+ /**
+ * サイズ変更
+ */
+ public updateSize(
+ newSize: number,
+ value: any = null,
+ callPlacementNew = true
+ ): void {
+ const curSize: number = this._size;
+
+ if (curSize < newSize) {
+ this.prepareCapacity(newSize); // capacity更新
+
+ if (callPlacementNew) {
+ for (let i: number = this._size; i < newSize; i++) {
+ if (typeof value == 'function') {
+ // new
+ this._ptr[i] = JSON.parse(JSON.stringify(new value()));
+ } // プリミティブ型なので値渡し
+ else {
+ this._ptr[i] = value;
+ }
+ }
+ } else {
+ for (let i: number = this._size; i < newSize; i++) {
+ this._ptr[i] = value;
+ }
+ }
+ } else {
+ // newSize <= this._size
+ //---
+ const sub = this._size - newSize;
+ this._ptr.splice(this._size - sub, sub); // 不要なので破棄する
+ }
+ this._size = newSize;
+ }
+
+ /**
+ * コンテナにコンテナ要素を挿入する
+ * @param position 挿入する位置
+ * @param begin 挿入するコンテナの開始位置
+ * @param end 挿入するコンテナの終端位置
+ */
+ public insert(
+ position: iterator,
+ begin: iterator,
+ end: iterator
+ ): void {
+ let dstSi: number = position._index;
+ const srcSi: number = begin._index;
+ const srcEi: number = end._index;
+
+ const addCount: number = srcEi - srcSi;
+
+ this.prepareCapacity(this._size + addCount);
+
+ // 挿入用の既存データをシフトして隙間を作る
+ const addSize = this._size - dstSi;
+ if (addSize > 0) {
+ for (let i = 0; i < addSize; i++) {
+ this._ptr.splice(dstSi + i, 0, null);
+ }
+ }
+
+ for (let i: number = srcSi; i < srcEi; i++, dstSi++) {
+ this._ptr[dstSi] = begin._vector._ptr[i];
+ }
+
+ this._size = this._size + addCount;
+ }
+
+ /**
+ * コンテナからインデックスで指定した要素を削除する
+ * @param index インデックス値
+ * @return true 削除実行
+ * @return false 削除範囲外
+ */
+ public remove(index: number): boolean {
+ if (index < 0 || this._size <= index) {
+ return false; // 削除範囲外
+ }
+
+ this._ptr.splice(index, 1);
+ --this._size;
+
+ return true;
+ }
+
+ /**
+ * コンテナから要素を削除して他の要素をシフトする
+ * @param ite 削除する要素
+ */
+ public erase(ite: iterator): iterator {
+ const index: number = ite._index;
+ if (index < 0 || this._size <= index) {
+ return ite; // 削除範囲外
+ }
+
+ // 削除
+ this._ptr.splice(index, 1);
+ --this._size;
+
+ const ite2: iterator = new iterator(this, index); // 終了
+ return ite2;
+ }
+
+ /**
+ * コンテナのキャパシティを確保する
+ * @param newSize 新たなキャパシティ。引数の値が現在のサイズ未満の場合は何もしない.
+ */
+ public prepareCapacity(newSize: number): void {
+ if (newSize > this._capacity) {
+ if (this._capacity == 0) {
+ this._ptr = new Array(newSize);
+ this._capacity = newSize;
+ } else {
+ this._ptr.length = newSize;
+ this._capacity = newSize;
+ }
+ }
+ }
+
+ /**
+ * コンテナの先頭要素を返す
+ */
+ public begin(): iterator {
+ const ite: iterator =
+ this._size == 0 ? this.end() : new iterator(this, 0);
+ return ite;
+ }
+
+ /**
+ * コンテナの終端要素を返す
+ */
+ public end(): iterator {
+ const ite: iterator = new iterator(this, this._size);
+ return ite;
+ }
+
+ public getOffset(offset: number): csmVector {
+ const newVector = new csmVector();
+ newVector._ptr = this.get(offset);
+ newVector._size = this.get(offset).length;
+ newVector._capacity = this.get(offset).length;
+
+ return newVector;
+ }
+
+ _ptr: T[]; // コンテナの先頭アドレス
+ _size: number; // コンテナの要素数
+ _capacity: number; // コンテナのキャパシティ
+
+ static readonly s_defaultSize = 10; // コンテナ初期化のデフォルトサイズ
+}
+
+export class iterator {
+ /**
+ * コンストラクタ
+ */
+ public constructor(v?: csmVector, index?: number) {
+ this._vector = v != undefined ? v : null;
+ this._index = index != undefined ? index : 0;
+ }
+
+ /**
+ * 代入
+ */
+ public set(ite: iterator): iterator {
+ this._index = ite._index;
+ this._vector = ite._vector;
+ return this;
+ }
+
+ /**
+ * 前置き++演算
+ */
+ public preIncrement(): iterator {
+ ++this._index;
+ return this;
+ }
+
+ /**
+ * 前置き--演算
+ */
+ public preDecrement(): iterator {
+ --this._index;
+ return this;
+ }
+
+ /**
+ * 後置き++演算子
+ */
+ public increment(): iterator {
+ const iteold = new iterator(this._vector, this._index++); // 古い値を保存
+ return iteold;
+ }
+
+ /**
+ * 後置き--演算子
+ */
+ public decrement(): iterator {
+ const iteold = new iterator(this._vector, this._index--); // 古い値を保存
+ return iteold;
+ }
+
+ /**
+ * ptr
+ */
+ public ptr(): T {
+ return this._vector._ptr[this._index];
+ }
+
+ /**
+ * =演算子のオーバーロード
+ */
+ public substitution(ite: iterator): iterator {
+ this._index = ite._index;
+ this._vector = ite._vector;
+ return this;
+ }
+
+ /**
+ * !=演算子のオーバーロード
+ */
+ public notEqual(ite: iterator): boolean {
+ return this._index != ite._index || this._vector != ite._vector;
+ }
+
+ _index: number; // コンテナのインデックス値
+ _vector: csmVector; // コンテナ
+}
+
+// Namespace definition for compatibility.
+import * as $ from './csmvector';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const csmVector = $.csmVector;
+ export type csmVector = $.csmVector;
+ export const iterator = $.iterator;
+ export type iterator = $.iterator;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismdebug.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismdebug.ts
new file mode 100644
index 000000000..4d6f1433a
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismdebug.ts
@@ -0,0 +1,162 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import {
+ CSM_LOG_LEVEL,
+ CSM_LOG_LEVEL_DEBUG,
+ CSM_LOG_LEVEL_ERROR,
+ CSM_LOG_LEVEL_INFO,
+ CSM_LOG_LEVEL_VERBOSE,
+ CSM_LOG_LEVEL_WARNING
+} from '../cubismframeworkconfig';
+import { CubismFramework, LogLevel } from '../live2dcubismframework';
+
+export const CubismLogPrint = (level: LogLevel, fmt: string, args: any[]) => {
+ CubismDebug.print(level, '[CSM]' + fmt, args);
+};
+
+export const CubismLogPrintIn = (level: LogLevel, fmt: string, args: any[]) => {
+ CubismLogPrint(level, fmt + '\n', args);
+};
+
+export const CSM_ASSERT = (expr: any) => {
+ console.assert(expr);
+};
+
+export let CubismLogVerbose: (fmt: string, ...args: any[]) => void;
+export let CubismLogDebug: (fmt: string, ...args: any[]) => void;
+export let CubismLogInfo: (fmt: string, ...args: any[]) => void;
+export let CubismLogWarning: (fmt: string, ...args: any[]) => void;
+export let CubismLogError: (fmt: string, ...args: any[]) => void;
+
+if (CSM_LOG_LEVEL <= CSM_LOG_LEVEL_VERBOSE) {
+ CubismLogVerbose = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Verbose, '[V]' + fmt, args);
+ };
+
+ CubismLogDebug = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
+ };
+
+ CubismLogInfo = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
+ };
+
+ CubismLogWarning = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
+ };
+
+ CubismLogError = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
+ };
+} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_DEBUG) {
+ CubismLogDebug = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Debug, '[D]' + fmt, args);
+ };
+
+ CubismLogInfo = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
+ };
+
+ CubismLogWarning = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
+ };
+
+ CubismLogError = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
+ };
+} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_INFO) {
+ CubismLogInfo = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Info, '[I]' + fmt, args);
+ };
+
+ CubismLogWarning = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
+ };
+
+ CubismLogError = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
+ };
+} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_WARNING) {
+ CubismLogWarning = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Warning, '[W]' + fmt, args);
+ };
+
+ CubismLogError = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
+ };
+} else if (CSM_LOG_LEVEL == CSM_LOG_LEVEL_ERROR) {
+ CubismLogError = (fmt: string, ...args: any[]) => {
+ CubismLogPrintIn(LogLevel.LogLevel_Error, '[E]' + fmt, args);
+ };
+}
+
+/**
+ * デバッグ用のユーティリティクラス。
+ * ログの出力、バイトのダンプなど
+ */
+export class CubismDebug {
+ /**
+ * ログを出力する。第一引数にログレベルを設定する。
+ * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
+ *
+ * @param logLevel ログレベルの設定
+ * @param format 書式付き文字列
+ * @param args 可変長引数
+ */
+ public static print(logLevel: LogLevel, format: string, args?: any[]): void {
+ // オプションで設定されたログ出力レベルを下回る場合はログに出さない
+ if (logLevel < CubismFramework.getLoggingLevel()) {
+ return;
+ }
+
+ const logPrint: Live2DCubismCore.csmLogFunction =
+ CubismFramework.coreLogFunction;
+
+ if (!logPrint) return;
+
+ const buffer: string = format.replace(/\{(\d+)\}/g, (m, k) => {
+ return args[k];
+ });
+ logPrint(buffer);
+ }
+
+ /**
+ * データから指定した長さだけダンプ出力する。
+ * CubismFramework.initialize()時にオプションで設定されたログ出力レベルを下回る場合はログに出さない。
+ *
+ * @param logLevel ログレベルの設定
+ * @param data ダンプするデータ
+ * @param length ダンプする長さ
+ */
+ public static dumpBytes(
+ logLevel: LogLevel,
+ data: Uint8Array,
+ length: number
+ ): void {
+ for (let i = 0; i < length; i++) {
+ if (i % 16 == 0 && i > 0) this.print(logLevel, '\n');
+ else if (i % 8 == 0 && i > 0) this.print(logLevel, ' ');
+ this.print(logLevel, '{0} ', [data[i] & 0xff]);
+ }
+
+ this.print(logLevel, '\n');
+ }
+
+ /**
+ * private コンストラクタ
+ */
+ private constructor() {}
+}
+
+// Namespace definition for compatibility.
+import * as $ from './cubismdebug';
+// eslint-disable-next-line @typescript-eslint/no-namespace
+export namespace Live2DCubismFramework {
+ export const CubismDebug = $.CubismDebug;
+ export type CubismDebug = $.CubismDebug;
+}
diff --git a/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismjson.ts b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismjson.ts
new file mode 100644
index 000000000..d8097a0cb
--- /dev/null
+++ b/ui/src/phaser3-rex-plugins/plugins/gameobjects/live2d/framework/src/utils/cubismjson.ts
@@ -0,0 +1,1253 @@
+/**
+ * Copyright(c) Live2D Inc. All rights reserved.
+ *
+ * Use of this source code is governed by the Live2D Open Software license
+ * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
+ */
+
+import { strtod } from '../live2dcubismframework';
+import { csmMap, iterator as csmMap_iterator } from '../type/csmmap';
+import { csmString } from '../type/csmstring';
+import { csmVector, iterator as csmVector_iterator } from '../type/csmvector';
+import { CubismLogInfo } from './cubismdebug';
+
+// StaticInitializeNotForClientCall()で初期化する
+const CSM_JSON_ERROR_TYPE_MISMATCH = 'Error: type mismatch';
+const CSM_JSON_ERROR_INDEX_OF_BOUNDS = 'Error: index out of bounds';
+
+/**
+ * パースしたJSONエレメントの要素の基底クラス。
+ */
+export abstract class Value {
+ /**
+ * コンストラクタ
+ */
+ public constructor() {}
+
+ /**
+ * 要素を文字列型で返す(csmString型)
+ */
+ public abstract getString(defaultValue?: string, indent?: string): string;
+
+ /**
+ * 要素を文字列型で返す(string)
+ */
+ public getRawString(defaultValue?: string, indent?: string): string {
+ return this.getString(defaultValue, indent);
+ }
+
+ /**
+ * 要素を数値型で返す(number)
+ */
+ public toInt(defaultValue = 0): number {
+ return defaultValue;
+ }
+
+ /**
+ * 要素を数値型で返す(number)
+ */
+ public toFloat(defaultValue = 0): number {
+ return defaultValue;
+ }
+
+ /**
+ * 要素を真偽値で返す(boolean)
+ */
+ public toBoolean(defaultValue = false): boolean {
+ return defaultValue;
+ }
+
+ /**
+ * サイズを返す
+ */
+ public getSize(): number {
+ return 0;
+ }
+
+ /**
+ * 要素を配列で返す(Value[])
+ */
+ public getArray(defaultValue: Value[] = null): Value[] {
+ return defaultValue;
+ }
+
+ /**
+ * 要素をコンテナで返す(array)
+ */
+ public getVector(defaultValue = new csmVector()): csmVector {
+ return defaultValue;
+ }
+
+ /**
+ * 要素をマップで返す(csmMap)
+ */
+ public getMap(defaultValue?: csmMap): csmMap