From 78bebc5b5c964c1e122d2a82657b9cec7c0e99b9 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Dec 2023 23:29:44 +0100 Subject: [PATCH 01/44] convert: Load nyan data API v0.4.1. --- .../convert/service/read/nyan_api_loader.py | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/openage/convert/service/read/nyan_api_loader.py b/openage/convert/service/read/nyan_api_loader.py index 067c4d1065..24bd7c10ef 100644 --- a/openage/convert/service/read/nyan_api_loader.py +++ b/openage/convert/service/read/nyan_api_loader.py @@ -111,6 +111,13 @@ def _create_objects(api_objects: dict[str, NyanObject]) -> None: nyan_object.set_fqon(fqon) api_objects.update({fqon: nyan_object}) + # engine.ability.type.Activity + parents = [api_objects["engine.ability.Ability"]] + nyan_object = NyanObject("Activity", parents) + fqon = "engine.ability.type.Activity" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + # engine.ability.type.ApplyContinuousEffect parents = [api_objects["engine.ability.Ability"]] nyan_object = NyanObject("ApplyContinuousEffect", parents) @@ -518,6 +525,111 @@ def _create_objects(api_objects: dict[str, NyanObject]) -> None: nyan_object.set_fqon(fqon) api_objects.update({fqon: nyan_object}) + # engine.util.activity.Activity + parents = [api_objects["engine.root.Object"]] + nyan_object = NyanObject("Activity", parents) + fqon = "engine.util.activity.Activity" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.condition.Condition + parents = [api_objects["engine.root.Object"]] + nyan_object = NyanObject("Condition", parents) + fqon = "engine.util.activity.condition.Condition" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.condition.type.CommandInQueue + parents = [api_objects["engine.util.activity.condition.Condition"]] + nyan_object = NyanObject("CommandInQueue", parents) + fqon = "engine.util.activity.condition.type.CommandInQueue" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.condition.type.NextCommandIdle + parents = [api_objects["engine.util.activity.condition.Condition"]] + nyan_object = NyanObject("NextCommandIdle", parents) + fqon = "engine.util.activity.condition.type.NextCommandIdle" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.condition.type.NextCommandMove + parents = [api_objects["engine.util.activity.condition.Condition"]] + nyan_object = NyanObject("NextCommandMove", parents) + fqon = "engine.util.activity.condition.type.NextCommandMove" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.event.Event + parents = [api_objects["engine.root.Object"]] + nyan_object = NyanObject("Event", parents) + fqon = "engine.util.activity.event.Event" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.event.type.CommandInQueue + parents = [api_objects["engine.util.activity.event.Event"]] + nyan_object = NyanObject("CommandInQueue", parents) + fqon = "engine.util.activity.event.type.CommandInQueue" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.event.type.Wait + parents = [api_objects["engine.util.activity.event.Event"]] + nyan_object = NyanObject("Wait", parents) + fqon = "engine.util.activity.event.type.Wait" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.event.type.WaitAbility + parents = [api_objects["engine.util.activity.event.Event"]] + nyan_object = NyanObject("WaitAbility", parents) + fqon = "engine.util.activity.event.type.WaitAbility" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.Node + parents = [api_objects["engine.root.Object"]] + nyan_object = NyanObject("Node", parents) + fqon = "engine.util.activity.node.Node" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.type.Ability + parents = [api_objects["engine.util.activity.node.Node"]] + nyan_object = NyanObject("Ability", parents) + fqon = "engine.util.activity.node.type.Ability" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.type.End + parents = [api_objects["engine.util.activity.node.Node"]] + nyan_object = NyanObject("End", parents) + fqon = "engine.util.activity.node.type.End" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.type.Start + parents = [api_objects["engine.util.activity.node.Node"]] + nyan_object = NyanObject("Start", parents) + fqon = "engine.util.activity.node.type.Start" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.type.XOREventGate + parents = [api_objects["engine.util.activity.node.Node"]] + nyan_object = NyanObject("XOREventGate", parents) + fqon = "engine.util.activity.node.type.XOREventGate" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + + # engine.util.activity.node.type.XORGate + parents = [api_objects["engine.util.activity.node.Node"]] + nyan_object = NyanObject("XORGate", parents) + fqon = "engine.util.activity.node.type.XORGate" + nyan_object.set_fqon(fqon) + api_objects.update({fqon: nyan_object}) + # engine.util.animation_override.AnimationOverride parents = [api_objects["engine.root.Object"]] nyan_object = NyanObject("AnimationOverride", parents) @@ -2489,6 +2601,13 @@ def _insert_members(api_objects: dict[str, NyanObject]) -> None: member = NyanMember("transform_progress", member_type, None, None, 0) api_object.add_member(member) + # engine.ability.type.Activity + api_object = api_objects["engine.ability.type.Activity"] + + member_type = NyanMemberType(api_objects["engine.util.activity.Activity"]) + member = NyanMember("graph", member_type, None, None, 0) + api_object.add_member(member) + # engine.ability.type.ApplyContinuousEffect api_object = api_objects["engine.ability.type.ApplyContinuousEffect"] @@ -3154,6 +3273,64 @@ def _insert_members(api_objects: dict[str, NyanObject]) -> None: member = NyanMember("blacklisted_entities", member_type, None, None, 0) api_object.add_member(member) + # engine.util.activity.Activity + api_object = api_objects["engine.util.activity.Activity"] + + member_type = NyanMemberType(api_objects["engine.util.activity.node.type.Start"]) + member = NyanMember("start", member_type, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.condition.Condition + api_object = api_objects["engine.util.activity.condition.Condition"] + + member_type = NyanMemberType(api_objects["engine.util.activity.node.Node"]) + member = NyanMember("next", member_type, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.event.type.Wait + api_object = api_objects["engine.util.activity.event.type.Wait"] + + member = NyanMember("time", N_FLOAT, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.node.type.Ability + api_object = api_objects["engine.util.activity.node.type.Ability"] + + member_type = NyanMemberType(api_objects["engine.util.activity.node.Node"]) + member = NyanMember("next", member_type, None, None, 0) + api_object.add_member(member) + subtype = NyanMemberType(api_objects["engine.ability.Ability"]) + member_type = NyanMemberType(MemberType.ABSTRACT, (subtype,)) + member = NyanMember("ability", member_type, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.node.type.Start + api_object = api_objects["engine.util.activity.node.type.Start"] + + member_type = NyanMemberType(api_objects["engine.util.activity.node.Node"]) + member = NyanMember("next", member_type, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.node.type.XOREventGate + api_object = api_objects["engine.util.activity.node.type.XOREventGate"] + + key_type = NyanMemberType(api_objects["engine.util.activity.event.Event"]) + value_type = NyanMemberType(api_objects["engine.util.activity.node.Node"]) + member_type = NyanMemberType(MemberType.DICT, (key_type, value_type)) + member = NyanMember("next", member_type, None, None, 0) + api_object.add_member(member) + + # engine.util.activity.node.type.XORGate + api_object = api_objects["engine.util.activity.node.type.XORGate"] + + elem_type = NyanMemberType(api_objects["engine.util.activity.condition.Condition"]) + member_type = NyanMemberType(MemberType.ORDEREDSET, (elem_type,)) + member = NyanMember("next", member_type, None, None, 0) + api_object.add_member(member) + member_type = NyanMemberType(api_objects["engine.util.activity.node.Node"]) + member = NyanMember("default", member_type, None, None, 0) + api_object.add_member(member) + # engine.util.animation_override.AnimationOverride api_object = api_objects["engine.util.animation_override.AnimationOverride"] From b7e693aefef95c052a3ccb1571ac56f374863494 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 7 Dec 2023 23:02:44 +0100 Subject: [PATCH 02/44] convert: Basic activity for units. --- .../conversion/aoc/pregen_processor.py | 292 ++++++++++++++++++ 1 file changed, 292 insertions(+) diff --git a/openage/convert/processor/conversion/aoc/pregen_processor.py b/openage/convert/processor/conversion/aoc/pregen_processor.py index fe6151ea20..ba41e11d86 100644 --- a/openage/convert/processor/conversion/aoc/pregen_processor.py +++ b/openage/convert/processor/conversion/aoc/pregen_processor.py @@ -35,6 +35,7 @@ def generate(cls, full_data_set: GenieObjectContainer) -> None: # Stores pregenerated raw API objects as a container pregen_converter_group = ConverterObjectGroup("pregen") + cls.generate_activities(full_data_set, pregen_converter_group) cls.generate_attributes(full_data_set, pregen_converter_group) cls.generate_diplomatic_stances(full_data_set, pregen_converter_group) cls.generate_team_property(full_data_set, pregen_converter_group) @@ -62,6 +63,297 @@ def generate(cls, full_data_set: GenieObjectContainer) -> None: raise RuntimeError(f"{repr(pregen_object)}: Pregenerated object is not ready " "for export. Member or object not initialized.") + @staticmethod + def generate_activities( + full_data_set: GenieObjectContainer, + pregen_converter_group: ConverterObjectGroup + ) -> None: + """ + Generate the activities for game entity behaviour. + + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + :type full_data_set: ...dataformat.aoc.genie_object_container.GenieObjectContainer + :param pregen_converter_group: GenieObjectGroup instance that stores + pregenerated API objects for referencing with + ForwardRef + :type pregen_converter_group: ...dataformat.aoc.genie_object_container.GenieObjectGroup + """ + pregen_nyan_objects = full_data_set.pregen_nyan_objects + api_objects = full_data_set.nyan_api_objects + + activity_parent = "engine.util.activity.Activity" + activity_location = "data/util/activity/" + + # Node types + start_parent = "engine.util.activity.node.type.Start" + end_parent = "engine.util.activity.node.type.End" + ability_parent = "engine.util.activity.node.type.Ability" + xor_parent = "engine.util.activity.node.type.XORGate" + xor_event_parent = "engine.util.activity.node.type.XOREventGate" + + # Condition types + condition_parent = "engine.util.activity.condition.Condition" + condition_queue_parent = "engine.util.activity.condition.type.CommandInQueue" + condition_next_move_parent = "engine.util.activity.condition.type.NextCommandMove" + + # ======================================================================= + # Default (Start -> Ability(Idle) -> End) + # ======================================================================= + default_ref_in_modpack = "util.activity.types.Default" + default_raw_api_object = RawAPIObject(default_ref_in_modpack, + "Default", api_objects, + activity_location) + default_raw_api_object.set_filename("types") + default_raw_api_object.add_raw_parent(activity_parent) + + start_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Default.Start") + default_raw_api_object.add_raw_member("start", start_forward_ref, + activity_parent) + + pregen_converter_group.add_raw_api_object(default_raw_api_object) + pregen_nyan_objects.update({default_ref_in_modpack: default_raw_api_object}) + + unit_forward_ref = ForwardRef(pregen_converter_group, default_ref_in_modpack) + + # Start + start_ref_in_modpack = "util.activity.types.Default.Start" + start_raw_api_object = RawAPIObject(start_ref_in_modpack, + "Start", api_objects) + start_raw_api_object.set_location(unit_forward_ref) + start_raw_api_object.add_raw_parent(start_parent) + + idle_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Default.Idle") + start_raw_api_object.add_raw_member("next", idle_forward_ref, + start_parent) + + pregen_converter_group.add_raw_api_object(start_raw_api_object) + pregen_nyan_objects.update({start_ref_in_modpack: start_raw_api_object}) + + # Idle + idle_ref_in_modpack = "util.activity.types.Default.Idle" + idle_raw_api_object = RawAPIObject(idle_ref_in_modpack, + "Idle", api_objects) + idle_raw_api_object.set_location(unit_forward_ref) + idle_raw_api_object.add_raw_parent(ability_parent) + + end_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Default.End") + idle_raw_api_object.add_raw_member("next", end_forward_ref, + ability_parent) + idle_raw_api_object.add_raw_member("ability", + api_objects["engine.ability.type.Idle"], + ability_parent) + + pregen_converter_group.add_raw_api_object(idle_raw_api_object) + pregen_nyan_objects.update({idle_ref_in_modpack: idle_raw_api_object}) + + # End + end_ref_in_modpack = "util.activity.types.Default.End" + end_raw_api_object = RawAPIObject(end_ref_in_modpack, + "End", api_objects) + end_raw_api_object.set_location(unit_forward_ref) + end_raw_api_object.add_raw_parent(end_parent) + + pregen_converter_group.add_raw_api_object(end_raw_api_object) + pregen_nyan_objects.update({end_ref_in_modpack: end_raw_api_object}) + + # ======================================================================= + # Units + # ======================================================================= + unit_ref_in_modpack = "util.activity.types.Unit" + unit_raw_api_object = RawAPIObject(unit_ref_in_modpack, + "Unit", api_objects, + activity_location) + unit_raw_api_object.set_filename("types") + unit_raw_api_object.add_raw_parent(activity_parent) + + start_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.Start") + unit_raw_api_object.add_raw_member("start", start_forward_ref, + activity_parent) + + pregen_converter_group.add_raw_api_object(unit_raw_api_object) + pregen_nyan_objects.update({unit_ref_in_modpack: unit_raw_api_object}) + + unit_forward_ref = ForwardRef(pregen_converter_group, unit_ref_in_modpack) + + # Start + start_ref_in_modpack = "util.activity.types.Unit.Start" + start_raw_api_object = RawAPIObject(start_ref_in_modpack, + "Start", api_objects) + start_raw_api_object.set_location(unit_forward_ref) + start_raw_api_object.add_raw_parent(start_parent) + + idle_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.Idle") + start_raw_api_object.add_raw_member("next", idle_forward_ref, + start_parent) + + pregen_converter_group.add_raw_api_object(start_raw_api_object) + pregen_nyan_objects.update({start_ref_in_modpack: start_raw_api_object}) + + # Idle + idle_ref_in_modpack = "util.activity.types.Unit.Idle" + idle_raw_api_object = RawAPIObject(idle_ref_in_modpack, + "Idle", api_objects) + idle_raw_api_object.set_location(unit_forward_ref) + idle_raw_api_object.add_raw_parent(ability_parent) + + queue_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.CheckQueue") + idle_raw_api_object.add_raw_member("next", queue_forward_ref, + ability_parent) + idle_raw_api_object.add_raw_member("ability", + api_objects["engine.ability.type.Idle"], + ability_parent) + + pregen_converter_group.add_raw_api_object(idle_raw_api_object) + pregen_nyan_objects.update({idle_ref_in_modpack: idle_raw_api_object}) + + # Check if command is in queue + queue_ref_in_modpack = "util.activity.types.Unit.CheckQueue" + queue_raw_api_object = RawAPIObject(queue_ref_in_modpack, + "CheckQueue", api_objects) + queue_raw_api_object.set_location(unit_forward_ref) + queue_raw_api_object.add_raw_parent(xor_parent) + + condition_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.CommandInQueue") + queue_raw_api_object.add_raw_member("next", + [condition_forward_ref], + xor_parent) + command_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.WaitForCommand") + queue_raw_api_object.add_raw_member("default", + command_forward_ref, + xor_parent) + + pregen_converter_group.add_raw_api_object(queue_raw_api_object) + pregen_nyan_objects.update({queue_ref_in_modpack: queue_raw_api_object}) + + # condition for command in queue + condition_ref_in_modpack = "util.activity.types.Unit.CommandInQueue" + condition_raw_api_object = RawAPIObject(condition_ref_in_modpack, + "CommandInQueue", api_objects) + condition_raw_api_object.set_location(queue_forward_ref) + condition_raw_api_object.add_raw_parent(condition_queue_parent) + + branch_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.BranchCommand") + condition_raw_api_object.add_raw_member("next", + branch_forward_ref, + condition_parent) + + pregen_converter_group.add_raw_api_object(condition_raw_api_object) + pregen_nyan_objects.update({condition_ref_in_modpack: condition_raw_api_object}) + + # Wait for Command + command_ref_in_modpack = "util.activity.types.Unit.WaitForCommand" + command_raw_api_object = RawAPIObject(command_ref_in_modpack, + "WaitForCommand", api_objects) + command_raw_api_object.set_location(unit_forward_ref) + command_raw_api_object.add_raw_parent(xor_event_parent) + + event_api_object = api_objects["engine.util.activity.event.type.CommandInQueue"] + branch_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.BranchCommand") + command_raw_api_object.add_raw_member("next", + {event_api_object: branch_forward_ref}, + xor_event_parent) + + pregen_converter_group.add_raw_api_object(command_raw_api_object) + pregen_nyan_objects.update({command_ref_in_modpack: command_raw_api_object}) + + # Branch on command type + branch_ref_in_modpack = "util.activity.types.Unit.BranchCommand" + branch_raw_api_object = RawAPIObject(branch_ref_in_modpack, + "BranchCommand", api_objects) + branch_raw_api_object.set_location(unit_forward_ref) + branch_raw_api_object.add_raw_parent(xor_parent) + + condition_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.NextCommandMove") + branch_raw_api_object.add_raw_member("next", + [condition_forward_ref], + xor_parent) + idle_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.Idle") + branch_raw_api_object.add_raw_member("default", + idle_forward_ref, + xor_parent) + + pregen_converter_group.add_raw_api_object(branch_raw_api_object) + pregen_nyan_objects.update({branch_ref_in_modpack: branch_raw_api_object}) + + # condition for branching to move + condition_ref_in_modpack = "util.activity.types.Unit.NextCommandMove" + condition_raw_api_object = RawAPIObject(condition_ref_in_modpack, + "NextCommandMove", api_objects) + condition_raw_api_object.set_location(branch_forward_ref) + condition_raw_api_object.add_raw_parent(condition_next_move_parent) + + move_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.Move") + condition_raw_api_object.add_raw_member("next", + move_forward_ref, + condition_parent) + + pregen_converter_group.add_raw_api_object(condition_raw_api_object) + pregen_nyan_objects.update({condition_ref_in_modpack: condition_raw_api_object}) + + # Move + move_ref_in_modpack = "util.activity.types.Unit.Move" + move_raw_api_object = RawAPIObject(move_ref_in_modpack, + "Move", api_objects) + move_raw_api_object.set_location(unit_forward_ref) + move_raw_api_object.add_raw_parent(ability_parent) + + wait_forward_ref = ForwardRef(pregen_converter_group, + "util.activity.types.Unit.Wait") + move_raw_api_object.add_raw_member("next", wait_forward_ref, + ability_parent) + move_raw_api_object.add_raw_member("ability", + api_objects["engine.ability.type.Move"], + ability_parent) + + pregen_converter_group.add_raw_api_object(move_raw_api_object) + pregen_nyan_objects.update({move_ref_in_modpack: move_raw_api_object}) + + # Wait (for Move or Command) + wait_ref_in_modpack = "util.activity.types.Unit.Wait" + wait_raw_api_object = RawAPIObject(wait_ref_in_modpack, + "Wait", api_objects) + wait_raw_api_object.set_location(unit_forward_ref) + wait_raw_api_object.add_raw_parent(xor_event_parent) + + wait_finish = api_objects["engine.util.activity.event.type.WaitAbility"] + wait_command = api_objects["engine.util.activity.event.type.CommandInQueue"] + wait_raw_api_object.add_raw_member("next", + { + wait_finish: queue_forward_ref, + # TODO: don't go back to move, go to xor gate that + # branches depending on command + wait_command: branch_forward_ref + }, + xor_event_parent) + + pregen_converter_group.add_raw_api_object(wait_raw_api_object) + pregen_nyan_objects.update({wait_ref_in_modpack: wait_raw_api_object}) + + # End + end_ref_in_modpack = "util.activity.types.Unit.End" + end_raw_api_object = RawAPIObject(end_ref_in_modpack, + "End", api_objects) + end_raw_api_object.set_location(unit_forward_ref) + end_raw_api_object.add_raw_parent(end_parent) + + pregen_converter_group.add_raw_api_object(end_raw_api_object) + pregen_nyan_objects.update({end_ref_in_modpack: end_raw_api_object}) + @staticmethod def generate_attributes( full_data_set: GenieObjectContainer, From b62266d91aebc54fa4d4f65adc17e73aa512ac9b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 10 Dec 2023 23:59:57 +0100 Subject: [PATCH 03/44] convert: Export new API version as modpack. --- openage/convert/service/init/api_export_required.py | 2 +- openage/convert/tool/api_export.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openage/convert/service/init/api_export_required.py b/openage/convert/service/init/api_export_required.py index 61b8f5ba4d..105527e176 100644 --- a/openage/convert/service/init/api_export_required.py +++ b/openage/convert/service/init/api_export_required.py @@ -16,7 +16,7 @@ from openage.util.fslike.union import UnionPath -CURRENT_API_VERSION = "0.4.0" +CURRENT_API_VERSION = "0.4.1" def api_export_required(asset_dir: UnionPath) -> bool: diff --git a/openage/convert/tool/api_export.py b/openage/convert/tool/api_export.py index e0d14e703e..0e35ec4602 100644 --- a/openage/convert/tool/api_export.py +++ b/openage/convert/tool/api_export.py @@ -76,7 +76,7 @@ def create_modpack() -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("engine", "0.4.0", repo="openage") + mod_def.set_info("engine", "0.4.1", "0.4.1", repo="openage") mod_def.add_include("**") From 33a53f3fcb87e19d4d511bbc7edda7b8b73be37d Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 11 Dec 2023 00:30:03 +0100 Subject: [PATCH 04/44] convert: Activity ability. --- .../conversion/aoc/ability_subprocessor.py | 41 ++++++++++++++++++- .../conversion/aoc/modpack_subprocessor.py | 2 +- .../conversion/aoc/nyan_subprocessor.py | 1 + .../aoc_demo/modpack_subprocessor.py | 2 +- .../conversion/de1/modpack_subprocessor.py | 2 +- .../conversion/de2/modpack_subprocessor.py | 2 +- .../conversion/de2/nyan_subprocessor.py | 1 + .../conversion/hd/modpack_subprocessor.py | 2 +- .../conversion/ror/modpack_subprocessor.py | 2 +- .../conversion/ror/nyan_subprocessor.py | 3 +- .../conversion/ror/pregen_subprocessor.py | 1 + .../conversion/swgbcc/modpack_subprocessor.py | 2 +- .../conversion/swgbcc/nyan_subprocessor.py | 1 + .../conversion/swgbcc/pregen_subprocessor.py | 1 + 14 files changed, 54 insertions(+), 9 deletions(-) diff --git a/openage/convert/processor/conversion/aoc/ability_subprocessor.py b/openage/convert/processor/conversion/aoc/ability_subprocessor.py index 8e53371b2f..ffa0a98e57 100644 --- a/openage/convert/processor/conversion/aoc/ability_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/ability_subprocessor.py @@ -302,6 +302,45 @@ def apply_continuous_effect_ability( return ability_forward_ref + @staticmethod + def activity_ability(line: GenieGameEntityGroup) -> ForwardRef: + """ + Adds the Activity ability to a line. + + :param line: Unit/Building line that gets the ability. + :type line: ...dataformat.converter_object.ConverterObjectGroup + :returns: The forward reference for the ability. + :rtype: ...dataformat.forward_ref.ForwardRef + """ + current_unit_id = line.get_head_unit_id() + + dataset = line.data + + name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version) + + game_entity_name = name_lookup_dict[current_unit_id][0] + + ability_ref = f"{game_entity_name}.Activity" + ability_raw_api_object = RawAPIObject(ability_ref, "Activity", dataset.nyan_api_objects) + ability_raw_api_object.add_raw_parent("engine.ability.type.Activity") + ability_location = ForwardRef(line, game_entity_name) + ability_raw_api_object.set_location(ability_location) + + # activity graph + if isinstance(line, GenieUnitLineGroup): + activity = dataset.pregen_nyan_objects["util.activity.types.Unit"].get_nyan_object() + + else: + activity = dataset.pregen_nyan_objects["util.activity.types.Default"].get_nyan_object() + + ability_raw_api_object.add_raw_member("graph", activity, "engine.ability.type.Activity") + + line.add_raw_api_object(ability_raw_api_object) + + ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id()) + + return ability_forward_ref + @staticmethod def apply_discrete_effect_ability( line: GenieGameEntityGroup, @@ -614,7 +653,7 @@ def apply_discrete_effect_ability( return ability_forward_ref @staticmethod - def attribute_change_tracker_ability(line) -> ForwardRef: + def attribute_change_tracker_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the AttributeChangeTracker ability to a line. diff --git a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py index 8d54d68fed..29764c5823 100644 --- a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py @@ -44,7 +44,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("aoe2_base", "0.5", versionstr="1.0c", repo="openage") + mod_def.set_info("aoe2_base", "0.5.1", versionstr="1.0c", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/aoc/nyan_subprocessor.py b/openage/convert/processor/conversion/aoc/nyan_subprocessor.py index b519412ca3..d280b5d9c1 100644 --- a/openage/convert/processor/conversion/aoc/nyan_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/nyan_subprocessor.py @@ -219,6 +219,7 @@ def unit_line_to_game_entity(unit_line: GenieUnitLineGroup) -> None: # ======================================================================= abilities_set = [] + abilities_set.append(AoCAbilitySubprocessor.activity_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.death_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.delete_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.despawn_ability(unit_line)) diff --git a/openage/convert/processor/conversion/aoc_demo/modpack_subprocessor.py b/openage/convert/processor/conversion/aoc_demo/modpack_subprocessor.py index ffd171214e..8ae5c83705 100644 --- a/openage/convert/processor/conversion/aoc_demo/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/aoc_demo/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_demo_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("trial_base", "0.5", versionstr="Trial", repo="openage") + mod_def.set_info("trial_base", "0.5.1", versionstr="Trial", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/de1/modpack_subprocessor.py b/openage/convert/processor/conversion/de1/modpack_subprocessor.py index 9cc3f5a95d..1a431e81d2 100644 --- a/openage/convert/processor/conversion/de1/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/de1/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_aoe1_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("de1_base", "0.5", versionstr="1.0a", repo="openage") + mod_def.set_info("de1_base", "0.5.1", versionstr="1.0a", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/de2/modpack_subprocessor.py b/openage/convert/processor/conversion/de2/modpack_subprocessor.py index 22d9d220e2..810ff96752 100644 --- a/openage/convert/processor/conversion/de2/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/de2/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("de2_base", "0.5", versionstr="1.0c", repo="openage") + mod_def.set_info("de2_base", "0.5.1", versionstr="1.0c", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/de2/nyan_subprocessor.py b/openage/convert/processor/conversion/de2/nyan_subprocessor.py index b8f40000ef..242f67e7cd 100644 --- a/openage/convert/processor/conversion/de2/nyan_subprocessor.py +++ b/openage/convert/processor/conversion/de2/nyan_subprocessor.py @@ -219,6 +219,7 @@ def unit_line_to_game_entity(unit_line: GenieUnitLineGroup) -> None: # ======================================================================= abilities_set = [] + abilities_set.append(AoCAbilitySubprocessor.activity_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.death_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.delete_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.despawn_ability(unit_line)) diff --git a/openage/convert/processor/conversion/hd/modpack_subprocessor.py b/openage/convert/processor/conversion/hd/modpack_subprocessor.py index 50e77ba711..8be52ac744 100644 --- a/openage/convert/processor/conversion/hd/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/hd/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("hd_base", "0.5", versionstr="5.8", repo="openage") + mod_def.set_info("hd_base", "0.5.1", versionstr="5.8", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/ror/modpack_subprocessor.py b/openage/convert/processor/conversion/ror/modpack_subprocessor.py index dfe485b003..ff7746e861 100644 --- a/openage/convert/processor/conversion/ror/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/ror/modpack_subprocessor.py @@ -41,7 +41,7 @@ def _get_aoe1_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("aoe1_base", "0.5", versionstr="1.0a", repo="openage") + mod_def.set_info("aoe1_base", "0.5.1", versionstr="1.0a", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/ror/nyan_subprocessor.py b/openage/convert/processor/conversion/ror/nyan_subprocessor.py index 75262402ae..a06a86e1dc 100644 --- a/openage/convert/processor/conversion/ror/nyan_subprocessor.py +++ b/openage/convert/processor/conversion/ror/nyan_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 the openage authors. See copying.md for legal info. +# Copyright 2020-2023 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-lines,too-many-locals,too-many-statements,too-many-branches # @@ -209,6 +209,7 @@ def unit_line_to_game_entity(unit_line): # ======================================================================= abilities_set = [] + abilities_set.append(AoCAbilitySubprocessor.activity_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.death_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.delete_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.despawn_ability(unit_line)) diff --git a/openage/convert/processor/conversion/ror/pregen_subprocessor.py b/openage/convert/processor/conversion/ror/pregen_subprocessor.py index 4bb4eb0ad1..271110bedc 100644 --- a/openage/convert/processor/conversion/ror/pregen_subprocessor.py +++ b/openage/convert/processor/conversion/ror/pregen_subprocessor.py @@ -32,6 +32,7 @@ def generate(cls, full_data_set: GenieObjectContainer) -> None: # Stores pregenerated raw API objects as a container pregen_converter_group = ConverterObjectGroup("pregen") + AoCPregenSubprocessor.generate_activities(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_attributes(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_diplomatic_stances(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_entity_types(full_data_set, pregen_converter_group) diff --git a/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py b/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py index cef278f9eb..5a69a47516 100644 --- a/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_swgb_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("swgb_base", "0.5", versionstr="1.1-gog4", repo="openage") + mod_def.set_info("swgb_base", "0.5.1", versionstr="1.1-gog4", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/swgbcc/nyan_subprocessor.py b/openage/convert/processor/conversion/swgbcc/nyan_subprocessor.py index b48b15d309..9fd2c52242 100644 --- a/openage/convert/processor/conversion/swgbcc/nyan_subprocessor.py +++ b/openage/convert/processor/conversion/swgbcc/nyan_subprocessor.py @@ -217,6 +217,7 @@ def unit_line_to_game_entity(unit_line: GenieUnitLineGroup) -> None: # ======================================================================= abilities_set = [] + abilities_set.append(AoCAbilitySubprocessor.activity_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.death_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.delete_ability(unit_line)) abilities_set.append(AoCAbilitySubprocessor.despawn_ability(unit_line)) diff --git a/openage/convert/processor/conversion/swgbcc/pregen_subprocessor.py b/openage/convert/processor/conversion/swgbcc/pregen_subprocessor.py index be0d20e705..a9114648c7 100644 --- a/openage/convert/processor/conversion/swgbcc/pregen_subprocessor.py +++ b/openage/convert/processor/conversion/swgbcc/pregen_subprocessor.py @@ -37,6 +37,7 @@ def generate(cls, full_data_set: GenieObjectContainer) -> None: # Stores pregenerated raw API objects as a container pregen_converter_group = ConverterObjectGroup("pregen") + AoCPregenSubprocessor.generate_activities(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_attributes(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_diplomatic_stances(full_data_set, pregen_converter_group) AoCPregenSubprocessor.generate_team_property(full_data_set, pregen_converter_group) From 884a15f6c6537fa57f2cd9e9514e986afb0f2719 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 17 Dec 2023 09:49:15 +0100 Subject: [PATCH 05/44] gamestate: Docstrings for activity. --- libopenage/gamestate/activity/activity.h | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/libopenage/gamestate/activity/activity.h b/libopenage/gamestate/activity/activity.h index f5be9d254a..5bc93b565c 100644 --- a/libopenage/gamestate/activity/activity.h +++ b/libopenage/gamestate/activity/activity.h @@ -18,19 +18,52 @@ using activity_label = std::string; */ class Activity { public: + /** + * Create a new activity. + * + * @param id Unique ID. + * @param label Human-readable label (optional). + * @param start Start node in the graph. + */ Activity(activity_id id, activity_label label = "", - const std::shared_ptr &start = {}); + const std::shared_ptr &start = nullptr); + /** + * Get the unique ID of this activity. + * + * @return Unique ID. + */ activity_id get_id() const; + /** + * Get the human-readable label of this activity. + * + * @return Human-readable label. + */ const activity_label get_label() const; + /** + * Get the start node of this activity. + * + * @return Start node. + */ const std::shared_ptr &get_start() const; private: + /** + * Unique ID. + */ const activity_id id; + + /** + * Human-readable label. + */ const activity_label label; + + /** + * Start node. + */ std::shared_ptr start; }; From 33ced296e10957384e1b720b011e38c74d0ee273 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 17 Dec 2023 09:49:38 +0100 Subject: [PATCH 06/44] gamestate: API interface for nyan activities. --- libopenage/gamestate/api/CMakeLists.txt | 1 + libopenage/gamestate/api/activity.cpp | 65 +++++++++++++++++++++ libopenage/gamestate/api/activity.h | 76 +++++++++++++++++++++++++ libopenage/gamestate/api/definitions.h | 28 ++++++++- 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 libopenage/gamestate/api/activity.cpp create mode 100644 libopenage/gamestate/api/activity.h diff --git a/libopenage/gamestate/api/CMakeLists.txt b/libopenage/gamestate/api/CMakeLists.txt index f079f11be3..f3ad8d7b40 100644 --- a/libopenage/gamestate/api/CMakeLists.txt +++ b/libopenage/gamestate/api/CMakeLists.txt @@ -1,5 +1,6 @@ add_sources(libopenage ability.cpp + activity.cpp animation.cpp definitions.cpp patch.cpp diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp new file mode 100644 index 0000000000..19e67e65d6 --- /dev/null +++ b/libopenage/gamestate/api/activity.cpp @@ -0,0 +1,65 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "activity.h" + +#include "gamestate/api/definitions.h" + + +namespace openage::gamestate::api { + +bool APIActivity::is_activity(const nyan::Object &obj) { + nyan::fqon_t immediate_parent = obj.get_parents()[0]; + return immediate_parent == "engine.util.activity.Activity"; +} + +nyan::Object APIActivity::get_start(const nyan::Object &activity) { + auto obj_value = activity.get("Activity.start"); + + std::shared_ptr db_view = activity.get_view(); + return db_view->get_object(obj_value->get_name()); +} + + +bool APIActivityNode::is_node(const nyan::Object &obj) { + nyan::fqon_t immediate_parent = obj.get_parents()[0]; + return immediate_parent == "engine.util.activity.node.Node"; +} + +activity::node_t APIActivityNode::get_type(const nyan::Object &node) { + nyan::fqon_t immediate_parent = node.get_parents()[0]; + return ACTIVITY_NODE_DEFS.get(immediate_parent); +} + +std::vector APIActivityNode::get_next(const nyan::Object &node) { + switch (APIActivityNode::get_type(node)) { + // 0 next nodes + case activity::node_t::END: { + return {}; + } + // 1 next node + case activity::node_t::TASK_SYSTEM: + case activity::node_t::START: { + auto next = node.get("Node.next"); + std::shared_ptr db_view = node.get_view(); + return {db_view->get_object(next->get_name())}; + } + // 1+ next nodes + case activity::node_t::XOR_GATE: + case activity::node_t::XOR_EVENT_GATE: { + auto next = node.get("Node.next"); + std::shared_ptr db_view = node.get_view(); + + std::vector next_nodes; + for (auto &next_node : next->get()) { + auto next_node_value = std::dynamic_pointer_cast(next_node.second.get_ptr()); + next_nodes.push_back(db_view->get_object(next_node_value->get_name())); + } + + return next_nodes; + } + default: + throw Error(MSG(err) << "Unknown activity node type."); + } +} + +} // namespace openage::gamestate::api diff --git a/libopenage/gamestate/api/activity.h b/libopenage/gamestate/api/activity.h new file mode 100644 index 0000000000..28e7c88dbe --- /dev/null +++ b/libopenage/gamestate/api/activity.h @@ -0,0 +1,76 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include + +#include "gamestate/activity/types.h" + + +namespace openage::gamestate { + +namespace api { + +/** + * Helper class for creating Activity objects from the nyan API. + */ +class APIActivity { +public: + /** + * Check if a nyan object is an Activity (type == \p engine.util.activity.Activity). + * + * @param obj nyan object. + * + * @return true if the object is an activity, else false. + */ + static bool is_activity(const nyan::Object &obj); + + /** + * Get the start node of an activity. + * + * @param activity nyan object. + * + * @return nyan object handle of the start node. + */ + static nyan::Object get_start(const nyan::Object &activity); +}; + + +class APIActivityNode { +public: + /** + * Check if a nyan object is a node (type == \p engine.util.activity.node.Node). + * + * @param obj nyan object. + * + * @return true if the object is a node, else false. + */ + static bool is_node(const nyan::Object &obj); + + /** + * Get the type of a node. + * + * @param node nyan object. + * + * @return Type of the node. + */ + static activity::node_t get_type(const nyan::Object &node); + + /** + * Get the next nodes of a node. + * + * The number of next nodes depends on the type of the node and can range + * from 0 (end nodes) to n (gateways). + * + * @param node nyan object. + * + * @return nyan object handles of the next nodes. + */ + static std::vector get_next(const nyan::Object &node); +}; + + +} // namespace api +} // namespace openage::gamestate diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index d06553c89e..0a2c452a0a 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -8,12 +8,15 @@ #include #include "datastructure/constexpr_map.h" +#include "gamestate/activity/types.h" #include "gamestate/api/types.h" namespace openage::gamestate::api { -/** Maps internal ability types to nyan API values **/ +/** + * Maps internal ability types to nyan API values. + **/ static const auto ABILITY_DEFS = datastructure::create_const_map( std::pair(ability_t::IDLE, nyan::ValueHolder(std::make_shared("engine.ability.type.Idle"))), @@ -24,8 +27,9 @@ static const auto ABILITY_DEFS = datastructure::create_const_map("engine.ability.type.Turn")))); - -/** Maps internal property types to nyan API values **/ +/** + * Maps internal property types to nyan API values. + **/ static const auto ABILITY_PROPERTY_DEFS = datastructure::create_const_map( std::pair(ability_property_t::ANIMATED, nyan::ValueHolder(std::make_shared("engine.ability.property.type.Animated"))), @@ -40,6 +44,24 @@ static const auto ABILITY_PROPERTY_DEFS = datastructure::create_const_map("engine.ability.property.type.Lock")))); +/** + * Maps API activity node types to engine node types. + */ +static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( + std::pair("engine.util.activity.node.type.Start", + activity::node_t::START), + std::pair("engine.util.activity.node.type.End", + activity::node_t::END), + std::pair("engine.util.activity.node.type.Ability", + activity::node_t::TASK_SYSTEM), + std::pair("engine.util.activity.node.type.XORGate", + activity::node_t::XOR_GATE), + std::pair("engine.util.activity.node.type.XOREventGate", + activity::node_t::XOR_EVENT_GATE)); + +/** + * Maps internal patch property types to nyan API values. + **/ static const auto PATCH_PROPERTY_DEFS = datastructure::create_const_map( std::pair(patch_property_t::DIPLOMATIC, nyan::ValueHolder(std::make_shared("engine.patch.property.type.Diplomatic")))); From 943e67a6a2fd7e044d9ca075096d0e4b255ae743 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 17 Dec 2023 16:53:26 +0100 Subject: [PATCH 07/44] gamestate: Create activity from nyan data. --- libopenage/gamestate/activity/activity.cpp | 4 +- libopenage/gamestate/activity/activity.h | 6 +- libopenage/gamestate/entity_factory.cpp | 90 +++++++++++++++++++++- libopenage/gamestate/entity_factory.h | 5 ++ 4 files changed, 96 insertions(+), 9 deletions(-) diff --git a/libopenage/gamestate/activity/activity.cpp b/libopenage/gamestate/activity/activity.cpp index 7ee478eda8..9af770b8fd 100644 --- a/libopenage/gamestate/activity/activity.cpp +++ b/libopenage/gamestate/activity/activity.cpp @@ -6,8 +6,8 @@ namespace openage::gamestate::activity { Activity::Activity(activity_id id, - activity_label label, - const std::shared_ptr &start) : + const std::shared_ptr &start, + activity_label label) : id{id}, label{label}, start{start} { diff --git a/libopenage/gamestate/activity/activity.h b/libopenage/gamestate/activity/activity.h index 5bc93b565c..d0832068ad 100644 --- a/libopenage/gamestate/activity/activity.h +++ b/libopenage/gamestate/activity/activity.h @@ -22,12 +22,12 @@ class Activity { * Create a new activity. * * @param id Unique ID. - * @param label Human-readable label (optional). * @param start Start node in the graph. + * @param label Human-readable label (optional). */ Activity(activity_id id, - activity_label label = "", - const std::shared_ptr &start = nullptr); + const std::shared_ptr &start, + activity_label label = ""); /** * Get the unique ID of this activity. diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index f6c3d6f660..d8ff2361b8 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -19,6 +19,7 @@ #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_system_node.h" #include "gamestate/activity/xor_node.h" +#include "gamestate/api/activity.h" #include "gamestate/component/api/idle.h" #include "gamestate/component/api/live.h" #include "gamestate/component/api/move.h" @@ -149,7 +150,7 @@ std::shared_ptr create_test_activity() { return 1; // idle->get_id(); }); - return std::make_shared(0, "test", start); + return std::make_shared(0, start, "test"); } EntityFactory::EntityFactory() : @@ -210,6 +211,7 @@ void EntityFactory::init_components(const std::shared_ptrget_object(nyan_entity); nyan::set_t abilities = nyan_obj.get_set("GameEntity.abilities"); + std::optional activity_ability; for (const auto &ability_val : abilities) { auto ability_fqon = std::dynamic_pointer_cast(ability_val.get_ptr())->get_name(); auto ability_obj = owner_db_view->get_object(ability_fqon); @@ -247,11 +249,91 @@ void EntityFactory::init_components(const std::shared_ptr(loop, create_test_activity()); + entity->add_component(activity); + } +} + +void EntityFactory::init_activity(const std::shared_ptr &loop, + const std::shared_ptr &owner_db_view, + const std::shared_ptr &entity, + const nyan::Object &ability) { + nyan::Object graph = ability.get_object("Activity.graph"); + auto start_obj = api::APIActivity::get_start(graph); + + size_t node_id = 0; + + std::deque nyan_nodes; + std::unordered_map> node_id_map{}; + std::unordered_map visited{}; + std::shared_ptr start_node; + + // First pass: create all nodes using breadth-first search + nyan_nodes.push_back(start_obj); + while (!nyan_nodes.empty()) { + auto node = nyan_nodes.front(); + nyan_nodes.pop_front(); + + if (visited.contains(node.get_name())) { + continue; + } + + // Create the node + switch (api::APIActivityNode::get_type(node)) { + case activity::node_t::END: + break; + case activity::node_t::START: + start_node = std::make_shared(node_id); + node_id_map[node_id] = start_node; + break; + case activity::node_t::TASK_SYSTEM: + node_id_map[node_id] = std::make_shared(node_id); + break; + case activity::node_t::XOR_GATE: + node_id_map[node_id] = std::make_shared(node_id); + break; + case activity::node_t::XOR_EVENT_GATE: + node_id_map[node_id] = std::make_shared(node_id); + break; + default: + throw Error{ERR << "Unknown activity node type"}; + } + + // Get the node's outputs + auto next_nodes = api::APIActivityNode::get_next(node); + nyan_nodes.insert(nyan_nodes.end(), next_nodes.begin(), next_nodes.end()); + + visited.insert({node.get_name(), node_id}); + node_id++; } - // must be initialized after all other components - auto activity = std::make_shared(loop, create_test_activity()); - entity->add_component(activity); + // Second pass: connect the nodes + for (const auto &node : visited) { + auto nyan_node = owner_db_view->get_object(node.first); + auto activity_node = node_id_map[node.second]; + + auto next_nodes = api::APIActivityNode::get_next(nyan_node); + for (const auto &next_node : next_nodes) { + auto next_node_id = visited[next_node.get_name()]; + auto next_engine_node = node_id_map[next_node_id]; + + activity_node->add_output(next_engine_node); + } + } + + auto activity = std::make_shared(0, start_node, graph.get_name()); + + auto component = std::make_shared(loop, activity); + entity->add_component(component); } entity_id_t EntityFactory::get_next_entity_id() { diff --git a/libopenage/gamestate/entity_factory.h b/libopenage/gamestate/entity_factory.h index 5a369d995e..728a91b820 100644 --- a/libopenage/gamestate/entity_factory.h +++ b/libopenage/gamestate/entity_factory.h @@ -92,6 +92,11 @@ class EntityFactory { const std::shared_ptr &entity, const nyan::fqon_t &nyan_entity); + void init_activity(const std::shared_ptr &loop, + const std::shared_ptr &owner_db_view, + const std::shared_ptr &entity, + const nyan::Object &ability); + /** * Get a unique ID for creating a game entity. * From eba3c96ac9a2fd5f7850dc9359e99b307305f56e Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 18 Dec 2023 01:23:35 +0100 Subject: [PATCH 08/44] gamestate: Rename activity node files. --- libopenage/gamestate/activity/CMakeLists.txt | 4 ++-- libopenage/gamestate/activity/tests.cpp | 4 ++-- .../activity/{event_node.cpp => xor_event_gate.cpp} | 10 +++++----- .../activity/{event_node.h => xor_event_gate.h} | 0 .../gamestate/activity/{xor_node.cpp => xor_gate.cpp} | 8 ++++---- .../gamestate/activity/{xor_node.h => xor_gate.h} | 0 libopenage/gamestate/entity_factory.cpp | 4 ++-- libopenage/gamestate/system/activity.cpp | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) rename libopenage/gamestate/activity/{event_node.cpp => xor_event_gate.cpp} (75%) rename libopenage/gamestate/activity/{event_node.h => xor_event_gate.h} (100%) rename libopenage/gamestate/activity/{xor_node.cpp => xor_gate.cpp} (73%) rename libopenage/gamestate/activity/{xor_node.h => xor_gate.h} (100%) diff --git a/libopenage/gamestate/activity/CMakeLists.txt b/libopenage/gamestate/activity/CMakeLists.txt index 27b43c1776..b005c32001 100644 --- a/libopenage/gamestate/activity/CMakeLists.txt +++ b/libopenage/gamestate/activity/CMakeLists.txt @@ -1,12 +1,12 @@ add_sources(libopenage activity.cpp end_node.cpp - event_node.cpp node.cpp start_node.cpp task_node.cpp task_system_node.cpp tests.cpp types.cpp - xor_node.cpp + xor_event_gate.cpp + xor_gate.cpp ) diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index 7588e5a9b0..cc0f64a691 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -15,12 +15,12 @@ #include "log/message.h" #include "gamestate/activity/end_node.h" -#include "gamestate/activity/event_node.h" #include "gamestate/activity/node.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_node.h" #include "gamestate/activity/types.h" -#include "gamestate/activity/xor_node.h" +#include "gamestate/activity/xor_event_gate.h" +#include "gamestate/activity/xor_gate.h" #include "time/time.h" diff --git a/libopenage/gamestate/activity/event_node.cpp b/libopenage/gamestate/activity/xor_event_gate.cpp similarity index 75% rename from libopenage/gamestate/activity/event_node.cpp rename to libopenage/gamestate/activity/xor_event_gate.cpp index 3d28e007a3..f3b4ddb0e9 100644 --- a/libopenage/gamestate/activity/event_node.cpp +++ b/libopenage/gamestate/activity/xor_event_gate.cpp @@ -1,6 +1,6 @@ // Copyright 2023-2023 the openage authors. See copying.md for legal info. -#include "event_node.h" +#include "xor_event_gate.h" #include @@ -8,10 +8,10 @@ namespace openage::gamestate::activity { XorEventGate::XorEventGate(node_id id, - node_label label, - const std::vector> &outputs, - event_primer_func_t primer_func, - event_next_func_t next_func) : + node_label label, + const std::vector> &outputs, + event_primer_func_t primer_func, + event_next_func_t next_func) : Node{id, label, outputs}, primer_func{primer_func}, next_func{next_func} { diff --git a/libopenage/gamestate/activity/event_node.h b/libopenage/gamestate/activity/xor_event_gate.h similarity index 100% rename from libopenage/gamestate/activity/event_node.h rename to libopenage/gamestate/activity/xor_event_gate.h diff --git a/libopenage/gamestate/activity/xor_node.cpp b/libopenage/gamestate/activity/xor_gate.cpp similarity index 73% rename from libopenage/gamestate/activity/xor_node.cpp rename to libopenage/gamestate/activity/xor_gate.cpp index 644362f877..771dbfbe29 100644 --- a/libopenage/gamestate/activity/xor_node.cpp +++ b/libopenage/gamestate/activity/xor_gate.cpp @@ -1,6 +1,6 @@ // Copyright 2023-2023 the openage authors. See copying.md for legal info. -#include "xor_node.h" +#include "xor_gate.h" #include @@ -8,9 +8,9 @@ namespace openage::gamestate::activity { XorGate::XorGate(node_id id, - node_label label, - const std::vector> &outputs, - condition_func_t condition_func) : + node_label label, + const std::vector> &outputs, + condition_func_t condition_func) : Node{id, label, outputs}, condition_func{condition_func} { } diff --git a/libopenage/gamestate/activity/xor_node.h b/libopenage/gamestate/activity/xor_gate.h similarity index 100% rename from libopenage/gamestate/activity/xor_node.h rename to libopenage/gamestate/activity/xor_gate.h diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index d8ff2361b8..95ac3c5397 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -15,10 +15,10 @@ #include "event/event_loop.h" #include "gamestate/activity/activity.h" #include "gamestate/activity/end_node.h" -#include "gamestate/activity/event_node.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_system_node.h" -#include "gamestate/activity/xor_node.h" +#include "gamestate/activity/xor_event_gate.h" +#include "gamestate/activity/xor_gate.h" #include "gamestate/api/activity.h" #include "gamestate/component/api/idle.h" #include "gamestate/component/api/live.h" diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index 240b5c7c50..fe37142bfe 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -9,13 +9,13 @@ #include "error/error.h" #include "log/message.h" -#include "gamestate/activity/event_node.h" #include "gamestate/activity/node.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_node.h" #include "gamestate/activity/task_system_node.h" #include "gamestate/activity/types.h" -#include "gamestate/activity/xor_node.h" +#include "gamestate/activity/xor_event_gate.h" +#include "gamestate/activity/xor_gate.h" #include "gamestate/component/internal/activity.h" #include "gamestate/component/types.h" #include "gamestate/game_entity.h" From a9e7d4474b5d072d7b5f760d05caa18f26b7148d Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 18 Dec 2023 02:09:41 +0100 Subject: [PATCH 09/44] gamestate: Refactor XorGate to use one condition per node. --- libopenage/gamestate/activity/end_node.cpp | 4 +- libopenage/gamestate/activity/end_node.h | 4 +- libopenage/gamestate/activity/node.cpp | 10 +- libopenage/gamestate/activity/node.h | 20 ++-- libopenage/gamestate/activity/start_node.cpp | 6 +- libopenage/gamestate/activity/start_node.h | 6 +- libopenage/gamestate/activity/task_node.cpp | 6 +- libopenage/gamestate/activity/task_node.h | 6 +- .../gamestate/activity/task_system_node.cpp | 6 +- .../gamestate/activity/task_system_node.h | 6 +- .../gamestate/activity/xor_event_gate.cpp | 4 +- .../gamestate/activity/xor_event_gate.h | 18 ++-- libopenage/gamestate/activity/xor_gate.cpp | 60 +++++++++++- libopenage/gamestate/activity/xor_gate.h | 96 +++++++++++++++++-- libopenage/gamestate/api/definitions.h | 6 +- 15 files changed, 201 insertions(+), 57 deletions(-) diff --git a/libopenage/gamestate/activity/end_node.cpp b/libopenage/gamestate/activity/end_node.cpp index 01ecfebade..bb5de4898d 100644 --- a/libopenage/gamestate/activity/end_node.cpp +++ b/libopenage/gamestate/activity/end_node.cpp @@ -8,8 +8,8 @@ namespace openage::gamestate::activity { -EndNode::EndNode(node_id id, - node_label label) : +EndNode::EndNode(node_id_t id, + node_label_t label) : Node{id, label, {}} { } diff --git a/libopenage/gamestate/activity/end_node.h b/libopenage/gamestate/activity/end_node.h index c068f39475..ff8754cc18 100644 --- a/libopenage/gamestate/activity/end_node.h +++ b/libopenage/gamestate/activity/end_node.h @@ -26,8 +26,8 @@ class EndNode : public Node { * @param id Unique identifier for this node. * @param label Human-readable label (optional). */ - EndNode(node_id id, - node_label label = "End"); + EndNode(node_id_t id, + node_label_t label = "End"); virtual ~EndNode() = default; inline node_t get_type() const override { diff --git a/libopenage/gamestate/activity/node.cpp b/libopenage/gamestate/activity/node.cpp index 5c7f007d74..833b4031eb 100644 --- a/libopenage/gamestate/activity/node.cpp +++ b/libopenage/gamestate/activity/node.cpp @@ -10,8 +10,8 @@ namespace openage::gamestate::activity { -Node::Node(node_id id, - node_label label, +Node::Node(node_id_t id, + node_label_t label, const std::vector> &outputs) : outputs{}, id{id}, @@ -22,11 +22,11 @@ Node::Node(node_id id, } } -node_id Node::get_id() const { +node_id_t Node::get_id() const { return this->id; } -const node_label Node::get_label() const { +const node_label_t Node::get_label() const { return this->label; } @@ -42,7 +42,7 @@ std::string Node::str() const { return ret.str(); } -const std::shared_ptr &Node::next(node_id id) const { +const std::shared_ptr &Node::next(node_id_t id) const { if (not this->outputs.contains(id)) [[unlikely]] { throw Error{MSG(err) << "Node " << this->str() << " has no output with id " << id}; } diff --git a/libopenage/gamestate/activity/node.h b/libopenage/gamestate/activity/node.h index 9a8edfcf9f..303793fa99 100644 --- a/libopenage/gamestate/activity/node.h +++ b/libopenage/gamestate/activity/node.h @@ -13,8 +13,8 @@ namespace openage::gamestate::activity { -using node_id = size_t; -using node_label = std::string; +using node_id_t = size_t; +using node_label_t = std::string; /** * Node in the flow graph describing the activity. @@ -28,8 +28,8 @@ class Node { * @param label Human-readable label (optional). * @param outputs Output nodes. */ - Node(node_id id, - node_label label = "", + Node(node_id_t id, + node_label_t label = "", const std::vector> &outputs = {}); virtual ~Node() = default; @@ -45,14 +45,14 @@ class Node { * * @return The unique identifier. */ - node_id get_id() const; + node_id_t get_id() const; /** * Get the human-readable label for this node. * * @return Human-readable label. */ - const node_label get_label() const; + const node_label_t get_label() const; /** * Get a human-readable string representation of this node. @@ -67,7 +67,7 @@ class Node { * @param id Unique identifier of the output node. * @return Output node. */ - const std::shared_ptr &next(node_id id) const; + const std::shared_ptr &next(node_id_t id) const; /** * Add an output node. @@ -80,18 +80,18 @@ class Node { /** * Output nodes. */ - std::unordered_map> outputs; + std::unordered_map> outputs; private: /** * Unique identifier for this node. */ - const node_id id; + const node_id_t id; /** * Human-readable label. */ - const node_label label; + const node_label_t label; }; } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/start_node.cpp b/libopenage/gamestate/activity/start_node.cpp index 8686fc2f4e..a6b400fb1e 100644 --- a/libopenage/gamestate/activity/start_node.cpp +++ b/libopenage/gamestate/activity/start_node.cpp @@ -8,8 +8,8 @@ namespace openage::gamestate::activity { -StartNode::StartNode(node_id id, - node_label label, +StartNode::StartNode(node_id_t id, + node_label_t label, const std::shared_ptr &output) : Node{id, label} { if (output) { @@ -22,7 +22,7 @@ void StartNode::add_output(const std::shared_ptr &output) { this->outputs.emplace(output->get_id(), output); } -node_id StartNode::get_next() const { +node_id_t StartNode::get_next() const { return (*this->outputs.begin()).first; } diff --git a/libopenage/gamestate/activity/start_node.h b/libopenage/gamestate/activity/start_node.h index bec65e6ea7..125827b8ac 100644 --- a/libopenage/gamestate/activity/start_node.h +++ b/libopenage/gamestate/activity/start_node.h @@ -25,8 +25,8 @@ class StartNode : public Node { * @param label Human-readable label (optional). * @param output Next node to visit (can be set later). */ - StartNode(node_id id, - node_label label = "Start", + StartNode(node_id_t id, + node_label_t label = "Start", const std::shared_ptr &output = nullptr); virtual ~StartNode() = default; @@ -49,7 +49,7 @@ class StartNode : public Node { * @param time Current time. * @return Next node to visit. */ - node_id get_next() const; + node_id_t get_next() const; }; } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/task_node.cpp b/libopenage/gamestate/activity/task_node.cpp index 36b5de6663..59186eb451 100644 --- a/libopenage/gamestate/activity/task_node.cpp +++ b/libopenage/gamestate/activity/task_node.cpp @@ -8,8 +8,8 @@ namespace openage::gamestate::activity { -TaskCustom::TaskCustom(node_id id, - node_label label, +TaskCustom::TaskCustom(node_id_t id, + node_label_t label, const std::shared_ptr &output, task_func_t task_func) : Node{id, label}, @@ -32,7 +32,7 @@ task_func_t TaskCustom::get_task_func() const { return this->task_func; } -node_id TaskCustom::get_next() const { +node_id_t TaskCustom::get_next() const { return (*this->outputs.begin()).first; } diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h index 0efd3f784b..b3c7cb822b 100644 --- a/libopenage/gamestate/activity/task_node.h +++ b/libopenage/gamestate/activity/task_node.h @@ -39,8 +39,8 @@ class TaskCustom : public Node { * @param task_func Action to perform when visiting this node (can be set later). * @param output Next node to visit (optional). */ - TaskCustom(node_id id, - node_label label = "TaskCustom", + TaskCustom(node_id_t id, + node_label_t label = "TaskCustom", const std::shared_ptr &output = nullptr, task_func_t task_func = no_task); virtual ~TaskCustom() = default; @@ -76,7 +76,7 @@ class TaskCustom : public Node { * @param time Current time. * @return Next node to visit. */ - node_id get_next() const; + node_id_t get_next() const; private: /** diff --git a/libopenage/gamestate/activity/task_system_node.cpp b/libopenage/gamestate/activity/task_system_node.cpp index 158bcc41b2..c73e00475e 100644 --- a/libopenage/gamestate/activity/task_system_node.cpp +++ b/libopenage/gamestate/activity/task_system_node.cpp @@ -8,8 +8,8 @@ namespace openage::gamestate::activity { -TaskSystemNode::TaskSystemNode(node_id id, - node_label label, +TaskSystemNode::TaskSystemNode(node_id_t id, + node_label_t label, const std::shared_ptr &output, system::system_id_t system_id) : Node{id, label}, @@ -32,7 +32,7 @@ system::system_id_t TaskSystemNode::get_system_id() const { return this->system_id; } -node_id TaskSystemNode::get_next() const { +node_id_t TaskSystemNode::get_next() const { return (*this->outputs.begin()).first; } diff --git a/libopenage/gamestate/activity/task_system_node.h b/libopenage/gamestate/activity/task_system_node.h index 8f9e6b1163..d594fcdae2 100644 --- a/libopenage/gamestate/activity/task_system_node.h +++ b/libopenage/gamestate/activity/task_system_node.h @@ -25,8 +25,8 @@ class TaskSystemNode : public Node { * @param output Next node to visit (optional). * @param system_id System to run when visiting this node (can be set later). */ - TaskSystemNode(node_id id, - node_label label = "TaskSystem", + TaskSystemNode(node_id_t id, + node_label_t label = "TaskSystem", const std::shared_ptr &output = nullptr, system::system_id_t system_id = system::system_id_t::NONE); virtual ~TaskSystemNode() = default; @@ -62,7 +62,7 @@ class TaskSystemNode : public Node { * @param time Current time. * @return Next node to visit. */ - node_id get_next() const; + node_id_t get_next() const; private: /** diff --git a/libopenage/gamestate/activity/xor_event_gate.cpp b/libopenage/gamestate/activity/xor_event_gate.cpp index f3b4ddb0e9..e76cb60558 100644 --- a/libopenage/gamestate/activity/xor_event_gate.cpp +++ b/libopenage/gamestate/activity/xor_event_gate.cpp @@ -7,8 +7,8 @@ namespace openage::gamestate::activity { -XorEventGate::XorEventGate(node_id id, - node_label label, +XorEventGate::XorEventGate(node_id_t id, + node_label_t label, const std::vector> &outputs, event_primer_func_t primer_func, event_next_func_t next_func) : diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h index a753682b68..a27e79d891 100644 --- a/libopenage/gamestate/activity/xor_event_gate.h +++ b/libopenage/gamestate/activity/xor_event_gate.h @@ -54,12 +54,15 @@ using event_primer_func_t = std::function &, - const std::shared_ptr &, - const std::shared_ptr &)>; +using event_next_func_t = std::function &, + const std::shared_ptr &, + const std::shared_ptr &)>; +/** + * Default primer function that throws an error. + */ static const event_primer_func_t no_event = [](const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, @@ -68,6 +71,9 @@ static const event_primer_func_t no_event = [](const time::time_t &, return event_store_t{}; }; +/** + * Default next function that throws an error. + */ static const event_next_func_t no_next = [](const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, @@ -91,8 +97,8 @@ class XorEventGate : public Node { * @param primer_func Function to create and register the event. * @param next_func Function to decide which node to visit after the event is handled. */ - XorEventGate(node_id id, - node_label label = "Event", + XorEventGate(node_id_t id, + node_label_t label = "Event", const std::vector> &outputs = {}, event_primer_func_t primer_func = no_event, event_next_func_t next_func = no_next); diff --git a/libopenage/gamestate/activity/xor_gate.cpp b/libopenage/gamestate/activity/xor_gate.cpp index 771dbfbe29..73cf8a5270 100644 --- a/libopenage/gamestate/activity/xor_gate.cpp +++ b/libopenage/gamestate/activity/xor_gate.cpp @@ -7,18 +7,52 @@ namespace openage::gamestate::activity { -XorGate::XorGate(node_id id, - node_label label, +XorGate::XorGate(node_id_t id, + node_label_t label, const std::vector> &outputs, condition_func_t condition_func) : Node{id, label, outputs}, - condition_func{condition_func} { + condition_func{condition_func}, + conditions{}, + default_id{std::nullopt} { +} + +XorGate::XorGate(node_id_t id, + node_label_t label, + const std::vector> &outputs, + const std::vector &conditions, + const node_id_t default_id) : + Node{id, label, outputs}, + condition_func{no_condition}, + conditions{}, + default_id{std::nullopt} { + if (conditions.size() != outputs.size()) [[unlikely]] { + throw Error{MSG(err) << "XorGate " << this->str() << " has " << outputs.size() + << " outputs but " << conditions.size() << " conditions"}; + } + + for (size_t i = 0; i < conditions.size(); ++i) { + this->conditions.emplace(outputs[i]->get_id(), conditions[i]); + } + + this->set_default_id(default_id); } void XorGate::add_output(const std::shared_ptr &output) { this->outputs.emplace(output->get_id(), output); } +void XorGate::add_output(const std::shared_ptr &output, + const condition_t condition_func) { + this->outputs.emplace(output->get_id(), output); + this->conditions.emplace(output->get_id(), condition_func); + + // If this is the first output, set it as the default. + if (not this->default_id) [[unlikely]] { + this->default_id = output->get_id(); + } +} + void XorGate::set_condition_func(condition_func_t condition_func) { this->condition_func = condition_func; } @@ -27,4 +61,24 @@ condition_func_t XorGate::get_condition_func() const { return this->condition_func; } +const std::map &XorGate::get_conditions() const { + return this->conditions; +} + +node_id_t XorGate::get_default_id() const { + if (not this->default_id) [[unlikely]] { + throw Error{MSG(err) << "XorGate " << this->str() << " has no default output"}; + } + + return this->default_id.value(); +} + +void XorGate::set_default_id(node_id_t id) { + if (not this->outputs.contains(id)) [[unlikely]] { + throw Error{MSG(err) << "XorGate " << this->str() << " has no output with id " << id}; + } + + this->default_id = id; +} + } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/xor_gate.h b/libopenage/gamestate/activity/xor_gate.h index 39efca6209..adc2b5bf05 100644 --- a/libopenage/gamestate/activity/xor_gate.h +++ b/libopenage/gamestate/activity/xor_gate.h @@ -3,7 +3,9 @@ #pragma once #include +#include #include +#include #include #include "error/error.h" @@ -19,17 +21,38 @@ class GameEntity; namespace activity { -using condition_func_t = std::function &)>; +/** + * Function that determines if an output node is chosen. + * + * @param time Current game time. + * @param entity Entity that is executing the activity. + * + * @return true if the output node is chosen, false otherwise. + */ +using condition_t = std::function &)>; + +/** + * Function that determines which output node is chosen. + * + * @param time Current game time. + * @param entity Entity that is executing the activity. + * @return ID of the output node that is chosen. + */ +using condition_func_t = std::function &)>; +/** + * Default condition function that throws an error. + */ static const condition_func_t no_condition = [](const time::time_t &, - const std::shared_ptr &) -> node_id { + const std::shared_ptr &) -> node_id_t { throw Error{MSG(err) << "No condition function set."}; }; /** - * Chooses one of its output nodes based on a condition. + * Chooses one of its output nodes based on conditions. */ class XorGate : public Node { public: @@ -42,10 +65,26 @@ class XorGate : public Node { * @param condition_func Function that determines which output node is chosen (can be set later). * This must be a valid node ID of one of the output nodes. */ - XorGate(node_id id, - node_label label = "ExclusiveGateway", + XorGate(node_id_t id, + node_label_t label = "ExclusiveGateway", const std::vector> &outputs = {}, condition_func_t condition_func = no_condition); + + /** + * Creates a new condition node. + * + * @param id Unique identifier of the node. + * @param label Label of the node. + * @param outputs Output nodes. + * @param conditions Conditions for each output node. + * @param default_id Default output node ID. + */ + XorGate(node_id_t id, + node_label_t label, + const std::vector> &outputs, + const std::vector &conditions, + const node_id_t default_id); + virtual ~XorGate() = default; inline node_t get_type() const override { @@ -59,6 +98,16 @@ class XorGate : public Node { */ void add_output(const std::shared_ptr &output) override; + /** + * Add an output node. + * + * @param output Output node. + * @param condition_func Function that determines whether this output node is chosen. + * This must be a valid node ID of one of the output nodes. + */ + void add_output(const std::shared_ptr &output, + const condition_t condition_func); + /** * Set the function that determines which output node is chosen. * @@ -74,11 +123,46 @@ class XorGate : public Node { */ condition_func_t get_condition_func() const; + /** + * Get the output->condition mappings. + * + * @return Conditions for each output node. + */ + const std::map &get_conditions() const; + + /** + * Get the ID of the default output node. + * + * @return Default output node ID. + */ + node_id_t get_default_id() const; + + /** + * Set the ID of the default output node. + * + * The ID must be a valid node ID of one of the output nodes. + * + * @param id Default output node ID. + */ + void set_default_id(node_id_t id); + private: /** * Determines which output node is chosen. */ condition_func_t condition_func; + + /** + * Maps output node IDs to condition functions. + * + * Conditions are checked in order they appear in the map. + */ + std::map conditions; + + /** + * Default output node ID. Chosen if no condition is true. + */ + std::optional default_id; }; } // namespace activity diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index 0a2c452a0a..d97372962f 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -16,7 +16,7 @@ namespace openage::gamestate::api { /** * Maps internal ability types to nyan API values. - **/ + */ static const auto ABILITY_DEFS = datastructure::create_const_map( std::pair(ability_t::IDLE, nyan::ValueHolder(std::make_shared("engine.ability.type.Idle"))), @@ -29,7 +29,7 @@ static const auto ABILITY_DEFS = datastructure::create_const_map( std::pair(ability_property_t::ANIMATED, nyan::ValueHolder(std::make_shared("engine.ability.property.type.Animated"))), @@ -61,7 +61,7 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( std::pair(patch_property_t::DIPLOMATIC, nyan::ValueHolder(std::make_shared("engine.patch.property.type.Diplomatic")))); From c586d7f91a2215de49dc3c679a758b9c98986650 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 18 Dec 2023 02:26:39 +0100 Subject: [PATCH 10/44] gamestate: Adjust activity demo for new condition. --- libopenage/gamestate/activity/tests.cpp | 34 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index cc0f64a691..b43cf9ed9f 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -138,8 +138,14 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current); - auto condition = node->get_condition_func(); - auto next_id = condition(0, nullptr); + auto next_id = node->get_default_id(); + for (auto &condition : node->get_conditions()) { + auto condition_func = condition.second; + if (condition_func(0, nullptr)) { + next_id = condition.first; + break; + } + } current = node->next(next_id); } break; case activity::node_t::XOR_EVENT_GATE: { @@ -203,23 +209,27 @@ void activity_demo() { }); // Conditional branch - size_t counter = 0; - xor_node->add_output(task1); - xor_node->add_output(event_node); - xor_node->set_condition_func([&](const time::time_t & /* time */, - const std::shared_ptr & /* entity */) { + static size_t counter = 0; + activity::condition_t branch_task1 = [&](const time::time_t & /* time */, + const std::shared_ptr & /* entity */) { log::log(INFO << "Checking condition (counter < 4): counter=" << counter); if (counter < 4) { log::log(INFO << "Selecting path 1 (back to task node " << task1->get_id() << ")"); counter++; - return task1->get_id(); + return true; } - + return false; + }; + xor_node->add_output(task1, branch_task1); + activity::condition_t branch_event = [&](const time::time_t & /* time */, + const std::shared_ptr & /* entity */) { + // No check needed here, the event node is always selected log::log(INFO << "Selecting path 2 (to event node " << event_node->get_id() << ")"); - - return event_node->get_id(); - }); + return true; + }; + xor_node->add_output(event_node, branch_event); + xor_node->set_default_id(event_node->get_id()); // event node event_node->add_output(task2); From c960ab86f75304cf988106b990b8ba5fb1153541 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 18 Dec 2023 02:46:54 +0100 Subject: [PATCH 11/44] gamestate: Remove old condition functionality. --- libopenage/gamestate/activity/end_node.cpp | 4 -- libopenage/gamestate/activity/end_node.h | 9 ---- libopenage/gamestate/activity/node.h | 7 --- libopenage/gamestate/activity/start_node.h | 2 +- libopenage/gamestate/activity/task_node.h | 2 +- .../gamestate/activity/task_system_node.h | 2 +- .../gamestate/activity/xor_event_gate.h | 2 +- libopenage/gamestate/activity/xor_gate.cpp | 20 +------ libopenage/gamestate/activity/xor_gate.h | 52 +------------------ libopenage/gamestate/entity_factory.cpp | 41 ++++++++------- libopenage/gamestate/system/activity.cpp | 10 +++- 11 files changed, 38 insertions(+), 113 deletions(-) diff --git a/libopenage/gamestate/activity/end_node.cpp b/libopenage/gamestate/activity/end_node.cpp index bb5de4898d..f933a08172 100644 --- a/libopenage/gamestate/activity/end_node.cpp +++ b/libopenage/gamestate/activity/end_node.cpp @@ -13,8 +13,4 @@ EndNode::EndNode(node_id_t id, Node{id, label, {}} { } -void EndNode::add_output(const std::shared_ptr & /* output */) { - throw Error{ERR << "End node cannot have outputs"}; -} - } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/end_node.h b/libopenage/gamestate/activity/end_node.h index ff8754cc18..e20323d04a 100644 --- a/libopenage/gamestate/activity/end_node.h +++ b/libopenage/gamestate/activity/end_node.h @@ -33,15 +33,6 @@ class EndNode : public Node { inline node_t get_type() const override { return node_t::END; } - - /** - * Throws an error since end nodes are not supposed to have outputs - * - * @param output Output node. - * - * @throws openage::Error - */ - [[noreturn]] void add_output(const std::shared_ptr &output) override; }; } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/node.h b/libopenage/gamestate/activity/node.h index 303793fa99..ed56277930 100644 --- a/libopenage/gamestate/activity/node.h +++ b/libopenage/gamestate/activity/node.h @@ -69,13 +69,6 @@ class Node { */ const std::shared_ptr &next(node_id_t id) const; - /** - * Add an output node. - * - * @param output Output node. - */ - virtual void add_output(const std::shared_ptr &output) = 0; - protected: /** * Output nodes. diff --git a/libopenage/gamestate/activity/start_node.h b/libopenage/gamestate/activity/start_node.h index 125827b8ac..6a9406b381 100644 --- a/libopenage/gamestate/activity/start_node.h +++ b/libopenage/gamestate/activity/start_node.h @@ -41,7 +41,7 @@ class StartNode : public Node { * * @param output Output node. */ - void add_output(const std::shared_ptr &output) override; + void add_output(const std::shared_ptr &output); /** * Get the next node to visit. diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h index b3c7cb822b..a5eddf59c2 100644 --- a/libopenage/gamestate/activity/task_node.h +++ b/libopenage/gamestate/activity/task_node.h @@ -54,7 +54,7 @@ class TaskCustom : public Node { * * @param output Output node. */ - void add_output(const std::shared_ptr &output) override; + void add_output(const std::shared_ptr &output); /** * Set the task function. diff --git a/libopenage/gamestate/activity/task_system_node.h b/libopenage/gamestate/activity/task_system_node.h index d594fcdae2..74513cbd64 100644 --- a/libopenage/gamestate/activity/task_system_node.h +++ b/libopenage/gamestate/activity/task_system_node.h @@ -40,7 +40,7 @@ class TaskSystemNode : public Node { * * @param output Output node. */ - void add_output(const std::shared_ptr &output) override; + void add_output(const std::shared_ptr &output); /** * Set the system id. diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h index a27e79d891..bce572376b 100644 --- a/libopenage/gamestate/activity/xor_event_gate.h +++ b/libopenage/gamestate/activity/xor_event_gate.h @@ -113,7 +113,7 @@ class XorEventGate : public Node { * * @param output Output node. */ - void add_output(const std::shared_ptr &output) override; + void add_output(const std::shared_ptr &output); /** * Set the function to create the event. diff --git a/libopenage/gamestate/activity/xor_gate.cpp b/libopenage/gamestate/activity/xor_gate.cpp index 73cf8a5270..6069968ba1 100644 --- a/libopenage/gamestate/activity/xor_gate.cpp +++ b/libopenage/gamestate/activity/xor_gate.cpp @@ -8,11 +8,8 @@ namespace openage::gamestate::activity { XorGate::XorGate(node_id_t id, - node_label_t label, - const std::vector> &outputs, - condition_func_t condition_func) : - Node{id, label, outputs}, - condition_func{condition_func}, + node_label_t label) : + Node{id, label, {}}, conditions{}, default_id{std::nullopt} { } @@ -23,7 +20,6 @@ XorGate::XorGate(node_id_t id, const std::vector &conditions, const node_id_t default_id) : Node{id, label, outputs}, - condition_func{no_condition}, conditions{}, default_id{std::nullopt} { if (conditions.size() != outputs.size()) [[unlikely]] { @@ -38,10 +34,6 @@ XorGate::XorGate(node_id_t id, this->set_default_id(default_id); } -void XorGate::add_output(const std::shared_ptr &output) { - this->outputs.emplace(output->get_id(), output); -} - void XorGate::add_output(const std::shared_ptr &output, const condition_t condition_func) { this->outputs.emplace(output->get_id(), output); @@ -53,14 +45,6 @@ void XorGate::add_output(const std::shared_ptr &output, } } -void XorGate::set_condition_func(condition_func_t condition_func) { - this->condition_func = condition_func; -} - -condition_func_t XorGate::get_condition_func() const { - return this->condition_func; -} - const std::map &XorGate::get_conditions() const { return this->conditions; } diff --git a/libopenage/gamestate/activity/xor_gate.h b/libopenage/gamestate/activity/xor_gate.h index adc2b5bf05..34870472cf 100644 --- a/libopenage/gamestate/activity/xor_gate.h +++ b/libopenage/gamestate/activity/xor_gate.h @@ -32,24 +32,6 @@ namespace activity { using condition_t = std::function &)>; -/** - * Function that determines which output node is chosen. - * - * @param time Current game time. - * @param entity Entity that is executing the activity. - * @return ID of the output node that is chosen. - */ -using condition_func_t = std::function &)>; - -/** - * Default condition function that throws an error. - */ -static const condition_func_t no_condition = [](const time::time_t &, - const std::shared_ptr &) -> node_id_t { - throw Error{MSG(err) << "No condition function set."}; -}; - /** * Chooses one of its output nodes based on conditions. @@ -61,14 +43,9 @@ class XorGate : public Node { * * @param id Unique identifier of the node. * @param label Label of the node (optional). - * @param outputs Output nodes (can be set later). - * @param condition_func Function that determines which output node is chosen (can be set later). - * This must be a valid node ID of one of the output nodes. */ XorGate(node_id_t id, - node_label_t label = "ExclusiveGateway", - const std::vector> &outputs = {}, - condition_func_t condition_func = no_condition); + node_label_t label = "ExclusiveGateway"); /** * Creates a new condition node. @@ -95,34 +72,12 @@ class XorGate : public Node { * Add an output node. * * @param output Output node. - */ - void add_output(const std::shared_ptr &output) override; - - /** - * Add an output node. - * - * @param output Output node. * @param condition_func Function that determines whether this output node is chosen. * This must be a valid node ID of one of the output nodes. */ void add_output(const std::shared_ptr &output, const condition_t condition_func); - /** - * Set the function that determines which output node is chosen. - * - * @param condition_func Function that determines which output node is chosen. - * This must be a valid node ID of one of the output nodes. - */ - void set_condition_func(condition_func_t condition_func); - - /** - * Get the function that determines which output node is chosen. - * - * @return Function that determines which output node is chosen. - */ - condition_func_t get_condition_func() const; - /** * Get the output->condition mappings. * @@ -147,11 +102,6 @@ class XorGate : public Node { void set_default_id(node_id_t id); private: - /** - * Determines which output node is chosen. - */ - condition_func_t condition_func; - /** * Maps output node IDs to condition functions. * diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 95ac3c5397..4727e6cda5 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -79,16 +79,21 @@ std::shared_ptr create_test_activity() { idle->add_output(condition_moveable); idle->set_system_id(system::system_id_t::IDLE); - condition_moveable->add_output(wait_for_command); - condition_moveable->add_output(end); - condition_moveable->set_condition_func([&](const time::time_t & /* time */, - const std::shared_ptr &entity) { - if (entity->has_component(component::component_t::MOVE)) { - return 3; // wait_for_command->get_id(); - } - - return 7; // end->get_id(); - }); + // wait_for_command branch + activity::condition_t wait_branch = [&](const time::time_t & /* time */, + const std::shared_ptr &entity) { + return entity->has_component(component::component_t::MOVE); + }; + condition_moveable->add_output(wait_for_command, wait_branch); + + // end branch + activity::condition_t end_branch = [&](const time::time_t & /* time */, + const std::shared_ptr & /* entity */) { + // no checks, this is the default branch + return true; + }; + condition_moveable->add_output(end, end_branch); + condition_moveable->set_default_id(end->get_id()); wait_for_command->add_output(move); wait_for_command->set_primer_func([](const time::time_t & /* time */, @@ -254,13 +259,13 @@ void EntityFactory::init_components(const std::shared_ptr(loop, create_test_activity()); - entity->add_component(activity); - } + // if (activity_ability) { + // init_activity(loop, owner_db_view, entity, activity_ability.value()); + // } + // else { + auto activity = std::make_shared(loop, create_test_activity()); + entity->add_component(activity); + // } } void EntityFactory::init_activity(const std::shared_ptr &loop, @@ -326,7 +331,7 @@ void EntityFactory::init_activity(const std::shared_ptradd_output(next_engine_node); + // activity_node->add_output(next_engine_node); } } diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index fe37142bfe..ec363f56ce 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -82,8 +82,14 @@ void Activity::advance(const std::shared_ptr &entity, } break; case activity::node_t::XOR_GATE: { auto node = std::static_pointer_cast(current_node); - auto condition = node->get_condition_func(); - auto next_id = condition(start_time, entity); + auto next_id = node->get_default_id(); + for (auto &condition : node->get_conditions()) { + auto condition_func = condition.second; + if (condition_func(start_time, entity)) { + next_id = condition.first; + break; + } + } current_node = node->next(next_id); } break; case activity::node_t::XOR_EVENT_GATE: { From b381dc576b2d15c2625e488d92e1839d3256863a Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 19 Dec 2023 00:50:16 +0100 Subject: [PATCH 12/44] gamestate: Refactor XorEventGate to use one event primer per node. --- .../gamestate/activity/xor_event_gate.cpp | 36 ++++++++ .../gamestate/activity/xor_event_gate.h | 91 ++++++++++++++++--- 2 files changed, 114 insertions(+), 13 deletions(-) diff --git a/libopenage/gamestate/activity/xor_event_gate.cpp b/libopenage/gamestate/activity/xor_event_gate.cpp index e76cb60558..b0d498fc18 100644 --- a/libopenage/gamestate/activity/xor_event_gate.cpp +++ b/libopenage/gamestate/activity/xor_event_gate.cpp @@ -17,10 +17,42 @@ XorEventGate::XorEventGate(node_id_t id, next_func{next_func} { } +XorEventGate::XorEventGate(node_id_t id, + node_label_t label) : + Node{id, label}, + primer_func{no_event}, + next_func{no_next}, + primers{} { +} + +XorEventGate::XorEventGate(node_id_t id, + node_label_t label, + const std::vector> &outputs, + const std::map &primers) : + Node{id, label, outputs}, + primer_func{no_event}, + next_func{no_next}, + primers{} { + if (primers.size() != outputs.size()) { + throw Error{MSG(err) << "XorEventGate " << this->str() << " has " << outputs.size() + << " outputs but " << primers.size() << " primers"}; + } + + for (const auto &[id, primer] : primers) { + this->primers.emplace(id, primer); + } +} + void XorEventGate::add_output(const std::shared_ptr &output) { this->outputs.emplace(output->get_id(), output); } +void XorEventGate::add_output(const std::shared_ptr &output, + const event_primer_t &primer) { + this->outputs.emplace(output->get_id(), output); + this->primers.emplace(output->get_id(), primer); +} + void XorEventGate::set_primer_func(event_primer_func_t primer_func) { this->primer_func = primer_func; } @@ -37,4 +69,8 @@ event_next_func_t XorEventGate::get_next_func() const { return this->next_func; } +const std::map &XorEventGate::get_primers() const { + return this->primers; +} + } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h index bce572376b..67b15603fe 100644 --- a/libopenage/gamestate/activity/xor_event_gate.h +++ b/libopenage/gamestate/activity/xor_event_gate.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include @@ -28,9 +29,27 @@ namespace activity { using event_store_t = std::vector>; -/* */ + +/** + * Create and register an event on the event loop. + * + * When the event is executed, the control flow continues on the branch + * associated with the event. + * + * @param time Time at which the primer function is executed. + * @param entity Game entity that the activity is assigned to. + * @param loop Event loop that events are registered on. + * @param state Game state. + * + * @return Event registered on the event loop. + */ +using event_primer_t = std::function(const time::time_t &, + const std::shared_ptr &, + const std::shared_ptr &, + const std::shared_ptr &)>; + /** - * Create and register an event on the event loop + * Create and register events on the event loop * * @param time Time at which the primer function is executed. * @param entity Game entity that the node is associated with. @@ -96,12 +115,35 @@ class XorEventGate : public Node { * @param outputs Output nodes (can be set later). * @param primer_func Function to create and register the event. * @param next_func Function to decide which node to visit after the event is handled. + */ + [[deprecated]] XorEventGate(node_id_t id, + node_label_t label = "Event", + const std::vector> &outputs = {}, + event_primer_func_t primer_func = no_event, + event_next_func_t next_func = no_next); + + /** + * Create a new exclusive event gateway. + * + * @param id Unique identifier for this node. + * @param label Human-readable label (optional). */ XorEventGate(node_id_t id, - node_label_t label = "Event", - const std::vector> &outputs = {}, - event_primer_func_t primer_func = no_event, - event_next_func_t next_func = no_next); + node_label_t label /* = "EventGateWay" */); + + /** + * Create a new exclusive event gateway. + * + * @param id Unique identifier for this node. + * @param label Human-readable label. + * @param outputs Output nodes. + * @param primers Event primers for each output node. + */ + XorEventGate(node_id_t id, + node_label_t label, + const std::vector> &outputs, + const std::map &primers); + virtual ~XorEventGate() = default; inline node_t get_type() const override { @@ -113,46 +155,69 @@ class XorEventGate : public Node { * * @param output Output node. */ - void add_output(const std::shared_ptr &output); + [[deprecated]] void add_output(const std::shared_ptr &output); + + /** + * Add an output node. + * + * @param output Output node. + * @param primer Creation function for the event associated with the output node. + */ + void add_output(const std::shared_ptr &output, + const event_primer_t &primer); /** * Set the function to create the event. * * @param primer_func Event creation function. */ - void set_primer_func(event_primer_func_t primer_func); + [[deprecated]] void set_primer_func(event_primer_func_t primer_func); /** * Set the function to decide which node to visit after the event is handled. * * @param next_func Next node function. */ - void set_next_func(event_next_func_t next_func); + [[deprecated]] void set_next_func(event_next_func_t next_func); /** * Get the function to create the event. * * @return Event creation function. */ - event_primer_func_t get_primer_func() const; + [[deprecated]] event_primer_func_t get_primer_func() const; /** * Get the function to decide which node to visit after the event is handled. * * @return Next node function. */ - event_next_func_t get_next_func() const; + [[deprecated]] event_next_func_t get_next_func() const; + + /** + * Get the output->event primer mappings. + * + * @return Event primer functions for each output node. + */ + const std::map &get_primers() const; private: /** * Creates the event when the node is visited. */ - event_primer_func_t primer_func; + [[deprecated]] event_primer_func_t primer_func; /** * Decide which node to visit after the event is handled. */ - event_next_func_t next_func; + [[deprecated]] event_next_func_t next_func; + + /** + * Maps output node IDs to event primer functions. + * + * Events are created and registered on the event loop when the node is visited. + */ + std::map primers; }; } // namespace activity From 7de149d2665bf8a570d567e4bfb80dd0f8c5dc13 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 19 Dec 2023 01:38:36 +0100 Subject: [PATCH 13/44] gamestate: Adjust activity demo for new event primers. --- libopenage/event/event.h | 6 ++- libopenage/gamestate/activity/tests.cpp | 69 +++++++++++++++---------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/libopenage/event/event.h b/libopenage/event/event.h index 72c943d807..34fcbc0cee 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -12,6 +12,8 @@ namespace openage::event { class EventEntity; +using event_hash_t = size_t; + /** * The actual one event that may be called - it is used to manage the event itself. * It does not need to be stored. @@ -36,7 +38,7 @@ class Event : public std::enable_shared_from_this { */ void reschedule(const time::time_t reference_time); - size_t hash() const { + event_hash_t hash() const { return this->myhash; } @@ -111,7 +113,7 @@ class Event : public std::enable_shared_from_this { time::time_t last_change_time = time::time_t::min_value(); /** Precalculated std::hash for the event */ - size_t myhash; + event_hash_t myhash; }; diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index b43cf9ed9f..f2191ebdb1 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -33,7 +33,8 @@ namespace openage::gamestate::tests { * @param current_node Node where the control flow starts from. * @return Node where the control flow should continue. */ -const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node); +const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node, + const std::optional ev_params = std::nullopt); /** @@ -57,11 +58,11 @@ class TestActivityManager : public event::EventEntity { return "TestActivityManager"; } - void run() { + void run(const std::optional ev_params = std::nullopt) { if (not current_node) { throw Error{ERR << "No current node given"}; } - this->current_node = activity_flow(this->current_node); + this->current_node = activity_flow(this->current_node, ev_params); } std::shared_ptr current_node; @@ -93,9 +94,9 @@ class TestActivityHandler : public event::OnceEventHandler { const std::shared_ptr &target, const std::shared_ptr & /* state */, const time::time_t & /* time */, - const param_map & /* params */) override { + const param_map ¶ms) override { auto mgr_target = std::dynamic_pointer_cast(target); - mgr_target->run(); + mgr_target->run(params); } time::time_t predict_invoke_time(const std::shared_ptr & /* target */, @@ -106,14 +107,27 @@ class TestActivityHandler : public event::OnceEventHandler { }; -const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node) { +const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node, + const std::optional ev_params) { + // events that are currently being listened for + // in the gamestate these are stored in the activity component + static std::vector> events; + auto current = current_node; if (current->get_type() == activity::node_t::XOR_EVENT_GATE) { - auto node = std::static_pointer_cast(current); - auto event_next = node->get_next_func(); - auto next_id = event_next(0, nullptr, nullptr, nullptr); - current = node->next(next_id); + log::log(INFO << "Continuing from event node"); + if (not ev_params.has_value()) { + throw Error{ERR << "XorEventGate: No event parameters given on continue"}; + } + + auto next_id = ev_params.value().get("next"); + current = current->next(next_id); + + // cancel all other events that the manager may have been waiting for + for (auto &event : events) { + event->cancel(0); + } } while (current->get_type() != activity::node_t::END) { @@ -150,10 +164,14 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current); - auto event_primer = node->get_primer_func(); - event_primer(0, nullptr, nullptr, nullptr); + auto event_primers = node->get_primers(); + for (auto &primer : event_primers) { + auto ev = primer.second(0, nullptr, nullptr, nullptr); + events.push_back(ev); + } // wait for event + log::log(INFO << "Waiting for event"); return current; } break; default: @@ -163,7 +181,7 @@ const std::shared_ptr activity_flow(const std::shared_ptrstr()); } - log::log(INFO << "Reached end note: " << current->str()); + log::log(INFO << "Reached end node: " << current->str()); return current; } @@ -232,25 +250,20 @@ void activity_demo() { xor_node->set_default_id(event_node->get_id()); // event node - event_node->add_output(task2); - event_node->set_primer_func([&](const time::time_t & /* time */, - const std::shared_ptr & /* entity */, - const std::shared_ptr & /* loop */, - const std::shared_ptr & /* state */) { + activity::event_primer_t primer = [&](const time::time_t & /* time */, + const std::shared_ptr & /* entity */, + const std::shared_ptr & /* loop */, + const std::shared_ptr & /* state */) { log::log(INFO << "Setting up event"); + event::EventHandler::param_map::map_t params{{"next", task2->get_id()}}; auto ev = loop->create_event("test.activity", mgr, state, - 0); - return activity::event_store_t{ev}; - }); - event_node->set_next_func([&task2](const time::time_t & /* time */, - const std::shared_ptr & /* entity */, - const std::shared_ptr & /* loop */, - const std::shared_ptr & /* state */) { - log::log(INFO << "Selecting next node (task node " << task2->get_id() << ")"); - return task2->get_id(); - }); + 0, + params); + return ev; + }; + event_node->add_output(task2, primer); // task 2 task2->add_output(end); From 611821172a977c20817b0840e4b811d3623a258a Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 19 Dec 2023 02:31:20 +0100 Subject: [PATCH 14/44] gamestate: Use new event priming functionality. --- libopenage/gamestate/activity/tests.cpp | 11 ++- .../gamestate/activity/xor_event_gate.cpp | 34 ---------- .../gamestate/activity/xor_event_gate.h | 66 ++---------------- libopenage/gamestate/entity_factory.cpp | 68 +++++++------------ .../gamestate/event/process_command.cpp | 8 +-- libopenage/gamestate/event/wait.cpp | 8 +-- libopenage/gamestate/manager.cpp | 5 +- libopenage/gamestate/manager.h | 5 +- libopenage/gamestate/system/activity.cpp | 27 +++++--- libopenage/gamestate/system/activity.h | 17 +++-- 10 files changed, 79 insertions(+), 170 deletions(-) diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index f2191ebdb1..64997c9c74 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -166,7 +166,11 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current); auto event_primers = node->get_primers(); for (auto &primer : event_primers) { - auto ev = primer.second(0, nullptr, nullptr, nullptr); + auto ev = primer.second(0, + nullptr, + nullptr, + nullptr, + primer.first); events.push_back(ev); } @@ -253,9 +257,10 @@ void activity_demo() { activity::event_primer_t primer = [&](const time::time_t & /* time */, const std::shared_ptr & /* entity */, const std::shared_ptr & /* loop */, - const std::shared_ptr & /* state */) { + const std::shared_ptr & /* state */, + size_t next_id) { log::log(INFO << "Setting up event"); - event::EventHandler::param_map::map_t params{{"next", task2->get_id()}}; + event::EventHandler::param_map::map_t params{{"next", next_id}}; auto ev = loop->create_event("test.activity", mgr, state, diff --git a/libopenage/gamestate/activity/xor_event_gate.cpp b/libopenage/gamestate/activity/xor_event_gate.cpp index b0d498fc18..f018cfd96a 100644 --- a/libopenage/gamestate/activity/xor_event_gate.cpp +++ b/libopenage/gamestate/activity/xor_event_gate.cpp @@ -7,21 +7,9 @@ namespace openage::gamestate::activity { -XorEventGate::XorEventGate(node_id_t id, - node_label_t label, - const std::vector> &outputs, - event_primer_func_t primer_func, - event_next_func_t next_func) : - Node{id, label, outputs}, - primer_func{primer_func}, - next_func{next_func} { -} - XorEventGate::XorEventGate(node_id_t id, node_label_t label) : Node{id, label}, - primer_func{no_event}, - next_func{no_next}, primers{} { } @@ -30,8 +18,6 @@ XorEventGate::XorEventGate(node_id_t id, const std::vector> &outputs, const std::map &primers) : Node{id, label, outputs}, - primer_func{no_event}, - next_func{no_next}, primers{} { if (primers.size() != outputs.size()) { throw Error{MSG(err) << "XorEventGate " << this->str() << " has " << outputs.size() @@ -43,32 +29,12 @@ XorEventGate::XorEventGate(node_id_t id, } } -void XorEventGate::add_output(const std::shared_ptr &output) { - this->outputs.emplace(output->get_id(), output); -} - void XorEventGate::add_output(const std::shared_ptr &output, const event_primer_t &primer) { this->outputs.emplace(output->get_id(), output); this->primers.emplace(output->get_id(), primer); } -void XorEventGate::set_primer_func(event_primer_func_t primer_func) { - this->primer_func = primer_func; -} - -void XorEventGate::set_next_func(event_next_func_t next_func) { - this->next_func = next_func; -} - -event_primer_func_t XorEventGate::get_primer_func() const { - return this->primer_func; -} - -event_next_func_t XorEventGate::get_next_func() const { - return this->next_func; -} - const std::map &XorEventGate::get_primers() const { return this->primers; } diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h index 67b15603fe..ac9334fe92 100644 --- a/libopenage/gamestate/activity/xor_event_gate.h +++ b/libopenage/gamestate/activity/xor_event_gate.h @@ -40,13 +40,15 @@ using event_store_t = std::vector>; * @param entity Game entity that the activity is assigned to. * @param loop Event loop that events are registered on. * @param state Game state. + * @param next_id ID of the next node to visit. This is passed as an event parameter. * * @return Event registered on the event loop. */ using event_primer_t = std::function(const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, - const std::shared_ptr &)>; + const std::shared_ptr &, + size_t next_id)>; /** * Create and register events on the event loop @@ -112,24 +114,9 @@ class XorEventGate : public Node { * * @param id Unique identifier for this node. * @param label Human-readable label (optional). - * @param outputs Output nodes (can be set later). - * @param primer_func Function to create and register the event. - * @param next_func Function to decide which node to visit after the event is handled. - */ - [[deprecated]] XorEventGate(node_id_t id, - node_label_t label = "Event", - const std::vector> &outputs = {}, - event_primer_func_t primer_func = no_event, - event_next_func_t next_func = no_next); - - /** - * Create a new exclusive event gateway. - * - * @param id Unique identifier for this node. - * @param label Human-readable label (optional). */ XorEventGate(node_id_t id, - node_label_t label /* = "EventGateWay" */); + node_label_t label = "EventGateWay"); /** * Create a new exclusive event gateway. @@ -154,46 +141,11 @@ class XorEventGate : public Node { * Add an output node. * * @param output Output node. - */ - [[deprecated]] void add_output(const std::shared_ptr &output); - - /** - * Add an output node. - * - * @param output Output node. * @param primer Creation function for the event associated with the output node. */ void add_output(const std::shared_ptr &output, const event_primer_t &primer); - /** - * Set the function to create the event. - * - * @param primer_func Event creation function. - */ - [[deprecated]] void set_primer_func(event_primer_func_t primer_func); - - /** - * Set the function to decide which node to visit after the event is handled. - * - * @param next_func Next node function. - */ - [[deprecated]] void set_next_func(event_next_func_t next_func); - - /** - * Get the function to create the event. - * - * @return Event creation function. - */ - [[deprecated]] event_primer_func_t get_primer_func() const; - - /** - * Get the function to decide which node to visit after the event is handled. - * - * @return Next node function. - */ - [[deprecated]] event_next_func_t get_next_func() const; - /** * Get the output->event primer mappings. * @@ -202,16 +154,6 @@ class XorEventGate : public Node { const std::map &get_primers() const; private: - /** - * Creates the event when the node is visited. - */ - [[deprecated]] event_primer_func_t primer_func; - - /** - * Decide which node to visit after the event is handled. - */ - [[deprecated]] event_next_func_t next_func; - /** * Maps output node IDs to event primer functions. * diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 4727e6cda5..60f0f3e4cc 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -95,65 +95,47 @@ std::shared_ptr create_test_activity() { condition_moveable->add_output(end, end_branch); condition_moveable->set_default_id(end->get_id()); - wait_for_command->add_output(move); - wait_for_command->set_primer_func([](const time::time_t & /* time */, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state) { + activity::event_primer_t process_primer = [](const time::time_t &, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id(); auto ev = loop->create_event("game.process_command", entity->get_manager(), state, // event is not executed until a command is available - std::numeric_limits::max()); + std::numeric_limits::max(), + params); auto entity_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); auto &queue = const_cast> &>(entity_queue->get_queue()); queue.add_dependent(ev); - return activity::event_store_t{ev}; - }); - wait_for_command->set_next_func([](const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &, - const std::shared_ptr &) { - auto entity_queue = std::dynamic_pointer_cast( - entity->get_component(component::component_t::COMMANDQUEUE)); - auto &queue = entity_queue->get_queue(); - - if (queue.empty(time)) { - throw Error{ERR << "Command queue is empty"}; - } - auto &com = queue.front(time); - if (com->get_type() == component::command::command_t::MOVE) { - return 5; // move->get_id(); - } - - throw Error{ERR << "Unknown command type"}; - }); + return ev; + }; + wait_for_command->add_output(move, process_primer); move->add_output(wait_for_move); move->set_system_id(system::system_id_t::MOVE_COMMAND); - wait_for_move->add_output(idle); - wait_for_move->add_output(condition_command); - wait_for_move->add_output(end); - wait_for_move->set_primer_func([](const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state) { + activity::event_primer_t primer = [](const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + event::EventHandler::param_map::map_t params{{"next", next_id}}; // idle->get_id(); auto ev = loop->create_event("game.wait", entity->get_manager(), state, - time); - - return activity::event_store_t{ev}; - }); - wait_for_move->set_next_func([&](const time::time_t &, - const std::shared_ptr &, - const std::shared_ptr &, - const std::shared_ptr &) { - return 1; // idle->get_id(); - }); + time, + params); + + return ev; + }; + wait_for_move->add_output(idle, primer); + // wait_for_move->add_output(condition_command, primer); + // wait_for_move->add_output(end, primer); return std::make_shared(0, start, "test"); } diff --git a/libopenage/gamestate/event/process_command.cpp b/libopenage/gamestate/event/process_command.cpp index 4783fe89c8..1f4045214d 100644 --- a/libopenage/gamestate/event/process_command.cpp +++ b/libopenage/gamestate/event/process_command.cpp @@ -19,14 +19,14 @@ void ProcessCommandHandler::invoke(openage::event::EventLoop & /* loop */, const std::shared_ptr &target, const std::shared_ptr & /* state */, const time::time_t &time, - const param_map & /* params */) { + const param_map ¶ms) { auto mgr = std::dynamic_pointer_cast(target); - mgr->run_activity_system(time); + mgr->run_activity_system(time, params); } time::time_t ProcessCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, - const std::shared_ptr & /* state */, - const time::time_t &at) { + const std::shared_ptr & /* state */, + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/wait.cpp b/libopenage/gamestate/event/wait.cpp index 2d067086fa..b1bcf256cf 100644 --- a/libopenage/gamestate/event/wait.cpp +++ b/libopenage/gamestate/event/wait.cpp @@ -19,14 +19,14 @@ void WaitHandler::invoke(openage::event::EventLoop & /* loop */, const std::shared_ptr &target, const std::shared_ptr & /* state */, const time::time_t &time, - const param_map & /* params */) { + const param_map ¶ms) { auto mgr = std::dynamic_pointer_cast(target); - mgr->run_activity_system(time); + mgr->run_activity_system(time, params); } time::time_t WaitHandler::predict_invoke_time(const std::shared_ptr & /* target */, - const std::shared_ptr & /* state */, - const time::time_t &at) { + const std::shared_ptr & /* state */, + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/manager.cpp b/libopenage/gamestate/manager.cpp index 9c8f6c1899..f00ccab22c 100644 --- a/libopenage/gamestate/manager.cpp +++ b/libopenage/gamestate/manager.cpp @@ -20,9 +20,10 @@ GameEntityManager::GameEntityManager(const std::shared_ptr &ev_params) { log::log(DBG << "Running activity system for entity " << this->game_entity->get_id()); - system::Activity::advance(this->game_entity, time, this->loop, this->state); + system::Activity::advance(time, this->game_entity, this->loop, this->state, ev_params); } size_t GameEntityManager::id() const { diff --git a/libopenage/gamestate/manager.h b/libopenage/gamestate/manager.h index 6131c94bd2..8d6976c806 100644 --- a/libopenage/gamestate/manager.h +++ b/libopenage/gamestate/manager.h @@ -4,9 +4,11 @@ #include #include +#include #include #include "event/evententity.h" +#include "event/eventhandler.h" #include "time/time.h" @@ -26,7 +28,8 @@ class GameEntityManager : public openage::event::EventEntity { const std::shared_ptr &game_entity); ~GameEntityManager() = default; - void run_activity_system(const time::time_t &time); + void run_activity_system(const time::time_t &time, + const std::optional &ev_params = std::nullopt); size_t id() const override; std::string idstr() const override; diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index ec363f56ce..f63488edd0 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -27,10 +27,11 @@ namespace openage::gamestate::system { -void Activity::advance(const std::shared_ptr &entity, - const time::time_t &start_time, +void Activity::advance(const time::time_t &start_time, + const std::shared_ptr &entity, const std::shared_ptr &loop, - const std::shared_ptr &state) { + const std::shared_ptr &state, + const std::optional &ev_params) { auto activity_component = std::dynamic_pointer_cast( entity->get_component(component::component_t::ACTIVITY)); auto current_node = activity_component->get_node(start_time); @@ -44,10 +45,12 @@ void Activity::advance(const std::shared_ptr &entity, if (current_node->get_type() == activity::node_t::XOR_EVENT_GATE) { // returning to a event gateway means that the event has been triggered // move to the next node here - auto node = std::static_pointer_cast(current_node); - auto event_next = node->get_next_func(); - auto next_id = event_next(start_time, entity, loop, state); - current_node = node->next(next_id); + if (not ev_params.has_value()) { + throw Error{ERR << "XorEventGate: No event parameters given on continue"}; + } + + auto next_id = ev_params.value().get("next"); + current_node = current_node->next(next_id); // cancel all other events that the manager may have been waiting for activity_component->cancel_events(start_time); @@ -94,9 +97,13 @@ void Activity::advance(const std::shared_ptr &entity, } break; case activity::node_t::XOR_EVENT_GATE: { auto node = std::static_pointer_cast(current_node); - auto event_primer = node->get_primer_func(); - auto evs = event_primer(start_time + event_wait_time, entity, loop, state); - for (auto &ev : evs) { + auto event_primers = node->get_primers(); + for (auto &primer : event_primers) { + auto ev = primer.second(start_time + event_wait_time, + entity, + loop, + state, + primer.first); activity_component->add_event(ev); } diff --git a/libopenage/gamestate/system/activity.h b/libopenage/gamestate/system/activity.h index d5283fceca..d6c10215fc 100644 --- a/libopenage/gamestate/system/activity.h +++ b/libopenage/gamestate/system/activity.h @@ -3,9 +3,11 @@ #pragma once #include +#include -#include "time/time.h" +#include "event/eventhandler.h" #include "gamestate/system/types.h" +#include "time/time.h" namespace openage { @@ -25,13 +27,14 @@ class Activity { /** * Advance in the activity flow graph of the game entity. * - * @param entity Game entity. * @param start_time Start time of change. + * @param entity Game entity. */ - static void advance(const std::shared_ptr &entity, - const time::time_t &start_time, + static void advance(const time::time_t &start_time, + const std::shared_ptr &entity, const std::shared_ptr &loop, - const std::shared_ptr &state); + const std::shared_ptr &state, + const std::optional &ev_params = std::nullopt); private: /** @@ -44,8 +47,8 @@ class Activity { * @return Runtime of the change in simulation time. */ static const time::time_t handle_subsystem(const std::shared_ptr &entity, - const time::time_t &start_time, - system_id_t system_id); + const time::time_t &start_time, + system_id_t system_id); }; } // namespace system From 26d04bb48e630b96a3c8c48c94f70005d65c8206 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 19 Dec 2023 22:59:40 +0100 Subject: [PATCH 15/44] gamestate: Remove unused functions. --- .../gamestate/activity/xor_event_gate.h | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/libopenage/gamestate/activity/xor_event_gate.h b/libopenage/gamestate/activity/xor_event_gate.h index ac9334fe92..6c4912d714 100644 --- a/libopenage/gamestate/activity/xor_event_gate.h +++ b/libopenage/gamestate/activity/xor_event_gate.h @@ -27,8 +27,6 @@ class GameState; namespace activity { -using event_store_t = std::vector>; - /** * Create and register an event on the event loop. @@ -50,59 +48,6 @@ using event_primer_t = std::function(cons const std::shared_ptr &, size_t next_id)>; -/** - * Create and register events on the event loop - * - * @param time Time at which the primer function is executed. - * @param entity Game entity that the node is associated with. - * @param loop Event loop that events are registered on. - * @param state Game state. - * - * @return List of events registered on the event loop. - */ -using event_primer_func_t = std::function &, - const std::shared_ptr &, - const std::shared_ptr &)>; - -/** - * Decide which node to visit after the event is handled. - * - * @param time Time at which the next function is executed. - * @param entity Game entity that the node is associated with. - * @param loop Event loop that events are registered on. - * @param state Game state. - * - * @return ID of the next node to visit. - */ -using event_next_func_t = std::function &, - const std::shared_ptr &, - const std::shared_ptr &)>; - - -/** - * Default primer function that throws an error. - */ -static const event_primer_func_t no_event = [](const time::time_t &, - const std::shared_ptr &, - const std::shared_ptr &, - const std::shared_ptr &) { - throw Error{ERR << "No event primer function registered."}; - return event_store_t{}; -}; - -/** - * Default next function that throws an error. - */ -static const event_next_func_t no_next = [](const time::time_t &, - const std::shared_ptr &, - const std::shared_ptr &, - const std::shared_ptr &) { - throw Error{ERR << "No event next function registered."}; - return 0; -}; - /** * Waits for an event to be executed before continuing the control flow. From 64dfb075d0d02e88833bcd6683fa9aa188ecd2c4 Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 19 Dec 2023 23:42:44 +0100 Subject: [PATCH 16/44] gamestate: Add breakout event to default actvity graph. --- libopenage/gamestate/entity_factory.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 60f0f3e4cc..bafa82b9f2 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -119,11 +119,11 @@ std::shared_ptr create_test_activity() { move->add_output(wait_for_move); move->set_system_id(system::system_id_t::MOVE_COMMAND); - activity::event_primer_t primer = [](const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id) { + activity::event_primer_t wait_primer = [](const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { event::EventHandler::param_map::map_t params{{"next", next_id}}; // idle->get_id(); auto ev = loop->create_event("game.wait", entity->get_manager(), @@ -133,9 +133,8 @@ std::shared_ptr create_test_activity() { return ev; }; - wait_for_move->add_output(idle, primer); - // wait_for_move->add_output(condition_command, primer); - // wait_for_move->add_output(end, primer); + wait_for_move->add_output(idle, wait_primer); + wait_for_move->add_output(move, process_primer); return std::make_shared(0, start, "test"); } From 7e9dbd100d6b4a631b8042bca47f0171c7b52142 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 20 Dec 2023 01:23:41 +0100 Subject: [PATCH 17/44] gamestate: Predefined activity event functions. --- libopenage/gamestate/entity_factory.cpp | 41 +++---------------- .../gamestate/event/process_command.cpp | 23 +++++++++++ libopenage/gamestate/event/process_command.h | 26 +++++++++++- libopenage/gamestate/event/wait.cpp | 20 +++++++++ libopenage/gamestate/event/wait.h | 27 +++++++++++- 5 files changed, 97 insertions(+), 40 deletions(-) diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index bafa82b9f2..e9b000d7ae 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -39,6 +39,8 @@ #include "time/time.h" #include "util/fixed_point.h" +#include "gamestate/event/process_command.h" +#include "gamestate/event/wait.h" namespace openage::gamestate { @@ -95,46 +97,13 @@ std::shared_ptr create_test_activity() { condition_moveable->add_output(end, end_branch); condition_moveable->set_default_id(end->get_id()); - activity::event_primer_t process_primer = [](const time::time_t &, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id) { - event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id(); - auto ev = loop->create_event("game.process_command", - entity->get_manager(), - state, - // event is not executed until a command is available - std::numeric_limits::max(), - params); - auto entity_queue = std::dynamic_pointer_cast( - entity->get_component(component::component_t::COMMANDQUEUE)); - auto &queue = const_cast> &>(entity_queue->get_queue()); - queue.add_dependent(ev); - - return ev; - }; - wait_for_command->add_output(move, process_primer); + wait_for_command->add_output(move, gamestate::event::primer_process_command); move->add_output(wait_for_move); move->set_system_id(system::system_id_t::MOVE_COMMAND); - activity::event_primer_t wait_primer = [](const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id) { - event::EventHandler::param_map::map_t params{{"next", next_id}}; // idle->get_id(); - auto ev = loop->create_event("game.wait", - entity->get_manager(), - state, - time, - params); - - return ev; - }; - wait_for_move->add_output(idle, wait_primer); - wait_for_move->add_output(move, process_primer); + wait_for_move->add_output(idle, gamestate::event::primer_wait); + wait_for_move->add_output(move, gamestate::event::primer_process_command); return std::make_shared(0, start, "test"); } diff --git a/libopenage/gamestate/event/process_command.cpp b/libopenage/gamestate/event/process_command.cpp index 1f4045214d..bf5497bc16 100644 --- a/libopenage/gamestate/event/process_command.cpp +++ b/libopenage/gamestate/event/process_command.cpp @@ -2,6 +2,10 @@ #include "process_command.h" +#include "event/event_loop.h" +#include "gamestate/component/internal/command_queue.h" +#include "gamestate/game_entity.h" +#include "gamestate/game_state.h" #include "gamestate/manager.h" @@ -30,5 +34,24 @@ time::time_t ProcessCommandHandler::predict_invoke_time(const std::shared_ptr primer_process_command(const time::time_t &, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id(); + auto ev = loop->create_event("game.process_command", + entity->get_manager(), + state, + // event is not executed until a command is available + std::numeric_limits::max(), + params); + auto entity_queue = std::dynamic_pointer_cast( + entity->get_component(component::component_t::COMMANDQUEUE)); + auto &queue = const_cast> &>(entity_queue->get_queue()); + queue.add_dependent(ev); + + return ev; +}; } // namespace openage::gamestate::event diff --git a/libopenage/gamestate/event/process_command.h b/libopenage/gamestate/event/process_command.h index 8b940dd616..fb59558fcb 100644 --- a/libopenage/gamestate/event/process_command.h +++ b/libopenage/gamestate/event/process_command.h @@ -17,8 +17,12 @@ class EventEntity; class State; } // namespace event -namespace gamestate::event { +namespace gamestate { +class GameEntity; +class GameState; + +namespace event { /** * Process a command from a game entity command queue. */ @@ -42,5 +46,23 @@ class ProcessCommandHandler : public openage::event::OnceEventHandler { }; -} // namespace gamestate::event +/** + * Primer for process command events in the activity system. + * + * @param time Current simulation time. + * @param entity Game entity. + * @param loop Event loop that the event is registered on. + * @param state Game state. + * @param next_id ID of the next node in the activity graph. + * + * @return Scheduled event. + */ +std::shared_ptr primer_process_command(const time::time_t &, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id); + +} // namespace event +} // namespace gamestate } // namespace openage diff --git a/libopenage/gamestate/event/wait.cpp b/libopenage/gamestate/event/wait.cpp index b1bcf256cf..955cdd7b91 100644 --- a/libopenage/gamestate/event/wait.cpp +++ b/libopenage/gamestate/event/wait.cpp @@ -2,6 +2,9 @@ #include "wait.h" +#include "event/event_loop.h" +#include "gamestate/game_entity.h" +#include "gamestate/game_state.h" #include "gamestate/manager.h" @@ -30,4 +33,21 @@ time::time_t WaitHandler::predict_invoke_time(const std::shared_ptr primer_wait(const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; + auto ev = loop->create_event("game.wait", + entity->get_manager(), + state, + time, + params); + + return ev; +}; + + } // namespace openage::gamestate::event diff --git a/libopenage/gamestate/event/wait.h b/libopenage/gamestate/event/wait.h index 2c0d9732f2..a4b4c20fcd 100644 --- a/libopenage/gamestate/event/wait.h +++ b/libopenage/gamestate/event/wait.h @@ -17,7 +17,11 @@ class EventEntity; class State; } // namespace event -namespace gamestate::event { +namespace gamestate { +class GameEntity; +class GameState; + +namespace event { /** * Waits until the event is handled and calls back the entity manager. @@ -40,5 +44,24 @@ class WaitHandler : public openage::event::OnceEventHandler { const std::shared_ptr &state, const time::time_t &at) override; }; -} // namespace gamestate::event + +/** + * Primer for wait events in the activity system. + * + * @param time Wait until this time. If the time is in the past, the event is executed immediately. + * @param entity Game entity. + * @param loop Event loop that the event is registered on. + * @param state Game state. + * @param next_id ID of the next node in the activity graph. + * + * @return Scheduled event. + */ +std::shared_ptr primer_wait(const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id); + +} // namespace event +} // namespace gamestate } // namespace openage From 3027ce9646b0e8f8bca58fe8df3b2e77d32711b3 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 20 Dec 2023 02:33:36 +0100 Subject: [PATCH 18/44] gamestate: Get event primer from API definition. --- libopenage/gamestate/api/activity.cpp | 10 +++++++++ libopenage/gamestate/api/activity.h | 29 +++++++++++++++++++++++++- libopenage/gamestate/api/definitions.h | 12 +++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index 19e67e65d6..34d7896e8b 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -62,4 +62,14 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { } } +bool APIActivityEvent::is_event(const nyan::Object &obj) { + nyan::fqon_t immediate_parent = obj.get_parents()[0]; + return immediate_parent == "engine.util.activity.event.Event"; +} + +activity::event_primer_t APIActivityEvent::get_primer(const nyan::Object &event) { + nyan::fqon_t immediate_parent = event.get_parents()[0]; + return ACTIVITY_EVENT_PRIMERS.get(immediate_parent); +} + } // namespace openage::gamestate::api diff --git a/libopenage/gamestate/api/activity.h b/libopenage/gamestate/api/activity.h index 28e7c88dbe..cc365da449 100644 --- a/libopenage/gamestate/api/activity.h +++ b/libopenage/gamestate/api/activity.h @@ -7,6 +7,7 @@ #include #include "gamestate/activity/types.h" +#include "gamestate/activity/xor_event_gate.h" namespace openage::gamestate { @@ -37,7 +38,9 @@ class APIActivity { static nyan::Object get_start(const nyan::Object &activity); }; - +/** + * Helper class for creating Activity node objects from the nyan API. + */ class APIActivityNode { public: /** @@ -71,6 +74,30 @@ class APIActivityNode { static std::vector get_next(const nyan::Object &node); }; +/** + * Helper class for creating Activity event objects from the nyan API. + */ +class APIActivityEvent { +public: + /** + * Check if a nyan object is an event (type == \p engine.util.activity.event.Event). + * + * @param obj nyan object. + * + * @return true if the object is an event, else false. + */ + static bool is_event(const nyan::Object &obj); + + /** + * Get the primer function for an event type. + * + * @param event nyan object. + * + * @return Event primer function. + */ + static activity::event_primer_t get_primer(const nyan::Object &event); +}; + } // namespace api } // namespace openage::gamestate diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index d97372962f..f9caacdbc2 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -11,6 +11,10 @@ #include "gamestate/activity/types.h" #include "gamestate/api/types.h" +#include "gamestate/activity/xor_event_gate.h" +#include "gamestate/event/process_command.h" +#include "gamestate/event/wait.h" + namespace openage::gamestate::api { @@ -59,6 +63,14 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( + std::pair("engine.util.activity.event.type.Command", + std::function(gamestate::event::primer_process_command)), + std::pair("engine.util.activity.event.type.Wait", + std::function(gamestate::event::primer_wait)), + std::pair("engine.util.activity.event.type.WaitAbility", + std::function(gamestate::event::primer_wait))); + /** * Maps internal patch property types to nyan API values. */ From ca4f15e4271864254e28e67144931295df867b44 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 20 Dec 2023 02:34:26 +0100 Subject: [PATCH 19/44] gamestate: Create connections between nodes in activity graph. --- libopenage/gamestate/entity_factory.cpp | 34 ++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index e9b000d7ae..14f833eef7 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -252,6 +252,7 @@ void EntityFactory::init_activity(const std::shared_ptr(node_id); + // TODO: Set system ID break; case activity::node_t::XOR_GATE: node_id_map[node_id] = std::make_shared(node_id); @@ -272,16 +273,41 @@ void EntityFactory::init_activity(const std::shared_ptrget_object(node.first); - auto activity_node = node_id_map[node.second]; + for (const auto ¤t_node : visited) { + auto nyan_node = owner_db_view->get_object(current_node.first); + auto activity_node = node_id_map[current_node.second]; auto next_nodes = api::APIActivityNode::get_next(nyan_node); for (const auto &next_node : next_nodes) { auto next_node_id = visited[next_node.get_name()]; auto next_engine_node = node_id_map[next_node_id]; - // activity_node->add_output(next_engine_node); + switch (activity_node->get_type()) { + case activity::node_t::END: + break; + case activity::node_t::START: { + auto start = std::static_pointer_cast(activity_node); + start->add_output(next_engine_node); + break; + } + case activity::node_t::TASK_SYSTEM: { + auto task_system = std::static_pointer_cast(activity_node); + task_system->add_output(next_engine_node); + break; + } + case activity::node_t::XOR_GATE: { + auto xor_gate = std::static_pointer_cast(activity_node); + // TODO: Add conditions + break; + } + case activity::node_t::XOR_EVENT_GATE: { + auto xor_event_gate = std::static_pointer_cast(activity_node); + xor_event_gate->add_output(next_engine_node, api::APIActivityEvent::get_primer(next_node)); + break; + } + default: + throw Error{ERR << "Unknown activity node type"}; + } } } From 24d83c02da142a7f09279e0b69d07c3b8fd15b86 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 23 Dec 2023 03:53:40 +0100 Subject: [PATCH 20/44] gamestate: Set default node separately from other outputs. --- libopenage/gamestate/activity/tests.cpp | 4 +-- libopenage/gamestate/activity/xor_gate.cpp | 30 ++++++++-------------- libopenage/gamestate/activity/xor_gate.h | 22 ++++++++-------- libopenage/gamestate/entity_factory.cpp | 8 +----- libopenage/gamestate/system/activity.cpp | 2 +- 5 files changed, 25 insertions(+), 41 deletions(-) diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index 64997c9c74..1ddad0e7e8 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -152,7 +152,7 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current); - auto next_id = node->get_default_id(); + auto next_id = node->get_default()->get_id(); for (auto &condition : node->get_conditions()) { auto condition_func = condition.second; if (condition_func(0, nullptr)) { @@ -251,7 +251,7 @@ void activity_demo() { return true; }; xor_node->add_output(event_node, branch_event); - xor_node->set_default_id(event_node->get_id()); + xor_node->set_default(event_node); // event node activity::event_primer_t primer = [&](const time::time_t & /* time */, diff --git a/libopenage/gamestate/activity/xor_gate.cpp b/libopenage/gamestate/activity/xor_gate.cpp index 6069968ba1..5d37908f8b 100644 --- a/libopenage/gamestate/activity/xor_gate.cpp +++ b/libopenage/gamestate/activity/xor_gate.cpp @@ -11,17 +11,17 @@ XorGate::XorGate(node_id_t id, node_label_t label) : Node{id, label, {}}, conditions{}, - default_id{std::nullopt} { + default_node{nullptr} { } XorGate::XorGate(node_id_t id, node_label_t label, const std::vector> &outputs, const std::vector &conditions, - const node_id_t default_id) : + const std::shared_ptr &default_node) : Node{id, label, outputs}, conditions{}, - default_id{std::nullopt} { + default_node{default_node} { if (conditions.size() != outputs.size()) [[unlikely]] { throw Error{MSG(err) << "XorGate " << this->str() << " has " << outputs.size() << " outputs but " << conditions.size() << " conditions"}; @@ -30,39 +30,29 @@ XorGate::XorGate(node_id_t id, for (size_t i = 0; i < conditions.size(); ++i) { this->conditions.emplace(outputs[i]->get_id(), conditions[i]); } - - this->set_default_id(default_id); } void XorGate::add_output(const std::shared_ptr &output, const condition_t condition_func) { this->outputs.emplace(output->get_id(), output); this->conditions.emplace(output->get_id(), condition_func); - - // If this is the first output, set it as the default. - if (not this->default_id) [[unlikely]] { - this->default_id = output->get_id(); - } } const std::map &XorGate::get_conditions() const { return this->conditions; } -node_id_t XorGate::get_default_id() const { - if (not this->default_id) [[unlikely]] { - throw Error{MSG(err) << "XorGate " << this->str() << " has no default output"}; - } - - return this->default_id.value(); +const std::shared_ptr &XorGate::get_default() const { + return this->default_node; } -void XorGate::set_default_id(node_id_t id) { - if (not this->outputs.contains(id)) [[unlikely]] { - throw Error{MSG(err) << "XorGate " << this->str() << " has no output with id " << id}; +void XorGate::set_default(const std::shared_ptr &node) { + if (this->default_node != nullptr) { + throw Error{MSG(err) << "XorGate " << this->str() << " already has a default node"}; } - this->default_id = id; + this->outputs.emplace(node->get_id(), node); + this->default_node = node; } } // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/xor_gate.h b/libopenage/gamestate/activity/xor_gate.h index 34870472cf..0e85a4bf21 100644 --- a/libopenage/gamestate/activity/xor_gate.h +++ b/libopenage/gamestate/activity/xor_gate.h @@ -54,13 +54,13 @@ class XorGate : public Node { * @param label Label of the node. * @param outputs Output nodes. * @param conditions Conditions for each output node. - * @param default_id Default output node ID. + * @param default_node Default output node. Chosen if no condition is true. */ XorGate(node_id_t id, node_label_t label, const std::vector> &outputs, const std::vector &conditions, - const node_id_t default_id); + const std::shared_ptr &default_node); virtual ~XorGate() = default; @@ -86,20 +86,20 @@ class XorGate : public Node { const std::map &get_conditions() const; /** - * Get the ID of the default output node. + * Get the default output node. * - * @return Default output node ID. + * @return Default output node. */ - node_id_t get_default_id() const; + const std::shared_ptr &get_default() const; /** - * Set the ID of the default output node. + * Set the the default output node. * - * The ID must be a valid node ID of one of the output nodes. + * This node is chosen if no condition is true. * - * @param id Default output node ID. + * @param node Default output node. */ - void set_default_id(node_id_t id); + void set_default(const std::shared_ptr &node); private: /** @@ -110,9 +110,9 @@ class XorGate : public Node { std::map conditions; /** - * Default output node ID. Chosen if no condition is true. + * Default output node. Chosen if no condition is true. */ - std::optional default_id; + std::shared_ptr default_node; }; } // namespace activity diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 14f833eef7..09600c8b7a 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -89,13 +89,7 @@ std::shared_ptr create_test_activity() { condition_moveable->add_output(wait_for_command, wait_branch); // end branch - activity::condition_t end_branch = [&](const time::time_t & /* time */, - const std::shared_ptr & /* entity */) { - // no checks, this is the default branch - return true; - }; - condition_moveable->add_output(end, end_branch); - condition_moveable->set_default_id(end->get_id()); + condition_moveable->set_default(end); wait_for_command->add_output(move, gamestate::event::primer_process_command); diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index f63488edd0..49371fba30 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -85,7 +85,7 @@ void Activity::advance(const time::time_t &start_time, } break; case activity::node_t::XOR_GATE: { auto node = std::static_pointer_cast(current_node); - auto next_id = node->get_default_id(); + auto next_id = node->get_default()->get_id(); for (auto &condition : node->get_conditions()) { auto condition_func = condition.second; if (condition_func(start_time, entity)) { From a07f7d2269360f93fd7b476b6971764bd32bde61 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 23 Dec 2023 23:00:18 +0100 Subject: [PATCH 21/44] refactor: Move event primers into activity subfolders. --- libopenage/gamestate/activity/CMakeLists.txt | 2 + .../gamestate/activity/event/CMakeLists.txt | 4 ++ .../activity/event/command_in_queue.cpp | 35 +++++++++++++++ .../activity/event/command_in_queue.h | 42 ++++++++++++++++++ libopenage/gamestate/activity/event/wait.cpp | 29 ++++++++++++ libopenage/gamestate/activity/event/wait.h | 44 +++++++++++++++++++ libopenage/gamestate/api/definitions.h | 13 +++--- libopenage/gamestate/entity_factory.cpp | 11 +++-- .../gamestate/event/process_command.cpp | 24 ---------- libopenage/gamestate/event/process_command.h | 17 ------- libopenage/gamestate/event/wait.cpp | 19 -------- libopenage/gamestate/event/wait.h | 17 ------- 12 files changed, 167 insertions(+), 90 deletions(-) create mode 100644 libopenage/gamestate/activity/event/CMakeLists.txt create mode 100644 libopenage/gamestate/activity/event/command_in_queue.cpp create mode 100644 libopenage/gamestate/activity/event/command_in_queue.h create mode 100644 libopenage/gamestate/activity/event/wait.cpp create mode 100644 libopenage/gamestate/activity/event/wait.h diff --git a/libopenage/gamestate/activity/CMakeLists.txt b/libopenage/gamestate/activity/CMakeLists.txt index b005c32001..8d07a244a2 100644 --- a/libopenage/gamestate/activity/CMakeLists.txt +++ b/libopenage/gamestate/activity/CMakeLists.txt @@ -10,3 +10,5 @@ add_sources(libopenage xor_event_gate.cpp xor_gate.cpp ) + +add_subdirectory("event") diff --git a/libopenage/gamestate/activity/event/CMakeLists.txt b/libopenage/gamestate/activity/event/CMakeLists.txt new file mode 100644 index 0000000000..863fa6d28a --- /dev/null +++ b/libopenage/gamestate/activity/event/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources(libopenage + command_in_queue.cpp + wait.cpp +) diff --git a/libopenage/gamestate/activity/event/command_in_queue.cpp b/libopenage/gamestate/activity/event/command_in_queue.cpp new file mode 100644 index 0000000000..51a82750d0 --- /dev/null +++ b/libopenage/gamestate/activity/event/command_in_queue.cpp @@ -0,0 +1,35 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "command_in_queue.h" + +#include "event/event_loop.h" +#include "event/evententity.h" +#include "gamestate/component/internal/command_queue.h" +#include "gamestate/game_entity.h" +#include "gamestate/game_state.h" +#include "gamestate/manager.h" + + +namespace openage::gamestate::activity { + +std::shared_ptr primer_command_in_queue(const time::time_t &, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id(); + auto ev = loop->create_event("game.process_command", + entity->get_manager(), + state, + // event is not executed until a command is available + std::numeric_limits::max(), + params); + auto entity_queue = std::dynamic_pointer_cast( + entity->get_component(component::component_t::COMMANDQUEUE)); + auto &queue = const_cast> &>(entity_queue->get_queue()); + queue.add_dependent(ev); + + return ev; +}; + +} // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/event/command_in_queue.h b/libopenage/gamestate/activity/event/command_in_queue.h new file mode 100644 index 0000000000..ebb71c166d --- /dev/null +++ b/libopenage/gamestate/activity/event/command_in_queue.h @@ -0,0 +1,42 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "time/time.h" + + +namespace openage { +namespace event { +class Event; +class EventLoop; +} // namespace event + +namespace gamestate { +class GameEntity; +class GameState; + +namespace activity { + +/** + * Primer for command in queue events in the activity system. + * + * @param time Current simulation time. + * @param entity Game entity. + * @param loop Event loop that the event is registered on. + * @param state Game state. + * @param next_id ID of the next node in the activity graph. + * + * @return Scheduled event. + */ +std::shared_ptr primer_command_in_queue(const time::time_t &, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id); + +} // namespace activity +} // namespace gamestate +} // namespace openage diff --git a/libopenage/gamestate/activity/event/wait.cpp b/libopenage/gamestate/activity/event/wait.cpp new file mode 100644 index 0000000000..6bfd03f632 --- /dev/null +++ b/libopenage/gamestate/activity/event/wait.cpp @@ -0,0 +1,29 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "wait.h" + +#include "event/event_loop.h" +#include "event/evententity.h" +#include "gamestate/game_entity.h" +#include "gamestate/game_state.h" +#include "gamestate/manager.h" + + +namespace openage::gamestate::activity { + +std::shared_ptr primer_wait(const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id) { + openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; + auto ev = loop->create_event("game.wait", + entity->get_manager(), + state, + time, + params); + + return ev; +}; + +} // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/event/wait.h b/libopenage/gamestate/activity/event/wait.h new file mode 100644 index 0000000000..c33be2a5be --- /dev/null +++ b/libopenage/gamestate/activity/event/wait.h @@ -0,0 +1,44 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "time/time.h" + + +namespace openage { +namespace event { +class Event; +class EventLoop; +} // namespace event + +namespace gamestate { +class GameEntity; +class GameState; + +namespace activity { + + +/** + * Primer for wait events in the activity system. + * + * @param time Wait until this time. If the time is in the past, the event is executed immediately. + * @param entity Game entity. + * @param loop Event loop that the event is registered on. + * @param state Game state. + * @param next_id ID of the next node in the activity graph. + * + * @return Scheduled event. + */ +std::shared_ptr primer_wait(const time::time_t &time, + const std::shared_ptr &entity, + const std::shared_ptr &loop, + const std::shared_ptr &state, + size_t next_id); + + +} // namespace activity +} // namespace gamestate +} // namespace openage diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index f9caacdbc2..89f9ed17a7 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -8,12 +8,11 @@ #include #include "datastructure/constexpr_map.h" +#include "gamestate/activity/event/command_in_queue.h" +#include "gamestate/activity/event/wait.h" #include "gamestate/activity/types.h" -#include "gamestate/api/types.h" - #include "gamestate/activity/xor_event_gate.h" -#include "gamestate/event/process_command.h" -#include "gamestate/event/wait.h" +#include "gamestate/api/types.h" namespace openage::gamestate::api { @@ -65,11 +64,11 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( std::pair("engine.util.activity.event.type.Command", - std::function(gamestate::event::primer_process_command)), + std::function(gamestate::activity::primer_command_in_queue)), std::pair("engine.util.activity.event.type.Wait", - std::function(gamestate::event::primer_wait)), + std::function(gamestate::activity::primer_wait)), std::pair("engine.util.activity.event.type.WaitAbility", - std::function(gamestate::event::primer_wait))); + std::function(gamestate::activity::primer_wait))); /** * Maps internal patch property types to nyan API values. diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 09600c8b7a..f08bef7e6c 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -15,6 +15,8 @@ #include "event/event_loop.h" #include "gamestate/activity/activity.h" #include "gamestate/activity/end_node.h" +#include "gamestate/activity/event/command_in_queue.h" +#include "gamestate/activity/event/wait.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_system_node.h" #include "gamestate/activity/xor_event_gate.h" @@ -39,9 +41,6 @@ #include "time/time.h" #include "util/fixed_point.h" -#include "gamestate/event/process_command.h" -#include "gamestate/event/wait.h" - namespace openage::gamestate { /** @@ -91,13 +90,13 @@ std::shared_ptr create_test_activity() { // end branch condition_moveable->set_default(end); - wait_for_command->add_output(move, gamestate::event::primer_process_command); + wait_for_command->add_output(move, gamestate::activity::primer_command_in_queue); move->add_output(wait_for_move); move->set_system_id(system::system_id_t::MOVE_COMMAND); - wait_for_move->add_output(idle, gamestate::event::primer_wait); - wait_for_move->add_output(move, gamestate::event::primer_process_command); + wait_for_move->add_output(idle, gamestate::activity::primer_wait); + wait_for_move->add_output(move, gamestate::activity::primer_command_in_queue); return std::make_shared(0, start, "test"); } diff --git a/libopenage/gamestate/event/process_command.cpp b/libopenage/gamestate/event/process_command.cpp index bf5497bc16..d00762d8c9 100644 --- a/libopenage/gamestate/event/process_command.cpp +++ b/libopenage/gamestate/event/process_command.cpp @@ -2,10 +2,6 @@ #include "process_command.h" -#include "event/event_loop.h" -#include "gamestate/component/internal/command_queue.h" -#include "gamestate/game_entity.h" -#include "gamestate/game_state.h" #include "gamestate/manager.h" @@ -34,24 +30,4 @@ time::time_t ProcessCommandHandler::predict_invoke_time(const std::shared_ptr primer_process_command(const time::time_t &, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id) { - openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; // move->get_id(); - auto ev = loop->create_event("game.process_command", - entity->get_manager(), - state, - // event is not executed until a command is available - std::numeric_limits::max(), - params); - auto entity_queue = std::dynamic_pointer_cast( - entity->get_component(component::component_t::COMMANDQUEUE)); - auto &queue = const_cast> &>(entity_queue->get_queue()); - queue.add_dependent(ev); - - return ev; -}; - } // namespace openage::gamestate::event diff --git a/libopenage/gamestate/event/process_command.h b/libopenage/gamestate/event/process_command.h index fb59558fcb..db4fb18157 100644 --- a/libopenage/gamestate/event/process_command.h +++ b/libopenage/gamestate/event/process_command.h @@ -46,23 +46,6 @@ class ProcessCommandHandler : public openage::event::OnceEventHandler { }; -/** - * Primer for process command events in the activity system. - * - * @param time Current simulation time. - * @param entity Game entity. - * @param loop Event loop that the event is registered on. - * @param state Game state. - * @param next_id ID of the next node in the activity graph. - * - * @return Scheduled event. - */ -std::shared_ptr primer_process_command(const time::time_t &, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id); - } // namespace event } // namespace gamestate } // namespace openage diff --git a/libopenage/gamestate/event/wait.cpp b/libopenage/gamestate/event/wait.cpp index 955cdd7b91..eb2e793334 100644 --- a/libopenage/gamestate/event/wait.cpp +++ b/libopenage/gamestate/event/wait.cpp @@ -2,9 +2,6 @@ #include "wait.h" -#include "event/event_loop.h" -#include "gamestate/game_entity.h" -#include "gamestate/game_state.h" #include "gamestate/manager.h" @@ -34,20 +31,4 @@ time::time_t WaitHandler::predict_invoke_time(const std::shared_ptr primer_wait(const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id) { - openage::event::EventHandler::param_map::map_t params{{"next", next_id}}; - auto ev = loop->create_event("game.wait", - entity->get_manager(), - state, - time, - params); - - return ev; -}; - - } // namespace openage::gamestate::event diff --git a/libopenage/gamestate/event/wait.h b/libopenage/gamestate/event/wait.h index a4b4c20fcd..1c74015984 100644 --- a/libopenage/gamestate/event/wait.h +++ b/libopenage/gamestate/event/wait.h @@ -45,23 +45,6 @@ class WaitHandler : public openage::event::OnceEventHandler { const time::time_t &at) override; }; -/** - * Primer for wait events in the activity system. - * - * @param time Wait until this time. If the time is in the past, the event is executed immediately. - * @param entity Game entity. - * @param loop Event loop that the event is registered on. - * @param state Game state. - * @param next_id ID of the next node in the activity graph. - * - * @return Scheduled event. - */ -std::shared_ptr primer_wait(const time::time_t &time, - const std::shared_ptr &entity, - const std::shared_ptr &loop, - const std::shared_ptr &state, - size_t next_id); - } // namespace event } // namespace gamestate } // namespace openage From 7c12205132727aec7fdec9451fd53111e1b1a320 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 23 Dec 2023 23:36:16 +0100 Subject: [PATCH 22/44] gamestate: Add built-in condition functions to activity system. --- libopenage/gamestate/activity/CMakeLists.txt | 1 + .../activity/condition/CMakeLists.txt | 4 ++ .../activity/condition/command_in_queue.cpp | 19 +++++++++ .../activity/condition/command_in_queue.h | 28 +++++++++++++ .../activity/condition/next_command.cpp | 37 ++++++++++++++++++ .../activity/condition/next_command.h | 39 +++++++++++++++++++ libopenage/gamestate/api/definitions.h | 13 +++++++ 7 files changed, 141 insertions(+) create mode 100644 libopenage/gamestate/activity/condition/CMakeLists.txt create mode 100644 libopenage/gamestate/activity/condition/command_in_queue.cpp create mode 100644 libopenage/gamestate/activity/condition/command_in_queue.h create mode 100644 libopenage/gamestate/activity/condition/next_command.cpp create mode 100644 libopenage/gamestate/activity/condition/next_command.h diff --git a/libopenage/gamestate/activity/CMakeLists.txt b/libopenage/gamestate/activity/CMakeLists.txt index 8d07a244a2..78a78e7ab0 100644 --- a/libopenage/gamestate/activity/CMakeLists.txt +++ b/libopenage/gamestate/activity/CMakeLists.txt @@ -12,3 +12,4 @@ add_sources(libopenage ) add_subdirectory("event") +add_subdirectory("condition") diff --git a/libopenage/gamestate/activity/condition/CMakeLists.txt b/libopenage/gamestate/activity/condition/CMakeLists.txt new file mode 100644 index 0000000000..aabd159b0c --- /dev/null +++ b/libopenage/gamestate/activity/condition/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources(libopenage + command_in_queue.cpp + next_command.cpp +) diff --git a/libopenage/gamestate/activity/condition/command_in_queue.cpp b/libopenage/gamestate/activity/condition/command_in_queue.cpp new file mode 100644 index 0000000000..7e300701f1 --- /dev/null +++ b/libopenage/gamestate/activity/condition/command_in_queue.cpp @@ -0,0 +1,19 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "next_command.h" + +#include "gamestate/component/internal/command_queue.h" +#include "gamestate/game_entity.h" + + +namespace openage::gamestate::activity { + +bool command_in_queue(const time::time_t &time, + const std::shared_ptr &entity) { + auto command_queue = std::dynamic_pointer_cast( + entity->get_component(component::component_t::COMMANDQUEUE)); + + return not command_queue->get_queue().empty(time); +} + +} // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/condition/command_in_queue.h b/libopenage/gamestate/activity/condition/command_in_queue.h new file mode 100644 index 0000000000..67c7a794cc --- /dev/null +++ b/libopenage/gamestate/activity/condition/command_in_queue.h @@ -0,0 +1,28 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "time/time.h" + + +namespace openage::gamestate { +class GameEntity; + +namespace activity { + +/** + * Condition for command in queue check in the activity system. + * + * @param time Time when the condition is checked. + * @param entity Game entity. + * + * @return true if there is at least one command in the entity's command queue, false otherwise. + */ +bool command_in_queue(const time::time_t &time, + const std::shared_ptr &entity); + +} // namespace activity +} // namespace openage::gamestate diff --git a/libopenage/gamestate/activity/condition/next_command.cpp b/libopenage/gamestate/activity/condition/next_command.cpp new file mode 100644 index 0000000000..c0b619a783 --- /dev/null +++ b/libopenage/gamestate/activity/condition/next_command.cpp @@ -0,0 +1,37 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#include "next_command.h" + +#include "gamestate/component/internal/command_queue.h" +#include "gamestate/game_entity.h" + + +namespace openage::gamestate::activity { + +bool next_command_idle(const time::time_t &time, + const std::shared_ptr &entity) { + auto command_queue = std::dynamic_pointer_cast( + entity->get_component(component::component_t::COMMANDQUEUE)); + + if (command_queue->get_queue().empty(time)) { + return false; + } + + auto command = command_queue->get_queue().front(time); + return command->get_type() == component::command::command_t::MOVE; +} + +bool next_command_move(const time::time_t &time, + const std::shared_ptr &entity) { + auto command_queue = std::dynamic_pointer_cast( + entity->get_component(component::component_t::COMMANDQUEUE)); + + if (command_queue->get_queue().empty(time)) { + return false; + } + + auto command = command_queue->get_queue().front(time); + return command->get_type() == component::command::command_t::MOVE; +} + +} // namespace openage::gamestate::activity diff --git a/libopenage/gamestate/activity/condition/next_command.h b/libopenage/gamestate/activity/condition/next_command.h new file mode 100644 index 0000000000..046a18cec6 --- /dev/null +++ b/libopenage/gamestate/activity/condition/next_command.h @@ -0,0 +1,39 @@ +// Copyright 2023-2023 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "time/time.h" + + +namespace openage::gamestate { +class GameEntity; + +namespace activity { + +/** + * Condition for next command check in the activity system. + * + * @param time Time when the condition is checked. + * @param entity Game entity. + * + * @return true if the entity has a idle command next in the queue, false otherwise. + */ +bool next_command_idle(const time::time_t &time, + const std::shared_ptr &entity); + +/** + * Condition for next command check in the activity system. + * + * @param time Time when the condition is checked. + * @param entity Game entity. + * + * @return true if the entity has a move command next in the queue, false otherwise. + */ +bool next_command_move(const time::time_t &time, + const std::shared_ptr &entity); + +} // namespace activity +} // namespace openage::gamestate diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index 89f9ed17a7..cceaa277c3 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -8,10 +8,13 @@ #include #include "datastructure/constexpr_map.h" +#include "gamestate/activity/condition/command_in_queue.h" +#include "gamestate/activity/condition/next_command.h" #include "gamestate/activity/event/command_in_queue.h" #include "gamestate/activity/event/wait.h" #include "gamestate/activity/types.h" #include "gamestate/activity/xor_event_gate.h" +#include "gamestate/activity/xor_gate.h" #include "gamestate/api/types.h" @@ -62,6 +65,16 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( + std::pair("engine.util.activity.condition.type.CommandInQueue", + std::function(gamestate::activity::command_in_queue)), + // TODO: API object assignment is inconsistent here + // Ideally all conditions should be an activity condition type + std::pair("engine.util.command.type.Idle", + std::function(gamestate::activity::next_command_idle)), + std::pair("engine.util.command.type.Move", + std::function(gamestate::activity::next_command_move))); + static const auto ACTIVITY_EVENT_PRIMERS = datastructure::create_const_map( std::pair("engine.util.activity.event.type.Command", std::function(gamestate::activity::primer_command_in_queue)), From 831a4f89a16d389e48f0f82f3ec24c60f983306e Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 23 Dec 2023 23:59:34 +0100 Subject: [PATCH 23/44] gamestate: Use built-in condition for example activity. --- libopenage/gamestate/entity_factory.cpp | 43 +++++++++++++------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index f08bef7e6c..9376f775a3 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -14,6 +14,7 @@ #include "curve/queue.h" #include "event/event_loop.h" #include "gamestate/activity/activity.h" +#include "gamestate/activity/condition/command_in_queue.h" #include "gamestate/activity/end_node.h" #include "gamestate/activity/event/command_in_queue.h" #include "gamestate/activity/event/wait.h" @@ -49,19 +50,9 @@ namespace openage::gamestate { * The activity is as follows: * |------------------------------------------------------| * | v - * Start -> Idle -> Condition -> Wait for command -> Move -> Wait for move -> End - * ^ | - * |------------------------------------------------------| - * - * TODO: Should be: - * |----------------------------------------------------------------------| - * | v - * Start -> Idle -> -> Condition -> Wait for command <-> Condition -> Move -> Wait or command -> End - * ^ |^ | - * |---------------------------------------------||---------------------| - * (new condition in the middle: check if there is a command, if not go back to wait for command) - * (new node: go back to node 1 if there is no command) - * (node 5: wait for a command OR for a the wait time) + * Start -> Idle -> Condition -> Condition -> Wait for command -> Move -> Wait for move -> End + * ^ | + * |----------------------------------------------------------------| * * TODO: Replace with config */ @@ -69,33 +60,45 @@ std::shared_ptr create_test_activity() { auto start = std::make_shared(0); auto idle = std::make_shared(1, "Idle"); auto condition_moveable = std::make_shared(2); - auto wait_for_command = std::make_shared(3); - auto condition_command = std::make_shared(4); + auto condition_command = std::make_shared(3); + auto wait_for_command = std::make_shared(4); auto move = std::make_shared(5, "Move"); auto wait_for_move = std::make_shared(6); auto end = std::make_shared(7); start->add_output(idle); + // idle after start idle->add_output(condition_moveable); idle->set_system_id(system::system_id_t::IDLE); - // wait_for_command branch - activity::condition_t wait_branch = [&](const time::time_t & /* time */, - const std::shared_ptr &entity) { + // branch 1: check if the entity is moveable + activity::condition_t command_branch = [&](const time::time_t & /* time */, + const std::shared_ptr &entity) { return entity->has_component(component::component_t::MOVE); }; - condition_moveable->add_output(wait_for_command, wait_branch); + condition_moveable->add_output(condition_command, command_branch); - // end branch + // default: if it's not moveable, go straight to the end condition_moveable->set_default(end); + // branch 1: check if there is already a command in the queue + condition_command->add_output(move, gamestate::activity::command_in_queue); + + // default: if there is no command, wait for a command + condition_command->set_default(wait_for_command); + + // wait for a command event wait_for_command->add_output(move, gamestate::activity::primer_command_in_queue); + // move move->add_output(wait_for_move); move->set_system_id(system::system_id_t::MOVE_COMMAND); + // branch 1: wait for move event to finish wait_for_move->add_output(idle, gamestate::activity::primer_wait); + + // branch 2: wait for a new command event wait_for_move->add_output(move, gamestate::activity::primer_command_in_queue); return std::make_shared(0, start, "test"); From 97460f0a599b5ec122435c76747f15940964b265 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 00:04:58 +0100 Subject: [PATCH 24/44] gamestate: Fix command not returned by reference. --- libopenage/gamestate/component/internal/command_queue.cpp | 2 +- libopenage/gamestate/component/internal/command_queue.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libopenage/gamestate/component/internal/command_queue.cpp b/libopenage/gamestate/component/internal/command_queue.cpp index 6896f63cdf..b1e8ce1d47 100644 --- a/libopenage/gamestate/component/internal/command_queue.cpp +++ b/libopenage/gamestate/component/internal/command_queue.cpp @@ -26,7 +26,7 @@ const curve::Queue> &CommandQueue::get_queue() return this->command_queue; } -std::shared_ptr CommandQueue::pop_command(const time::time_t &time) { +const std::shared_ptr &CommandQueue::pop_command(const time::time_t &time) { return this->command_queue.pop_front(time); } diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index 6c16d5cbc7..4c63327a3b 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -53,7 +53,7 @@ class CommandQueue : public InternalComponent { * * @return Command in the front of the queue or nullptr if the queue is empty. */ - std::shared_ptr pop_command(const time::time_t &time); + const std::shared_ptr &pop_command(const time::time_t &time); private: /** From 8db98076121bc50e497379b5c92e944d6e441ed6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 01:39:04 +0100 Subject: [PATCH 25/44] gamestate: Detect condition from API objects. --- libopenage/gamestate/api/activity.cpp | 23 ++++++++++++++++++++++- libopenage/gamestate/api/activity.h | 25 +++++++++++++++++++++++++ libopenage/gamestate/api/definitions.h | 6 ++---- libopenage/gamestate/entity_factory.cpp | 2 +- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index 34d7896e8b..b47f1046cd 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -44,7 +44,18 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { return {db_view->get_object(next->get_name())}; } // 1+ next nodes - case activity::node_t::XOR_GATE: + case activity::node_t::XOR_GATE: { + auto next = node.get("Node.next"); + std::shared_ptr db_view = node.get_view(); + + std::vector next_nodes; + for (auto &next_node : next->get()) { + auto next_node_value = std::dynamic_pointer_cast(next_node.get_ptr()); + next_nodes.push_back(db_view->get_object(next_node_value->get_name())); + } + + return next_nodes; + } case activity::node_t::XOR_EVENT_GATE: { auto next = node.get("Node.next"); std::shared_ptr db_view = node.get_view(); @@ -62,6 +73,16 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { } } +bool APIActivityCondition::is_condition(const nyan::Object &obj) { + nyan::fqon_t immediate_parent = obj.get_parents()[0]; + return immediate_parent == "engine.util.activity.condition.Condition"; +} + +activity::condition_t APIActivityCondition::get_condition(const nyan::Object &condition) { + nyan::fqon_t immediate_parent = condition.get_parents()[0]; + return ACTIVITY_CONDITIONS.get(immediate_parent); +} + bool APIActivityEvent::is_event(const nyan::Object &obj) { nyan::fqon_t immediate_parent = obj.get_parents()[0]; return immediate_parent == "engine.util.activity.event.Event"; diff --git a/libopenage/gamestate/api/activity.h b/libopenage/gamestate/api/activity.h index cc365da449..e3eeeef661 100644 --- a/libopenage/gamestate/api/activity.h +++ b/libopenage/gamestate/api/activity.h @@ -8,6 +8,7 @@ #include "gamestate/activity/types.h" #include "gamestate/activity/xor_event_gate.h" +#include "gamestate/activity/xor_gate.h" namespace openage::gamestate { @@ -74,6 +75,30 @@ class APIActivityNode { static std::vector get_next(const nyan::Object &node); }; +/** + * Helper class for creating Activity condition objects from the nyan API. + */ +class APIActivityCondition { +public: + /** + * Check if a nyan object is a condition (type == \p engine.util.activity.condition.Condition). + * + * @param obj nyan object. + * + * @return true if the object is a condition, else false. + */ + static bool is_condition(const nyan::Object &obj); + + /** + * Get the condition function for a condition. + * + * @param condition nyan object. + * + * @return Condition function. + */ + static activity::condition_t get_condition(const nyan::Object &condition); +}; + /** * Helper class for creating Activity event objects from the nyan API. */ diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index cceaa277c3..56877de4e3 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -68,11 +68,9 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( std::pair("engine.util.activity.condition.type.CommandInQueue", std::function(gamestate::activity::command_in_queue)), - // TODO: API object assignment is inconsistent here - // Ideally all conditions should be an activity condition type - std::pair("engine.util.command.type.Idle", + std::pair("engine.util.activity.condition.type.NextCommandIdle", std::function(gamestate::activity::next_command_idle)), - std::pair("engine.util.command.type.Move", + std::pair("engine.util.activity.condition.type.NextCommandMove", std::function(gamestate::activity::next_command_move))); static const auto ACTIVITY_EVENT_PRIMERS = datastructure::create_const_map( diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 9376f775a3..97bc79a789 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -293,7 +293,7 @@ void EntityFactory::init_activity(const std::shared_ptr(activity_node); - // TODO: Add conditions + xor_gate->add_output(next_engine_node, api::APIActivityCondition::get_condition(nyan_node)); break; } case activity::node_t::XOR_EVENT_GATE: { From 31018e338d5ae61dc283f48dab01a5662085d9bb Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 01:57:20 +0100 Subject: [PATCH 26/44] gamestate: Detect system task from API objects. --- libopenage/gamestate/api/activity.cpp | 11 +++++++++++ libopenage/gamestate/api/activity.h | 10 ++++++++++ libopenage/gamestate/api/definitions.h | 15 +++++++++++++++ libopenage/gamestate/entity_factory.cpp | 12 +++++++----- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index b47f1046cd..82c5e6028a 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -73,6 +73,17 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { } } +system::system_id_t APIActivityNode::get_system_id(const nyan::Object &ability_node) { + auto ability = ability_node.get("Ability.ability"); + std::shared_ptr db_view = ability_node.get_view(); + + if (not ACTIVITY_TASK_SYSTEM_DEFS.contains(ability->get_name())) [[unlikely]] { + throw Error(MSG(err) << "Ability '" << ability->get_name() << "' has no associated system defined."); + } + + return ACTIVITY_TASK_SYSTEM_DEFS.get(ability->get_name()); +} + bool APIActivityCondition::is_condition(const nyan::Object &obj) { nyan::fqon_t immediate_parent = obj.get_parents()[0]; return immediate_parent == "engine.util.activity.condition.Condition"; diff --git a/libopenage/gamestate/api/activity.h b/libopenage/gamestate/api/activity.h index e3eeeef661..f1736d8d1c 100644 --- a/libopenage/gamestate/api/activity.h +++ b/libopenage/gamestate/api/activity.h @@ -9,6 +9,7 @@ #include "gamestate/activity/types.h" #include "gamestate/activity/xor_event_gate.h" #include "gamestate/activity/xor_gate.h" +#include "gamestate/system/types.h" namespace openage::gamestate { @@ -73,6 +74,15 @@ class APIActivityNode { * @return nyan object handles of the next nodes. */ static std::vector get_next(const nyan::Object &node); + + /** + * Get the system id of an Ability node. + * + * @param node nyan object. + * + * @return System ID of the node. + */ + static system::system_id_t get_system_id(const nyan::Object &ability_node); }; /** diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index 56877de4e3..ee435bf048 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -16,6 +16,7 @@ #include "gamestate/activity/xor_event_gate.h" #include "gamestate/activity/xor_gate.h" #include "gamestate/api/types.h" +#include "gamestate/system/types.h" namespace openage::gamestate::api { @@ -65,6 +66,20 @@ static const auto ACTIVITY_NODE_DEFS = datastructure::create_const_map( + std::pair("engine.ability.type.Idle", + system::system_id_t::IDLE), + std::pair("engine.ability.type.Move", + system::system_id_t::MOVE_COMMAND)); + +/** + * Maps API activity condition types to engine condition types. + */ static const auto ACTIVITY_CONDITIONS = datastructure::create_const_map( std::pair("engine.util.activity.condition.type.CommandInQueue", std::function(gamestate::activity::command_in_queue)), diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 97bc79a789..9abba34597 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -246,10 +246,12 @@ void EntityFactory::init_activity(const std::shared_ptr(node_id); node_id_map[node_id] = start_node; break; - case activity::node_t::TASK_SYSTEM: - node_id_map[node_id] = std::make_shared(node_id); - // TODO: Set system ID + case activity::node_t::TASK_SYSTEM: { + auto task_node = std::make_shared(node_id); + task_node->set_system_id(api::APIActivityNode::get_system_id(node)); + node_id_map[node_id] = task_node; break; + } case activity::node_t::XOR_GATE: node_id_map[node_id] = std::make_shared(node_id); break; @@ -257,7 +259,7 @@ void EntityFactory::init_activity(const std::shared_ptr(node_id); break; default: - throw Error{ERR << "Unknown activity node type"}; + throw Error{ERR << "Unknown activity node type of node: " << node.get_name()}; } // Get the node's outputs @@ -302,7 +304,7 @@ void EntityFactory::init_activity(const std::shared_ptr Date: Sun, 24 Dec 2023 02:09:20 +0100 Subject: [PATCH 27/44] gamestate: Fix reading from nyan activity condition API object. --- libopenage/gamestate/api/activity.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index 82c5e6028a..088c047103 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -45,12 +45,15 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { } // 1+ next nodes case activity::node_t::XOR_GATE: { - auto next = node.get("Node.next"); + auto conditions = node.get("Node.next"); std::shared_ptr db_view = node.get_view(); std::vector next_nodes; - for (auto &next_node : next->get()) { - auto next_node_value = std::dynamic_pointer_cast(next_node.get_ptr()); + for (auto &condition : conditions->get()) { + auto condition_fqon = std::dynamic_pointer_cast(condition.get_ptr()); + auto condition_obj = db_view->get_object(condition_fqon->get_name()); + + auto next_node_value = condition_obj.get("Condition.next"); next_nodes.push_back(db_view->get_object(next_node_value->get_name())); } From 537830aaf457ff66a524c7951af4933dd6fc90b6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 03:13:44 +0100 Subject: [PATCH 28/44] gamestate: Fix wrong member ID lookup. --- libopenage/gamestate/api/activity.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index 088c047103..f48db49867 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -37,15 +37,19 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { return {}; } // 1 next node - case activity::node_t::TASK_SYSTEM: + case activity::node_t::TASK_SYSTEM: { + auto next = node.get("Ability.next"); + std::shared_ptr db_view = node.get_view(); + return {db_view->get_object(next->get_name())}; + } case activity::node_t::START: { - auto next = node.get("Node.next"); + auto next = node.get("Start.next"); std::shared_ptr db_view = node.get_view(); return {db_view->get_object(next->get_name())}; } // 1+ next nodes case activity::node_t::XOR_GATE: { - auto conditions = node.get("Node.next"); + auto conditions = node.get("XORGate.next"); std::shared_ptr db_view = node.get_view(); std::vector next_nodes; @@ -60,7 +64,7 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { return next_nodes; } case activity::node_t::XOR_EVENT_GATE: { - auto next = node.get("Node.next"); + auto next = node.get("XOREventGate.next"); std::shared_ptr db_view = node.get_view(); std::vector next_nodes; From 30a6234abd58eaa0f5f54629aeceee1cfcabba63 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 03:39:58 +0100 Subject: [PATCH 29/44] gamestate: Fix lookup of activity conditions/events. --- libopenage/gamestate/api/activity.cpp | 10 ++- libopenage/gamestate/api/definitions.h | 2 +- libopenage/gamestate/entity_factory.cpp | 99 ++++++++++++++++--------- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index f48db49867..63dea05e6e 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -54,13 +54,16 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { std::vector next_nodes; for (auto &condition : conditions->get()) { - auto condition_fqon = std::dynamic_pointer_cast(condition.get_ptr()); - auto condition_obj = db_view->get_object(condition_fqon->get_name()); + auto condition_value = std::dynamic_pointer_cast(condition.get_ptr()); + auto condition_obj = db_view->get_object(condition_value->get_name()); auto next_node_value = condition_obj.get("Condition.next"); next_nodes.push_back(db_view->get_object(next_node_value->get_name())); } + auto default_next = node.get("XORGate.default"); + next_nodes.push_back(db_view->get_object(default_next->get_name())); + return next_nodes; } case activity::node_t::XOR_EVENT_GATE: { @@ -107,8 +110,7 @@ bool APIActivityEvent::is_event(const nyan::Object &obj) { } activity::event_primer_t APIActivityEvent::get_primer(const nyan::Object &event) { - nyan::fqon_t immediate_parent = event.get_parents()[0]; - return ACTIVITY_EVENT_PRIMERS.get(immediate_parent); + return ACTIVITY_EVENT_PRIMERS.get(event.get_name()); } } // namespace openage::gamestate::api diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index ee435bf048..f982571f94 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -89,7 +89,7 @@ static const auto ACTIVITY_CONDITIONS = datastructure::create_const_map( - std::pair("engine.util.activity.event.type.Command", + std::pair("engine.util.activity.event.type.CommandInQueue", std::function(gamestate::activity::primer_command_in_queue)), std::pair("engine.util.activity.event.type.Wait", std::function(gamestate::activity::primer_wait)), diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 9abba34597..cc9009b7b9 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -205,13 +205,13 @@ void EntityFactory::init_components(const std::shared_ptr(loop, create_test_activity()); - entity->add_component(activity); - // } + if (activity_ability) { + init_activity(loop, owner_db_view, entity, activity_ability.value()); + } + else { + auto activity = std::make_shared(loop, create_test_activity()); + entity->add_component(activity); + } } void EntityFactory::init_activity(const std::shared_ptr &loop, @@ -275,37 +275,64 @@ void EntityFactory::init_activity(const std::shared_ptrget_object(current_node.first); auto activity_node = node_id_map[current_node.second]; - auto next_nodes = api::APIActivityNode::get_next(nyan_node); - for (const auto &next_node : next_nodes) { - auto next_node_id = visited[next_node.get_name()]; - auto next_engine_node = node_id_map[next_node_id]; - - switch (activity_node->get_type()) { - case activity::node_t::END: - break; - case activity::node_t::START: { - auto start = std::static_pointer_cast(activity_node); - start->add_output(next_engine_node); - break; - } - case activity::node_t::TASK_SYSTEM: { - auto task_system = std::static_pointer_cast(activity_node); - task_system->add_output(next_engine_node); - break; - } - case activity::node_t::XOR_GATE: { - auto xor_gate = std::static_pointer_cast(activity_node); - xor_gate->add_output(next_engine_node, api::APIActivityCondition::get_condition(nyan_node)); - break; - } - case activity::node_t::XOR_EVENT_GATE: { - auto xor_event_gate = std::static_pointer_cast(activity_node); - xor_event_gate->add_output(next_engine_node, api::APIActivityEvent::get_primer(next_node)); - break; + switch (activity_node->get_type()) { + case activity::node_t::END: + break; + case activity::node_t::START: { + auto start = std::static_pointer_cast(activity_node); + auto output_fqon = nyan_node.get("Start.next")->get_name(); + auto output_id = visited[output_fqon]; + auto output_node = node_id_map[output_id]; + start->add_output(output_node); + break; + } + case activity::node_t::TASK_SYSTEM: { + auto task_system = std::static_pointer_cast(activity_node); + auto output_fqon = nyan_node.get("Ability.next")->get_name(); + auto output_id = visited[output_fqon]; + auto output_node = node_id_map[output_id]; + task_system->add_output(output_node); + break; + } + case activity::node_t::XOR_GATE: { + auto xor_gate = std::static_pointer_cast(activity_node); + auto conditions = nyan_node.get("XORGate.next"); + for (auto &condition : conditions->get()) { + auto condition_value = std::dynamic_pointer_cast(condition.get_ptr()); + auto condition_obj = owner_db_view->get_object(condition_value->get_name()); + + auto output_value = condition_obj.get("Condition.next")->get_name(); + auto output_id = visited[output_value]; + auto output_node = node_id_map[output_id]; + + xor_gate->add_output(output_node, api::APIActivityCondition::get_condition(condition_obj)); } - default: - throw Error{ERR << "Unknown activity node type of node: " << current_node.first}; + + auto default_fqon = nyan_node.get("XORGate.default")->get_name(); + auto default_id = visited[default_fqon]; + auto default_node = node_id_map[default_id]; + xor_gate->set_default(default_node); + break; + } + case activity::node_t::XOR_EVENT_GATE: { + auto xor_event_gate = std::static_pointer_cast(activity_node); + auto next = nyan_node.get("XOREventGate.next"); + for (auto &next_node : next->get()) { + auto event_value = std::dynamic_pointer_cast(next_node.first.get_ptr()); + auto event_obj = owner_db_view->get_object(event_value->get_name()); + + auto next_node_value = std::dynamic_pointer_cast(next_node.second.get_ptr()); + auto next_node_obj = owner_db_view->get_object(next_node_value->get_name()); + + auto output_id = visited[next_node_obj.get_name()]; + auto output_node = node_id_map[output_id]; + + xor_event_gate->add_output(output_node, api::APIActivityEvent::get_primer(event_obj)); } + break; + } + default: + throw Error{ERR << "Unknown activity node type of node: " << current_node.first}; } } From bc1f82dacf9398301eb7ae8ec6f10e131510c9ef Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 21:55:41 +0100 Subject: [PATCH 30/44] curve: Fix queue behaviour when popping elements. --- libopenage/curve/queue.h | 85 +++++++++++++++---- libopenage/curve/tests/container.cpp | 40 ++++++++- .../component/internal/command_queue.cpp | 2 +- .../component/internal/command_queue.h | 2 +- 4 files changed, 108 insertions(+), 21 deletions(-) diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index 3ac8d20d9a..a6a0a142ef 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -53,7 +53,7 @@ class Queue : public event::EventEntity { EventEntity{loop}, _id{id}, _idstr{idstr}, - last_front{this->container.begin()} {} + last_pop{time::time_t::zero()} {} // prevent accidental copy of queue Queue(const Queue &) = delete; @@ -69,12 +69,13 @@ class Queue : public event::EventEntity { const T &front(const time::time_t &time) const; /** - * Get the first element in the queue at the given time. + * Get the first element in the queue at the given time and remove it from + * the queue. * * @param time The time to get the element at. * @param value Queue element. */ - const T &pop_front(const time::time_t &time); + const T pop_front(const time::time_t &time); /** * Check if the queue is empty at a given time. @@ -183,24 +184,74 @@ class Queue : public event::EventEntity { */ container_t container; - iterator last_front; + /** + * The time of the last access to the queue. + */ + time::time_t last_pop; }; template -const T &Queue::front(const time::time_t &t) const { - return this->begin(t).value(); +const T &Queue::front(const time::time_t &time) const { + if (this->empty(time)) [[unlikely]] { + throw Error{MSG(err) << "Tried accessing front at " + << time << " but queue is empty."}; + } + + // search for the first element that is after the given time + auto it = this->container.begin(); + ++it; + while (it != this->container.end() and it->time() <= time) { + ++it; + } + + // get the last element before the given time + --it; + + return it->value; } template -bool Queue::empty(const time::time_t &time) const { - return this->last_front == this->begin(time).get_base(); +const T Queue::pop_front(const time::time_t &time) { + if (this->empty(time)) [[unlikely]] { + throw Error{MSG(err) << "Tried accessing front at " + << time << " but queue is empty."}; + } + + // search for the first element that is after the given time + auto it = this->container.begin(); + ++it; + while (it->time() <= time and it != this->container.end()) { + ++it; + } + + auto to = it->time(); + auto from = time; + + // get the last element inserted before the given time + --it; + auto val = it->value; + + // erase the element + // TODO: We should be able to reinsert elements + auto filter_iterator = QueueFilterIterator>(it, this, to, from); + this->erase(filter_iterator); + + this->last_pop = time; + + return val; } template -inline const T &Queue::pop_front(const time::time_t &time) { - this->last_front = this->begin(time).get_base(); - return this->front(time); +bool Queue::empty(const time::time_t &time) const { + if (this->container.empty()) { + return true; + } + + // search for the first element that is after the given time + auto begin = this->begin(time).get_base(); + + return begin == this->container.begin() and begin->time() > time; } template @@ -230,15 +281,14 @@ QueueFilterIterator> Queue::end(const time::time_t &t) const { template -QueueFilterIterator> Queue::between( - const time::time_t &begin, - const time::time_t &end) const { +QueueFilterIterator> Queue::between(const time::time_t &begin, + const time::time_t &end) const { auto it = QueueFilterIterator>( container.begin(), this, begin, end); - if (!container.empty() && !it.valid()) { + if (not it.valid()) { ++it; } return it; @@ -253,9 +303,8 @@ void Queue::erase(const CurveIterator> &it) { template -QueueFilterIterator> Queue::insert( - const time::time_t &time, - const T &e) { +QueueFilterIterator> Queue::insert(const time::time_t &time, + const T &e) { const_iterator insertion_point = this->container.end(); for (auto it = this->container.begin(); it != this->container.end(); ++it) { if (time < it->time()) { diff --git a/libopenage/curve/tests/container.cpp b/libopenage/curve/tests/container.cpp index a5efbf2887..9787243db3 100644 --- a/libopenage/curve/tests/container.cpp +++ b/libopenage/curve/tests/container.cpp @@ -149,11 +149,25 @@ void test_queue() { auto loop = std::make_shared(); Queue q{loop, 0}; - q.insert(0, 1); + + TESTEQUALS(q.empty(0), true); + TESTEQUALS(q.empty(1), true); + TESTEQUALS(q.empty(100001), true); + q.insert(2, 2); q.insert(4, 3); q.insert(10, 4); q.insert(100001, 5); + q.insert(100001, 6); + + TESTEQUALS(q.empty(0), true); + TESTEQUALS(q.empty(1), true); + TESTEQUALS(q.empty(2), false); + TESTEQUALS(q.empty(100001), false); + TESTEQUALS(q.empty(100002), false); + + q.insert(0, 1); + TESTEQUALS(*q.begin(0), 1); TESTEQUALS(*q.begin(1), 2); TESTEQUALS(*q.begin(2), 2); @@ -164,6 +178,17 @@ void test_queue() { TESTEQUALS(*q.begin(12), 5); TESTEQUALS(*q.begin(100000), 5); + TESTEQUALS(q.front(0), 1); + TESTEQUALS(q.front(1), 1); + TESTEQUALS(q.front(2), 2); + TESTEQUALS(q.front(3), 2); + TESTEQUALS(q.front(4), 3); + TESTEQUALS(q.front(5), 3); + TESTEQUALS(q.front(10), 4); + TESTEQUALS(q.front(12), 4); + TESTEQUALS(q.front(100000), 4); + TESTEQUALS(q.front(100001), 6); + { std::unordered_set reference = {1, 2, 3}; for (auto it = q.between(0, 6); it != q.end(); ++it) { @@ -204,6 +229,19 @@ void test_queue() { } TESTEQUALS(reference.empty(), true); } + + + TESTEQUALS(q.pop_front(0), 1); + TESTEQUALS(q.empty(0), true); + + TESTEQUALS(q.pop_front(12), 4); + TESTEQUALS(q.empty(12), false); + + TESTEQUALS(q.pop_front(12), 3); + TESTEQUALS(q.empty(12), false); + + TESTEQUALS(q.pop_front(12), 2); + TESTEQUALS(q.empty(12), true); } diff --git a/libopenage/gamestate/component/internal/command_queue.cpp b/libopenage/gamestate/component/internal/command_queue.cpp index b1e8ce1d47..b22d9fe834 100644 --- a/libopenage/gamestate/component/internal/command_queue.cpp +++ b/libopenage/gamestate/component/internal/command_queue.cpp @@ -26,7 +26,7 @@ const curve::Queue> &CommandQueue::get_queue() return this->command_queue; } -const std::shared_ptr &CommandQueue::pop_command(const time::time_t &time) { +const std::shared_ptr CommandQueue::pop_command(const time::time_t &time) { return this->command_queue.pop_front(time); } diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index 4c63327a3b..f0725a686d 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -53,7 +53,7 @@ class CommandQueue : public InternalComponent { * * @return Command in the front of the queue or nullptr if the queue is empty. */ - const std::shared_ptr &pop_command(const time::time_t &time); + const std::shared_ptr pop_command(const time::time_t &time); private: /** From c269a67c0d70b9cf308e8405905ac7dc342f145a Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 21:57:27 +0100 Subject: [PATCH 31/44] convert: Fix game entity activity not idling after move. --- openage/convert/processor/conversion/aoc/pregen_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openage/convert/processor/conversion/aoc/pregen_processor.py b/openage/convert/processor/conversion/aoc/pregen_processor.py index ba41e11d86..db92c0fa8a 100644 --- a/openage/convert/processor/conversion/aoc/pregen_processor.py +++ b/openage/convert/processor/conversion/aoc/pregen_processor.py @@ -334,7 +334,7 @@ def generate_activities( wait_command = api_objects["engine.util.activity.event.type.CommandInQueue"] wait_raw_api_object.add_raw_member("next", { - wait_finish: queue_forward_ref, + wait_finish: idle_forward_ref, # TODO: don't go back to move, go to xor gate that # branches depending on command wait_command: branch_forward_ref From c2244c4888d2e1da254f59aed4803ad168773e21 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 22:11:12 +0100 Subject: [PATCH 32/44] convert: Import aliases for nyan API activities. --- .../convert/processor/conversion/aoc/modpack_subprocessor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py index 29764c5823..1c823c3ba6 100644 --- a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py @@ -151,6 +151,11 @@ def _set_static_aliases(modpack: Modpack, import_tree: ImportTree) -> None: import_tree.add_alias(("engine", "ability", "property", "type"), "ability_prop") # Auxiliary objects + import_tree.add_alias( + ("engine", "util", "activity", "condition", "type"), "activity_condition" + ) + import_tree.add_alias(("engine", "util", "activity", "event", "type"), "activity_event") + import_tree.add_alias(("engine", "util", "activity", "node", "type"), "activity_node") import_tree.add_alias(("engine", "util", "accuracy"), "accuracy") import_tree.add_alias( ("engine", "util", "animation_override"), "animation_override" From d035829d47dc8f2ea986f10c1e6c5565f3220708 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 22:11:33 +0100 Subject: [PATCH 33/44] convert: Warn when alias cannot be added to import tree. --- openage/nyan/import_tree.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openage/nyan/import_tree.py b/openage/nyan/import_tree.py index 8a3880ff8d..5e61feb55c 100644 --- a/openage/nyan/import_tree.py +++ b/openage/nyan/import_tree.py @@ -7,6 +7,8 @@ from enum import Enum import typing +from openage.log import warn + if typing.TYPE_CHECKING: from openage.convert.entity_object.export.formats.nyan_file import NyanFile from openage.nyan.nyan_structs import NyanObject @@ -161,7 +163,9 @@ def add_alias(self, fqon: tuple[str], alias: str) -> None: current_node = current_node.get_child(node_str) except KeyError: # as err: - # TODO: Do not silently fail + # TODO: Fail when the fqon is not found in the tree + warn(f"fqon '{'.'.join(fqon)}' " + "could not be found in import tree") return # raise KeyError(f"fqon '{'.'.join(fqon)}' " # "could not be found in import tree") from err From c31e8615fdb3eda4a80c35df9d06e7c89741a6f6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 22:15:17 +0100 Subject: [PATCH 34/44] doc: Replace activity example in docs with current default activity. --- doc/code/game_simulation/images/activity_graph.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/code/game_simulation/images/activity_graph.svg b/doc/code/game_simulation/images/activity_graph.svg index bf87f779fa..4044797dc2 100644 --- a/doc/code/game_simulation/images/activity_graph.svg +++ b/doc/code/game_simulation/images/activity_graph.svg @@ -1,4 +1,4 @@ -IdleMoveStartMoveable?Can MoveCan't MoveWait for commandWait for Move to FinishEnd \ No newline at end of file +Idlecommand in queue?wait for commandbranch on commandGatherAttackMovemove finishednew commandcommand receivedMove \ No newline at end of file From 737e2b29065120477f9784d9fab4733719c58fea Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 24 Dec 2023 22:52:19 +0100 Subject: [PATCH 35/44] gamestate: Cache created activities. --- libopenage/gamestate/entity_factory.cpp | 11 +++++++++++ libopenage/gamestate/entity_factory.h | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index cc9009b7b9..c9ab7ab472 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -219,6 +219,16 @@ void EntityFactory::init_activity(const std::shared_ptr &entity, const nyan::Object &ability) { nyan::Object graph = ability.get_object("Activity.graph"); + + // Check if the activity is already exists in the cache + if (this->activity_cache.contains(graph.get_name())) { + auto activity = this->activity_cache.at(graph.get_name()); + auto component = std::make_shared(loop, activity); + entity->add_component(component); + + return; + } + auto start_obj = api::APIActivity::get_start(graph); size_t node_id = 0; @@ -337,6 +347,7 @@ void EntityFactory::init_activity(const std::shared_ptr(0, start_node, graph.get_name()); + this->activity_cache.insert({graph.get_name(), activity}); auto component = std::make_shared(loop, activity); entity->add_component(component); diff --git a/libopenage/gamestate/entity_factory.h b/libopenage/gamestate/entity_factory.h index 728a91b820..1a12ece80b 100644 --- a/libopenage/gamestate/entity_factory.h +++ b/libopenage/gamestate/entity_factory.h @@ -21,6 +21,11 @@ class RenderFactory; } namespace gamestate { + +namespace activity { +class Activity; +} // namespace activity + class GameEntity; class GameState; class Player; @@ -128,6 +133,11 @@ class EntityFactory { // TODO: Cache created game entities. + /** + * Cache for activities. + */ + std::unordered_map> activity_cache; + /** * Mutex for thread safety. */ From 2457ed8314215d4f8ee3e25a856ce433483757da Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 18:57:41 +0100 Subject: [PATCH 36/44] gamestate: Make command queue getter non-const. --- libopenage/gamestate/activity/event/command_in_queue.cpp | 2 +- libopenage/gamestate/component/internal/command_queue.cpp | 2 +- libopenage/gamestate/component/internal/command_queue.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libopenage/gamestate/activity/event/command_in_queue.cpp b/libopenage/gamestate/activity/event/command_in_queue.cpp index 51a82750d0..0168e2bf83 100644 --- a/libopenage/gamestate/activity/event/command_in_queue.cpp +++ b/libopenage/gamestate/activity/event/command_in_queue.cpp @@ -26,7 +26,7 @@ std::shared_ptr primer_command_in_queue(const time::time_ params); auto entity_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); - auto &queue = const_cast> &>(entity_queue->get_queue()); + auto &queue = entity_queue->get_queue(); queue.add_dependent(ev); return ev; diff --git a/libopenage/gamestate/component/internal/command_queue.cpp b/libopenage/gamestate/component/internal/command_queue.cpp index b22d9fe834..4c6963a75d 100644 --- a/libopenage/gamestate/component/internal/command_queue.cpp +++ b/libopenage/gamestate/component/internal/command_queue.cpp @@ -22,7 +22,7 @@ void CommandQueue::add_command(const time::time_t &time, this->command_queue.insert(time, command); } -const curve::Queue> &CommandQueue::get_queue() const { +curve::Queue> &CommandQueue::get_queue() { return this->command_queue; } diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index f0725a686d..ea0c26502a 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -44,7 +44,7 @@ class CommandQueue : public InternalComponent { * * @return Command queue. */ - const curve::Queue> &get_queue() const; + curve::Queue> &get_queue(); /** * Get the command in the front of the queue. From ab12e98d28d454365db95570c560a4c49be4fee5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:05:27 +0100 Subject: [PATCH 37/44] time: Constants for min, max, zero. --- libopenage/time/time.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libopenage/time/time.h b/libopenage/time/time.h index a6851f679c..ceac0c6365 100644 --- a/libopenage/time/time.h +++ b/libopenage/time/time.h @@ -15,4 +15,19 @@ namespace openage::time { */ using time_t = util::FixedPoint; +/** + * Minimum time value. + */ +static constexpr time_t TIME_MIN = std::numeric_limits::min(); + +/** + * Maximum time value. + */ +static constexpr time_t TIME_MAX = std::numeric_limits::max(); + +/** + * Zero time value (start of simulation). + */ +static constexpr time_t TIME_ZERO = time_t::zero(); + } // namespace openage::time From 26b2f97925a8f9f3c38bcf0ba77917286d0c85de Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:11:21 +0100 Subject: [PATCH 38/44] curve: Replace time numeric limit with constants. --- libopenage/curve/base_curve.h | 6 +++--- libopenage/curve/iterator.h | 4 ++-- libopenage/curve/keyframe.h | 2 +- libopenage/curve/keyframe_container.h | 8 ++++---- libopenage/curve/map.h | 12 ++++++------ libopenage/curve/queue.h | 16 ++++++++-------- libopenage/curve/tests/curve_types.cpp | 4 ++-- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h index 8c56586637..bd5b6003ae 100644 --- a/libopenage/curve/base_curve.h +++ b/libopenage/curve/base_curve.h @@ -115,7 +115,7 @@ class BaseCurve : public event::EventEntity { * the keyframes of \p other. */ void sync(const BaseCurve &other, - const time::time_t &start = std::numeric_limits::min()); + const time::time_t &start = time::TIME_MIN); /** * Copy keyframes from another curve (with a different element type) to this curve. @@ -134,7 +134,7 @@ class BaseCurve : public event::EventEntity { template void sync(const BaseCurve &other, const std::function &converter, - const time::time_t &start = std::numeric_limits::min()); + const time::time_t &start = time::TIME_MIN); /** * Get the identifier of this curve. @@ -270,7 +270,7 @@ std::string BaseCurve::str() const { template void BaseCurve::check_integrity() const { - time::time_t last_time = std::numeric_limits::min(); + time::time_t last_time = time::TIME_MIN; for (const auto &keyframe : this->container) { if (keyframe.time < last_time) { throw Error{MSG(err) << "curve is broken after t=" << last_time << ": " << this->str()}; diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h index 2500e083cd..1062ddcc1e 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/iterator.h @@ -33,8 +33,8 @@ class CurveIterator { explicit CurveIterator(const container_t *c) : base{}, container{c}, - from{-std::numeric_limits::max()}, - to{+std::numeric_limits::max()} {} + from{-time::TIME_MAX}, + to{+time::TIME_MAX} {} protected: /** diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index d6b90af4b5..82e36ed147 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -33,7 +33,7 @@ class Keyframe { time{time}, value{value} {} - const time::time_t time = std::numeric_limits::min(); + const time::time_t time = time::TIME_MIN; T value = T{}; }; diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index 1d7079498b..13b50bd04f 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -281,7 +281,7 @@ class KeyframeContainer { * the keyframes of \p other. */ iterator sync(const KeyframeContainer &other, - const time::time_t &start = std::numeric_limits::min()); + const time::time_t &start = time::TIME_MIN); /** * Copy keyframes from another container (with a different element type) to this container. @@ -298,7 +298,7 @@ class KeyframeContainer { template iterator sync(const KeyframeContainer &other, const std::function &converter, - const time::time_t &start = std::numeric_limits::min()); + const time::time_t &start = time::TIME_MIN); /** * Debugging method to be used from gdb to understand bugs better. @@ -328,7 +328,7 @@ template KeyframeContainer::KeyframeContainer() { // Create a default element at -Inf, that can always be dereferenced - so // there will by definition never be a element that cannot be dereferenced - this->container.push_back(keyframe_t(std::numeric_limits::min(), T())); + this->container.push_back(keyframe_t(time::TIME_MIN, T())); } @@ -336,7 +336,7 @@ template KeyframeContainer::KeyframeContainer(const T &defaultval) { // Create a default element at -Inf, that can always be dereferenced - so // there will by definition never be a element that cannot be dereferenced - this->container.push_back(keyframe_t(std::numeric_limits::min(), defaultval)); + this->container.push_back(keyframe_t(time::TIME_MIN, defaultval)); } diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h index 3a8374a128..9712c89470 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/map.h @@ -48,10 +48,10 @@ class UnorderedMap { at(const time::time_t &, const key_t &) const; MapFilterIterator - begin(const time::time_t &e = std::numeric_limits::max()) const; + begin(const time::time_t &e = time::TIME_MAX) const; MapFilterIterator - end(const time::time_t &e = std::numeric_limits::max()) const; + end(const time::time_t &e = time::TIME_MAX) const; MapFilterIterator insert(const time::time_t &birth, const key_t &, const val_t &); @@ -100,7 +100,7 @@ UnorderedMap::at(const time::time_t &time, e, this, time, - std::numeric_limits::max()); + time::TIME_MAX); } else { return {}; @@ -114,7 +114,7 @@ UnorderedMap::begin(const time::time_t &time) const { this->container.begin(), this, time, - std::numeric_limits::max()); + time::TIME_MAX); } template @@ -123,7 +123,7 @@ UnorderedMap::end(const time::time_t &time) const { return MapFilterIterator>( this->container.end(), this, - -std::numeric_limits::max(), + -time::TIME_MAX, time); } @@ -149,7 +149,7 @@ UnorderedMap::insert(const time::time_t &alive, const val_t &value) { return this->insert( alive, - std::numeric_limits::max(), + time::TIME_MAX, key, value); } diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index a6a0a142ef..de6807b5ab 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -53,7 +53,7 @@ class Queue : public event::EventEntity { EventEntity{loop}, _id{id}, _idstr{idstr}, - last_pop{time::time_t::zero()} {} + last_pop{time::TIME_ZERO} {} // prevent accidental copy of queue Queue(const Queue &) = delete; @@ -93,7 +93,7 @@ class Queue : public event::EventEntity { * @return Iterator to the first element. */ QueueFilterIterator> begin( - const time::time_t &t = -std::numeric_limits::max()) const; + const time::time_t &t = -time::TIME_MAX) const; /** * Get an iterator to the last element in the queue at the given time. @@ -102,7 +102,7 @@ class Queue : public event::EventEntity { * @return Iterator to the last element. */ QueueFilterIterator> end( - const time::time_t &t = std::numeric_limits::max()) const; + const time::time_t &t = time::TIME_MAX) const; /** * Get an iterator to elements that are in the queue between two time frames. @@ -112,8 +112,8 @@ class Queue : public event::EventEntity { * @return Iterator to the first element in the time frame. */ QueueFilterIterator> between( - const time::time_t &begin = std::numeric_limits::max(), - const time::time_t &end = std::numeric_limits::max()) const; + const time::time_t &begin = time::TIME_MAX, + const time::time_t &end = time::TIME_MAX) const; /** * Erase an element from the queue. @@ -262,7 +262,7 @@ QueueFilterIterator> Queue::begin(const time::time_t &t) const { it, this, t, - std::numeric_limits::max()); + time::TIME_MAX); } } @@ -276,7 +276,7 @@ QueueFilterIterator> Queue::end(const time::time_t &t) const { container.end(), this, t, - std::numeric_limits::max()); + time::TIME_MAX); } @@ -321,7 +321,7 @@ QueueFilterIterator> Queue::insert(const time::time_t &time, insertion_point, this, time, - std::numeric_limits::max()); + time::TIME_MAX); if (!ct.valid()) { ++ct; diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index cab8473bc3..40f20395ee 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -37,7 +37,7 @@ void curve_types() { { auto it = c.begin(); TESTEQUALS(it->value, 0); - TESTEQUALS(it->time, std::numeric_limits::min()); + TESTEQUALS(it->time, time::TIME_MIN); TESTEQUALS((++it)->time, 0); TESTEQUALS(it->value, 0); TESTEQUALS((++it)->time, 1); @@ -140,7 +140,7 @@ void curve_types() { { auto it = c.begin(); - TESTEQUALS(it->time, std::numeric_limits::min()); + TESTEQUALS(it->time, time::TIME_MIN); TESTEQUALS(it->value, 0); TESTEQUALS((++it)->time, 0); From 6f0117b768f0d086cefbe21e6955b6732baa00d3 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:11:29 +0100 Subject: [PATCH 39/44] event: Replace time numeric limit with constants. --- libopenage/event/demo/physics.cpp | 4 ++-- libopenage/event/event_loop.cpp | 4 ++-- libopenage/event/eventqueue.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp index d43365f042..e27a51d962 100644 --- a/libopenage/event/demo/physics.cpp +++ b/libopenage/event/demo/physics.cpp @@ -104,7 +104,7 @@ class BallReflectWall : public DependencyEventHandler { auto pos = positioncurve->get(now); if (speed[1] == 0) { - return std::numeric_limits::max(); + return time::TIME_MAX; } time::time_t ty = 0; @@ -227,7 +227,7 @@ class BallReflectPanel : public DependencyEventHandler { auto pos = positioncurve->get(now); if (speed[0] == 0) - return std::numeric_limits::max(); + return time::TIME_MAX; time::time_t ty = 0; diff --git a/libopenage/event/event_loop.cpp b/libopenage/event/event_loop.cpp index eb9e8ca6f1..26bf176978 100644 --- a/libopenage/event/event_loop.cpp +++ b/libopenage/event/event_loop.cpp @@ -151,7 +151,7 @@ int EventLoop::execute_events(const time::time_t &time_until, time::time_t new_time = event->get_eventhandler()->predict_invoke_time( target, state, event->get_time()); - if (new_time != std::numeric_limits::min()) { + if (new_time != time::TIME_MIN) { event->set_time(new_time); log::log(DBG << "Loop: repeating event \"" << event->get_eventhandler()->id() @@ -204,7 +204,7 @@ void EventLoop::update_changes(const std::shared_ptr &state) { time::time_t new_time = evnt->get_eventhandler() ->predict_invoke_time(entity, state, change.time); - if (new_time != std::numeric_limits::min()) { + if (new_time != time::TIME_MIN) { log::log(DBG << "Loop: due to a change, rescheduling event of '" << evnt->get_eventhandler()->id() << "' on entity '" << entity->idstr() diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp index 83a96e6e16..51a0c2fdaa 100644 --- a/libopenage/event/eventqueue.cpp +++ b/libopenage/event/eventqueue.cpp @@ -35,7 +35,7 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptrset_time(event->get_eventhandler() ->predict_invoke_time(trgt, state, reference_time)); - if (event->get_time() == std::numeric_limits::min()) { + if (event->get_time() == time::TIME_MIN) { log::log(DBG << "Queue: ignoring insertion of event " << event->get_eventhandler()->id() << " because no execution was scheduled."); From 1f54b5df5b370addd17500f9be4576bde507c75a Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:11:43 +0100 Subject: [PATCH 40/44] pong: Replace time numeric limit with constants. --- libopenage/main/demo/pong/aicontroller.cpp | 4 ++-- libopenage/main/demo/pong/physics.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libopenage/main/demo/pong/aicontroller.cpp b/libopenage/main/demo/pong/aicontroller.cpp index bdd1b7df3a..fd635d2a21 100644 --- a/libopenage/main/demo/pong/aicontroller.cpp +++ b/libopenage/main/demo/pong/aicontroller.cpp @@ -34,7 +34,7 @@ std::vector get_ai_inputs(const std::shared_ptr &player, time::time_t ty_hit = 0, tx_hit = 0; if (speed[0] == 0) { - tx_hit = std::numeric_limits::max(); + tx_hit = time::TIME_MAX; } else if (speed[0] > 0) { tx_hit = time::time_t::from_double((area_width - ball_pos[0]) / speed[0]); @@ -44,7 +44,7 @@ std::vector get_ai_inputs(const std::shared_ptr &player, } if (speed[1] == 0) { - ty_hit = std::numeric_limits::max(); + ty_hit = time::TIME_MAX; } else if (speed[1] > 0) { ty_hit = time::time_t::from_double((area_height - ball_pos[1]) / speed[1]); diff --git a/libopenage/main/demo/pong/physics.cpp b/libopenage/main/demo/pong/physics.cpp index 72a1f65c96..e2aa12e28e 100644 --- a/libopenage/main/demo/pong/physics.cpp +++ b/libopenage/main/demo/pong/physics.cpp @@ -75,7 +75,7 @@ class BallReflectWall : public event::DependencyEventHandler { auto screen_size = state->area_size->get(now); if (speed[1] == 0) { - return std::numeric_limits::max(); + return time::TIME_MAX; } time::time_t ty = 0; @@ -199,7 +199,7 @@ class BallReflectPanel : public event::DependencyEventHandler { auto screen_size = state->area_size->get(now); if (speed[0] == 0) - return std::numeric_limits::max(); + return time::TIME_MAX; time::time_t ty = 0; From 0e7178f6c069ff9823941cbf89c32184eb2defae Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:11:50 +0100 Subject: [PATCH 41/44] gamestate: Replace time numeric limit with constants. --- libopenage/gamestate/activity/event/command_in_queue.cpp | 2 +- libopenage/gamestate/entity_factory.cpp | 2 +- libopenage/gamestate/terrain.cpp | 2 +- libopenage/gamestate/terrain_factory.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libopenage/gamestate/activity/event/command_in_queue.cpp b/libopenage/gamestate/activity/event/command_in_queue.cpp index 0168e2bf83..af57692078 100644 --- a/libopenage/gamestate/activity/event/command_in_queue.cpp +++ b/libopenage/gamestate/activity/event/command_in_queue.cpp @@ -22,7 +22,7 @@ std::shared_ptr primer_command_in_queue(const time::time_ entity->get_manager(), state, // event is not executed until a command is available - std::numeric_limits::max(), + time::TIME_MAX, params); auto entity_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index c9ab7ab472..831527c1ec 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -191,7 +191,7 @@ void EntityFactory::init_components(const std::shared_ptradd_attribute(std::numeric_limits::min(), + live->add_attribute(time::TIME_MIN, attribute.get_name(), std::make_shared>(loop, 0, diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index f31fc648b8..9e40230ffe 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -32,7 +32,7 @@ void Terrain::attach_renderer(const std::shared_ptr &re chunk->get_offset()); chunk->set_render_entity(render_entity); - chunk->render_update(time::time_t::zero()); + chunk->render_update(time::TIME_ZERO); } } diff --git a/libopenage/gamestate/terrain_factory.cpp b/libopenage/gamestate/terrain_factory.cpp index 3a46773429..7276d73d57 100644 --- a/libopenage/gamestate/terrain_factory.cpp +++ b/libopenage/gamestate/terrain_factory.cpp @@ -131,7 +131,7 @@ std::shared_ptr TerrainFactory::add_chunk(const std::shared_ptrrender_factory->add_terrain_render_entity(size, offset); chunk->set_render_entity(render_entity); - chunk->render_update(time::time_t::zero(), + chunk->render_update(time::TIME_ZERO, test_texture_path); } From 73b97d4f4b09d80a37d2e5d41d35e65494b68b9b Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:26:01 +0100 Subject: [PATCH 42/44] curve: Search queue from the end instead from begin. --- libopenage/curve/queue.h | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index de6807b5ab..32675a72d3 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -198,15 +198,12 @@ const T &Queue::front(const time::time_t &time) const { << time << " but queue is empty."}; } - // search for the first element that is after the given time - auto it = this->container.begin(); - ++it; - while (it != this->container.end() and it->time() <= time) { - ++it; - } - - // get the last element before the given time + // search for the last element before the given time + auto it = this->container.end(); --it; + while (it->time() > time and it != this->container.begin()) { + --it; + } return it->value; } @@ -218,19 +215,20 @@ const T Queue::pop_front(const time::time_t &time) { << time << " but queue is empty."}; } - // search for the first element that is after the given time - auto it = this->container.begin(); - ++it; - while (it->time() <= time and it != this->container.end()) { - ++it; + // search for the last element before the given time + auto it = this->container.end(); + --it; + while (it->time() > time and it != this->container.begin()) { + --it; } - auto to = it->time(); - auto from = time; - // get the last element inserted before the given time + auto val = std::move(it->value); + + // get the time span between current time and the next element + auto to = (++it)->time(); --it; - auto val = it->value; + auto from = time; // erase the element // TODO: We should be able to reinsert elements From 6d41917e9932581d336844cf87ca59311e170f37 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 25 Dec 2023 19:36:15 +0100 Subject: [PATCH 43/44] gamestate: Remove unused variable. --- libopenage/gamestate/api/activity.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libopenage/gamestate/api/activity.cpp b/libopenage/gamestate/api/activity.cpp index 63dea05e6e..edd596abe8 100644 --- a/libopenage/gamestate/api/activity.cpp +++ b/libopenage/gamestate/api/activity.cpp @@ -85,7 +85,6 @@ std::vector APIActivityNode::get_next(const nyan::Object &node) { system::system_id_t APIActivityNode::get_system_id(const nyan::Object &ability_node) { auto ability = ability_node.get("Ability.ability"); - std::shared_ptr db_view = ability_node.get_view(); if (not ACTIVITY_TASK_SYSTEM_DEFS.contains(ability->get_name())) [[unlikely]] { throw Error(MSG(err) << "Ability '" << ability->get_name() << "' has no associated system defined."); From ec381dfe5ea2bd38433e92d138d58e7975fa247a Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 2 Jan 2024 19:39:51 +0100 Subject: [PATCH 44/44] convert: Clarify modpack version/versionstr difference. --- .../entity_object/export/formats/modpack_info.py | 12 ++++++------ openage/convert/tool/api_export.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openage/convert/entity_object/export/formats/modpack_info.py b/openage/convert/entity_object/export/formats/modpack_info.py index 1a6017ce4e..0df5f220ce 100644 --- a/openage/convert/entity_object/export/formats/modpack_info.py +++ b/openage/convert/entity_object/export/formats/modpack_info.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 the openage authors. See copying.md for legal info. +# Copyright 2020-2024 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-instance-attributes,too-many-arguments @@ -153,7 +153,7 @@ def add_dependency(self, modpack_id: str) -> None: def set_info( self, packagename: str, - version: str, + modpack_version: str, versionstr: str = None, repo: str = None, alias: str = None, @@ -168,9 +168,9 @@ def set_info( :param packagename: Name of the modpack. :type packagename: str - :param version: Internal version number. Must have semver format. - :type version: str - :param versionstr: Human-readable version number. + :param modpack_version: Internal version number. Must have semver format. + :type modpack_version: str + :param versionstr: Human-readable version number. Can be anything. :type versionstr: str :param repo: Name of the repo where the package is hosted. :type repo: str @@ -188,7 +188,7 @@ def set_info( :type licenses: list """ self.packagename = packagename - self.version = version + self.version = modpack_version if versionstr: self.extra_info["versionstr"] = versionstr diff --git a/openage/convert/tool/api_export.py b/openage/convert/tool/api_export.py index 0e35ec4602..1124da8771 100644 --- a/openage/convert/tool/api_export.py +++ b/openage/convert/tool/api_export.py @@ -1,4 +1,4 @@ -# Copyright 2023-2023 the openage authors. See copying.md for legal info. +# Copyright 2023-2024 the openage authors. See copying.md for legal info. """ Export tool for dumping the nyan API of the engine from the converter. @@ -76,7 +76,7 @@ def create_modpack() -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("engine", "0.4.1", "0.4.1", repo="openage") + mod_def.set_info("engine", modpack_version="0.4.1", versionstr="0.4.1", repo="openage") mod_def.add_include("**")