diff --git a/openage/convert/CMakeLists.txt b/openage/convert/CMakeLists.txt index d21057bd447..1a1f5cb3954 100644 --- a/openage/convert/CMakeLists.txt +++ b/openage/convert/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(hardcoded) add_subdirectory(interface) add_subdirectory(nyan) add_subdirectory(opus) +add_subdirectory(processor) diff --git a/openage/convert/dataformat/CMakeLists.txt b/openage/convert/dataformat/CMakeLists.txt index 027c54a7729..4d3d16de83e 100644 --- a/openage/convert/dataformat/CMakeLists.txt +++ b/openage/convert/dataformat/CMakeLists.txt @@ -1,6 +1,7 @@ add_py_modules( __init__.py content_snippet.py + converter_object.py data_definition.py data_formatter.py entry_parser.py @@ -15,3 +16,5 @@ add_py_modules( util.py value_member.py ) + +add_subdirectory(aoc) \ No newline at end of file diff --git a/openage/convert/dataformat/aoc/CMakeLists.txt b/openage/convert/dataformat/aoc/CMakeLists.txt new file mode 100644 index 00000000000..280499032bb --- /dev/null +++ b/openage/convert/dataformat/aoc/CMakeLists.txt @@ -0,0 +1,5 @@ +add_py_modules( + __init__.py + genie_unit.py + genie_object_container.py +) diff --git a/openage/convert/dataformat/aoc/__init__.py b/openage/convert/dataformat/aoc/__init__.py new file mode 100644 index 00000000000..e696c3acc8d --- /dev/null +++ b/openage/convert/dataformat/aoc/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + +""" +Conversion data formats for Age of Empires II. +""" diff --git a/openage/convert/dataformat/aoc/genie_object_container.py b/openage/convert/dataformat/aoc/genie_object_container.py new file mode 100644 index 00000000000..b07235f9c7e --- /dev/null +++ b/openage/convert/dataformat/aoc/genie_object_container.py @@ -0,0 +1,69 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + +from openage.convert.dataformat.converter_object import ConverterObjectContainer + + +class GenieObjectContainer(ConverterObjectContainer): + """ + Contains everything from the dat file, sorted into several + categories. + """ + + def __init__(self): + + # ConverterObject types (the data from the game) + self.genie_units = {} + self.genie_techs = {} + self.graphics = {} + self.age_connections = {} + self.building_connections = {} + self.unit_connections = {} + self.tech_connections = {} + self.sounds = {} + self.civs = {} + + # ConverterObjectGroup types (things that will become + # nyan objects) + # key: group_id; value: ConverterObjectGroup instance + self.unit_lines = {} + self.building_lines = {} + self.task_groups = {} + self.transform_groups = {} + self.villager_groups = {} + self.monk_groups = {} + + def add_unit_line(self, unit_line): + """ + Adds a Genie unit line to the data set. + """ + self.unit_lines.update({unit_line.get_id(): unit_line}) + + def add_building_line(self, building_line): + """ + Adds a Genie building line to the data set. + """ + self.building_lines.update({building_line.get_id(): building_line}) + + def add_monk_group(self, monk_group): + """ + Adds a Genie villager task group to the data set. + """ + self.monk_groups.update({monk_group.get_id(): monk_group}) + + def add_task_group(self, task_group): + """ + Adds a Genie task group to the data set. + """ + self.task_groups.update({task_group.get_id(): task_group}) + + def add_transform_group(self, transform_group): + """ + Adds a Genie transform group to the data set. + """ + self.transform_groups.update({transform_group.get_id(): transform_group}) + + def add_villager_group(self, task_group): + """ + Adds a Genie villager task group to the data set. + """ + self.villager_groups.update({task_group.get_id(): task_group}) diff --git a/openage/convert/dataformat/aoc/genie_unit.py b/openage/convert/dataformat/aoc/genie_unit.py new file mode 100644 index 00000000000..d102b224a6c --- /dev/null +++ b/openage/convert/dataformat/aoc/genie_unit.py @@ -0,0 +1,256 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + + +from openage.convert.dataformat.converter_object import ConverterObject,\ + ConverterObjectGroup + + +class GenieUnitObject(ConverterObject): + """ + Ingame object in AoE2. + """ + + def __init__(self, unit_id, full_data_set): + + super().__init__(unit_id, []) + + self.data = full_data_set + self.data.genie_units.append(self) + + +class GenieUnitLineGroup(ConverterObjectGroup): + """ + A collection of GenieUnitObject types that form an "upgrade line" + in Age of Empires. + + Example: Spearman->Pikeman->Helbardier + + The first unit in the line will become the GameEntity, the rest will + be patches to that GameEntity applied by Techs. + """ + + def __init__(self, line_id, full_data_set): + """ + Creates a new Genie unit line. + + :param line_id: Internal line id in the .dat file. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + + super().__init__(line_id) + + # The line is stored as an ordered list of GenieUnitObjects. + self.line = [] + + # Reference to everything else in the gamedata + self.data = full_data_set + self.data.add_unit_line(self) + + def add_unit(self, genie_unit, after=None): + """ + Adds a unit to the line. + + :param genie_unit: A GenieUnit object that is part of this + unit line. + :param after: ID of a unit after which the new unit is + placed in the line. If a unit with this id + is not present, the unit is appended at the end + of the line. + """ + + unit_type = genie_unit.get_member("type").get_value() + + # Valid units have type >= 70 + if unit_type < 70: + raise Exception("GenieUnitObject must have type >= 70" + "to be added to line") + + if after: + for unit in self.line: + if after == unit.get_id(): + self.line.insert(self.line.index(unit), genie_unit) + break + + else: + self.line.append(genie_unit) + + else: + self.line.append(genie_unit) + + def contains_unit(self, unit_id): + """ + Returns True if a unit with unit_id is part of the line. + """ + for unit in self.line: + if unit.get_member("id") == unit_id: + return True + + return False + + +class GenieBuildingLineGroup(ConverterObjectGroup): + """ + A collection of GenieUnitObject types that represent a building + in Age of Empires. While buildings have no actual "lines" like units in + the game data, we will handle them as if they were organized that way. + + Buildings in AoE2 also create units and research techs, so + this is handled in here. + + The 'head unit' of a building line becomes the GameEntity, the rest will + be patches to that GameEntity applied by Techs. + """ + + def __init__(self, head_building_id, full_data_set): + """ + Creates a new Genie building line. + + :param head_building_id: The building that is first in line. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + + super().__init__(head_building_id) + + # The line is stored as an ordered list of GenieUnitObjects. + self.line = [] + + # List of GenieUnitLine objects + self.creates = [] + + # List of TODO objects + self.researches = [] + + # Reference to everything else in the gamedata + self.data = full_data_set + self.data.add_building_line(self) + + +class GenieUnitTransformGroup(ConverterObjectGroup): + """ + Collection of genie units that reference each other with their + transform_id. + + Example: Trebuchet + """ + + def __init__(self, head_unit_id, full_data_set): + """ + Creates a new Genie transform group. + + :param head_unit_id: Internal unit id of the unit that should be + the initial state. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + + super().__init__(head_unit_id) + + # Reference to everything else in the gamedata + self.data = full_data_set + self.data.add_transform_group(self) + + self.head_unit = self.data.genie_units[head_unit_id] + + transform_id = self.head_unit.get_member("transform_id").get_value() + self.transform_unit = self.data.genie_units[transform_id] + + +class GenieUnitTaskGroup(ConverterObjectGroup): + """ + Collection of genie units that have the same task group. + + Example: Villager + + The 'head unit' of a task group becomes the GameEntity, all + the other ones become variants or AnimationOverrides of abilities. + """ + + def __init__(self, task_group_id, head_task_id, full_data_set): + """ + Creates a new Genie task group. + + :param task_group_id: Internal task group id in the .dat file. + :param head_task_id: The unit with this task will become the head unit. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + + super().__init__(task_group_id) + + self.head_task_id = head_task_id + + # The task group is stored as a dict of GenieUnitObjects. + # key: task id; value: unit + self.units = {} + + # Add task group to gamedata + self.data = full_data_set + self.data.add_task_group(self) + + +class GenieVillagerGroup(GenieUnitTaskGroup): + """ + Special GenieUnitTaskGroup for villagers. + + Villagers come in two task groups, so one task group is a + variant of the other one. + """ + + def __init__(self, task_group_id, head_task_id, + variant_task_group_id, full_data_set): + """ + Creates a new Genie villager group. + + :param task_group_id: Internal task group id in the .dat file. + :param head_task_id: The unit with this task will become the head unit. + :param variant_task_group_id: The task group id of the variant. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + super().__init__(task_group_id, head_task_id, full_data_set) + + # Reference to the other task group + self.variant = self.data.task_groups[variant_task_group_id] + + # List of buildings that villagers can create + self.creates = [] + + self.data.add_villager_group(self) + + +class GenieMonkGroup(ConverterObjectGroup): + """ + Collection of monk and monk with relic. The switch + is hardcoded in AoE2. + + The 'head unit' will become the GameEntity, the 'switch unit' + will become a Container ability with CarryProgress. + """ + + def __init__(self, head_unit_id, switch_unit_id, full_data_set): + """ + Creates a new Genie monk group. + + :param head_unit_id: The unit with this task will become the actual + GameEntity. + :param switch_unit_id: This unit will be used to determine the + CarryProgress objects. + :param full_data_set: GenieObjectContainer instance that + contains all relevant data for the conversion + process. + """ + super().__init__(head_unit_id) + + # Reference to everything else in the gamedata + self.data = full_data_set + self.data.add_monk_group(self) + + self.head_unit = self.data.genie_units[head_unit_id] + self.switch_unit = self.data.genie_units[switch_unit_id] diff --git a/openage/convert/dataformat/converter_object.py b/openage/convert/dataformat/converter_object.py index 46a76ac56ef..f6ef466ab43 100644 --- a/openage/convert/dataformat/converter_object.py +++ b/openage/convert/dataformat/converter_object.py @@ -1,6 +1,4 @@ -# Copyright 2014-2019 the openage authors. See copying.md for legal info. - -# TODO pylint: disable=C,R,abstract-method +# Copyright 2019-2019 the openage authors. See copying.md for legal info. """ Objects that represent data structures in the original game. @@ -186,3 +184,13 @@ def get_nyan_object(self): def __repr__(self): raise NotImplementedError( "return short description of the object %s" % (type(self))) + + +class ConverterObjectContainer: + """ + A conainer for all ConverterObject instances in a converter process. + + It is recommended to create one ConverterObjectContainer for everything + and pass the reference around. + """ + pass diff --git a/openage/convert/dataformat/value_members.py b/openage/convert/dataformat/value_members.py index 7996106e7c1..9d9965f0747 100644 --- a/openage/convert/dataformat/value_members.py +++ b/openage/convert/dataformat/value_members.py @@ -1,4 +1,4 @@ -# Copyright 2014-2019 the openage authors. See copying.md for legal info. +# Copyright 2019-2019 the openage authors. See copying.md for legal info. # TODO pylint: disable=C,R,abstract-method """ diff --git a/openage/convert/gamedata/tech.py b/openage/convert/gamedata/tech.py index fa3e19b2395..f9aea77f953 100644 --- a/openage/convert/gamedata/tech.py +++ b/openage/convert/gamedata/tech.py @@ -144,7 +144,6 @@ class AgeTechTree(Exportable): struct_description = "items available when this age was reached." data_format = [ - (READ, "total_unit_tech_groups", "int32_t"), (READ, "id", "int32_t"), # 0=generic # 1=TODO @@ -215,7 +214,11 @@ class AgeTechTree(Exportable): (READ, "group_length_per_zone", "int8_t[10]"), ]) - data_format.append((READ, "max_age_length", "int8_t")) + data_format.extend([ + (READ, "max_age_length", "int8_t"), + # 1=Age + (READ, "line_mode", "int32_t"), + ]) class BuildingConnection(Exportable): @@ -339,8 +342,8 @@ class UnitConnection(Exportable): # min amount of researches to be discovered for this unit to be # available (READ, "required_research", "int32_t"), - # 0=independent/new in its line, 3=depends on a previous research in - # its line + # 2=first unit in line + # 3=unit that depends on a previous research in its line (READ, "line_mode", "int32_t"), (READ, "enabling_research", "int32_t"), ]) @@ -404,6 +407,7 @@ class ResearchConnection(Exportable): (READ, "vertical_line", "int32_t"), (READ, "location_in_age", "int32_t"), # 0=hidden, 1=first, 2=second - # 0=first age, else other ages. + # 0=first age unlocks + # 4=research (READ, "line_mode", "int32_t"), ]) diff --git a/openage/convert/processor/CMakeLists.txt b/openage/convert/processor/CMakeLists.txt new file mode 100644 index 00000000000..3af73e3cea9 --- /dev/null +++ b/openage/convert/processor/CMakeLists.txt @@ -0,0 +1,5 @@ +add_py_modules( + __init__.py +) + +add_subdirectory(aoc) \ No newline at end of file diff --git a/openage/convert/processor/__init__.py b/openage/convert/processor/__init__.py new file mode 100644 index 00000000000..fbf1ad62c24 --- /dev/null +++ b/openage/convert/processor/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + +""" +Drives the conversion process for the individual games. + +Every processor should have three stages (+ subroutines). + - Pre-processor: Reads data and media files and converts them to + a converter format. Also prepares API objects for + hardcoded stuff in the older games. + - Processor: Translates the data and media to nyan/openage formats. + - Post-processor: Makes (optional) changes to the converted data and + creates the modpacks. The modpacks will be forwarded + to the exporter. +""" diff --git a/openage/convert/processor/aoc/CMakeLists.txt b/openage/convert/processor/aoc/CMakeLists.txt new file mode 100644 index 00000000000..40105d17e5f --- /dev/null +++ b/openage/convert/processor/aoc/CMakeLists.txt @@ -0,0 +1,4 @@ +add_py_modules( + __init__.py + processor.py +) \ No newline at end of file diff --git a/openage/convert/processor/aoc/__init__.py b/openage/convert/processor/aoc/__init__.py new file mode 100644 index 00000000000..1419abd7d78 --- /dev/null +++ b/openage/convert/processor/aoc/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + +""" +Drives the conversion process for AoE2: The Conquerors. +""" diff --git a/openage/convert/processor/aoc/processor.py b/openage/convert/processor/aoc/processor.py new file mode 100644 index 00000000000..2aff28daa81 --- /dev/null +++ b/openage/convert/processor/aoc/processor.py @@ -0,0 +1,17 @@ +# Copyright 2019-2019 the openage authors. See copying.md for legal info. + + +def convert(): + pass + + +def pre_processor(): + pass + + +def processor(): + pass + + +def post_processor(): + pass