📖

PythonでコモンイベントのDSL

2024/05/17に公開

Pythonを使ってコモンイベントのjsonファイルを生成するためのDSL(ドメイン特化言語)を作成する方法を紹介します。DSLを使うことで、コモンイベントの定義をより簡単に、かつプログラム的に記述できるようになります。

ステップ1: 基本的なDSLの設計

まずはDSLの基本構造を設計します。DSLを使ってイベントを定義し、その定義からjsonファイルを生成する流れを考えます。

ステップ2: DSLの実装

以下に、Pythonを使った簡単なDSLの例を示します。このDSLを使って、コモンイベントのjsonファイルを生成します。

1. 基本的なイベントコマンドのクラス定義

import json

class EventCommand:
    def __init__(self, code, parameters, indent=0):
        self.code = code
        self.parameters = parameters
        self.indent = indent

    def to_dict(self):
        return {
            "code": self.code,
            "parameters": self.parameters,
            "indent": self.indent
        }

class ShowText(EventCommand):
    def __init__(self, character_name, text, position=2):
        parameters = [character_name, 0, 0, position, text]
        super().__init__(101, parameters)

class ShowChoices(EventCommand):
    def __init__(self, choices):
        parameters = choices
        super().__init__(102, parameters)

class Wait(EventCommand):
    def __init__(self, frames):
        parameters = [frames]
        super().__init__(230, parameters)

# 他のコマンドも同様に定義します...

2. コモンイベントのクラス定義

class CommonEvent:
    def __init__(self, event_id, name, trigger=0, switch_id=0):
        self.id = event_id
        self.name = name
        self.trigger = trigger
        self.switch_id = switch_id
        self.list = []

    def add_command(self, command):
        self.list.append(command)

    def to_dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "trigger": self.trigger,
            "switchId": self.switch_id,
            "list": [cmd.to_dict() for cmd in self.list]
        }

class CommonEvents:
    def __init__(self):
        self.events = []

    def add_event(self, event):
        self.events.append(event)

    def to_json(self, file_path):
        data = [event.to_dict() for event in self.events]
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

3. DSLを使ってコモンイベントを定義し、jsonファイルを生成する

if __name__ == "__main__":
    common_events = CommonEvents()

    # イベント1を作成
    event1 = CommonEvent(1, "Morning Assembly")
    event1.add_command(ShowText("Teacher", "Good morning, students! Today we have a special announcement."))
    event1.add_command(Wait(60))
    event1.add_command(ShowText("Teacher", "Let's begin the class."))
    common_events.add_event(event1)

    # イベント2を作成
    event2 = CommonEvent(2, "Lunch Break")
    event2.add_command(ShowText("Friend", "Let's go to the cafeteria together!"))
    common_events.add_event(event2)

    # 他のイベントを追加...

    # jsonファイルを生成
    common_events.to_json("common_events.json")

このDSLを使うことで、コモンイベントをプログラム的に定義し、それをjsonファイルとして出力できます。上記の例では、いくつかの基本的なコマンド(ShowText、Waitなど)を定義していますが、必要に応じて他のコマンドも同様の方法で追加できます。

ステップ3: コマンドの拡充

上記の例に基づいて、他のイベントコマンド(選択肢の表示、変数の操作など)も定義し、DSLを拡充していきましょう。これにより、より複雑なイベントも簡単に定義できるようになります。

ifコマンドの書き方

IfCommandを追加することで、条件分岐を持つイベントを作成できるようにします。以下に、IfCommandのクラスを追加し、DSLの機能を拡張する方法を示します。

ステップ1: IfCommandクラスの定義

まず、条件分岐を扱うIfCommandクラスを定義します。

class IfCommand(EventCommand):
    def __init__(self, condition, true_branch=None, false_branch=None):
        parameters = [condition]
        super().__init__(111, parameters)
        self.true_branch = true_branch if true_branch else []
        self.false_branch = false_branch if false_branch else []

    def add_true_command(self, command):
        self.true_branch.append(command)

    def add_false_command(self, command):
        self.false_branch.append(command)

    def to_dict(self):
        true_branch_dicts = [cmd.to_dict() for cmd in self.true_branch]
        false_branch_dicts = [cmd.to_dict() for cmd in self.false_branch]

        return {
            "code": self.code,
            "parameters": self.parameters,
            "indent": self.indent,
            "true_branch": true_branch_dicts,
            "false_branch": false_branch_dicts
        }

ステップ2: CommonEventクラスの修正

CommonEventクラスでIfCommandを処理できるように、イベントリストの展開方法を修正します。

class CommonEvent:
    def __init__(self, event_id, name, trigger=0, switch_id=0):
        self.id = event_id
        self.name = name
        self.trigger = trigger
        self.switch_id = switch_id
        self.list = []

    def add_command(self, command):
        self.list.append(command)

    def to_dict(self):
        event_list = []
        for cmd in self.list:
            event_list.append(cmd.to_dict())
            if isinstance(cmd, IfCommand):
                event_list.extend(cmd.true_branch)
                if cmd.false_branch:
                    event_list.append({"code": 411, "indent": 0})
                    event_list.extend(cmd.false_branch)
                event_list.append({"code": 412, "indent": 0})
        return {
            "id": self.id,
            "name": self.name,
            "trigger": self.trigger,
            "switchId": self.switch_id,
            "list": event_list
        }

ステップ3: DSLを使って条件分岐を含むイベントを定義

以下の例では、条件分岐を含むイベントをDSLを使って定義します。

if __name__ == "__main__":
    common_events = CommonEvents()

    # イベント1を作成
    event1 = CommonEvent(1, "Morning Assembly")
    event1.add_command(ShowText("Teacher", "Good morning, students! Today we have a special announcement."))
    event1.add_command(Wait(60))
    
    # 条件分岐の追加
    if_command = IfCommand("Variable 1 == 1")
    if_command.add_true_command(ShowText("Teacher", "Let's begin the class."))
    if_command.add_false_command(ShowText("Teacher", "Please take your seats."))
    
    event1.add_command(if_command)
    common_events.add_event(event1)

    # イベント2を作成
    event2 = CommonEvent(2, "Lunch Break")
    event2.add_command(ShowText("Friend", "Let's go to the cafeteria together!"))
    common_events.add_event(event2)

    # 他のイベントを追加...

    # jsonファイルを生成
    common_events.to_json("common_events.json")

ステップ4: IfCommandの条件とtrue/falseブランチの処理

IfCommandの条件に基づいて、イベントリストにtrue_branchfalse_branchのコマンドを展開します。これにより、条件分岐を持つイベントをjsonファイルとして出力できるようになります。

完成したコード

以下に、上記のすべてのコードをまとめます。

import json

class EventCommand:
    def __init__(self, code, parameters, indent=0):
        self.code = code
        self.parameters = parameters
        self.indent = indent

    def to_dict(self):
        return {
            "code": self.code,
            "parameters": self.parameters,
            "indent": self.indent
        }

class ShowText(EventCommand):
    def __init__(self, character_name, text, position=2):
        parameters = [character_name, 0, 0, position, text]
        super().__init__(101, parameters)

class ShowChoices(EventCommand):
    def __init__(self, choices):
        parameters = choices
        super().__init__(102, parameters)

class Wait(EventCommand):
    def __init__(self, frames):
        parameters = [frames]
        super().__init__(230, parameters)

class IfCommand(EventCommand):
    def __init__(self, condition, true_branch=None, false_branch=None):
        parameters = [condition]
        super().__init__(111, parameters)
        self.true_branch = true_branch if true_branch else []
        self.false_branch = false_branch if false_branch else []

    def add_true_command(self, command):
        self.true_branch.append(command)

    def add_false_command(self, command):
        self.false_branch.append(command)

    def to_dict(self):
        true_branch_dicts = [cmd.to_dict() for cmd in self.true_branch]
        false_branch_dicts = [cmd.to_dict() for cmd in self.false_branch]

        return {
            "code": self.code,
            "parameters": self.parameters,
            "indent": self.indent,
            "true_branch": true_branch_dicts,
            "false_branch": false_branch_dicts
        }

class CommonEvent:
    def __init__(self, event_id, name, trigger=0, switch_id=0):
        self.id = event_id
        self.name = name
        self.trigger = trigger
        self.switch_id = switch_id
        self.list = []

    def add_command(self, command):
        self.list.append(command)

    def to_dict(self):
        event_list = []
        for cmd in self.list:
            event_list.append(cmd.to_dict())
            if isinstance(cmd, IfCommand):
                event_list.extend(cmd.true_branch)
                if cmd.false_branch:
                    event_list.append({"code": 411, "indent": 0})
                    event_list.extend(cmd.false_branch)
                event_list.append({"code": 412, "indent": 0})
        return {
            "id": self.id,
            "name": self.name,
            "trigger": self.trigger,
            "switchId": self.switch_id,
            "list": event_list
        }

class CommonEvents:
    def __init__(self):
        self.events = []

    def add_event(self, event):
        self.events.append(event)

    def to_json(self, file_path):
        data = [event.to_dict() for event in self.events]
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    common_events = CommonEvents()

    # イベント1を作成
    event1 = CommonEvent(1, "Morning Assembly")
    event1.add_command(ShowText("Teacher", "Good morning, students! Today we have a special announcement."))
    event1.add_command(Wait(60))
    
    # 条件分岐の追加
    if_command = IfCommand("Variable 1 == 1")
    if_command.add_true_command(ShowText("Teacher", "Let's begin the class."))
    if_command.add_false_command(ShowText("Teacher", "Please take your seats."))
    
    event1.add_command(if_command)
    common_events.add_event(event1)

    # イベント2を作成
    event2 = CommonEvent(2, "Lunch Break")
    event2.add_command(ShowText("Friend", "Let's go to the cafeteria together!"))
    common_events.add_event(event2)

    # 他のイベントを追加...

    # jsonファイルを生成
    common_events.to_json("common_events.json")

このコードを実行することで、条件分岐を含むコモンイベントのjsonファイルを生成できます。IfCommandクラスを使うことで、イベントの条件分岐を簡単に定義できるようになりました。

Discussion