1# Copyright 2013-2017 the openage authors. See copying.md for legal info.
2
3# TODO pylint: disable=C,R
4
5from ..dataformat.exportable import Exportable
6from ..dataformat.members import SubdataMember, EnumLookupMember
7from ..dataformat.member_access import READ, READ_EXPORT
8
9
10class Effect(Exportable):
11    name_struct        = "tech_effect"
12    name_struct_file   = "tech"
13    struct_description = "applied effect for a research technology."
14
15    data_format = [
16        (READ, "type_id", EnumLookupMember(
17            raw_type = "int8_t",
18            type_name = "effect_apply_type",
19            lookup_dict = {
20                # unused assignage: a = -1, b = -1, c = -1, d = 0
21                -1: "DISABLED",
22                0: "ATTRIBUTE_ABSSET",    # if a != -1: a == unit_id, else b == unit_class_id; c = attribute_id, d = new_value
23                1: "RESOURCE_MODIFY",     # a == resource_id, if b == 0: then d = absval, else d = relval (for inc/dec)
24                2: "UNIT_ENABLED",        # a == unit_id, if b == 0: disable unit, else b == 1: enable unit
25                3: "UNIT_UPGRADE",        # a == old_unit_id, b == new_unit_id
26                4: "ATTRIBUTE_RELSET",    # if a != -1: unit_id, else b == unit_class_id; c=attribute_id, d=relval
27                5: "ATTRIBUTE_MUL",       # if a != -1: unit_id, else b == unit_class_id; c=attribute_id, d=factor
28                6: "RESOURCE_MUL",        # a == resource_id, d == factor
29
30                # may mean something different in aok:hd:
31                10: "TEAM_ATTRIBUTE_ABSSET",
32                11: "TEAM_RESOURCE_MODIFY",
33                12: "TEAM_UNIT_ENABLED",
34                13: "TEAM_UNIT_UPGRADE",
35                14: "TEAM_ATTRIBUTE_RELSET",
36                15: "TEAM_ATTRIBUTE_MUL",
37                16: "TEAM_RESOURCE_MUL",
38
39                # these are only used in technology trees, 103 even requires one
40                101: "TECHCOST_MODIFY",   # a == research_id, b == resource_id, if c == 0: d==absval else: d == relval
41                102: "TECH_TOGGLE",       # d == research_id
42                103: "TECH_TIME_MODIFY",  # a == research_id, if c == 0: d==absval else d==relval
43
44                # attribute_id:
45                # 0: hit points
46                # 1: line of sight
47                # 2: garrison capacity
48                # 3: unit size x
49                # 4: unit size y
50                # 5: movement speed
51                # 6: rotation speed
52                # 7: unknown
53                # 8: armor                           # real_val = val + (256 * armor_id)
54                # 9: attack                          # real_val = val + (256 * attack_id)
55                # 10: attack reloading time
56                # 11: accuracy percent
57                # 12: max range
58                # 13: working rate
59                # 14: resource carriage
60                # 15: default armor
61                # 16: projectile unit
62                # 17: upgrade graphic (icon), graphics angle
63                # 18: terrain restriction to multiply damage received (always sets)
64                # 19: intelligent projectile aim 1=on, 0=off
65                # 20: minimum range
66                # 21: first resource storage
67                # 22: blast width (area damage)
68                # 23: search radius
69                # 80: boarding energy reload speed
70                # 100: resource cost
71                # 101: creation time
72                # 102: number of garrison arrows
73                # 103: food cost
74                # 104: wood cost
75                # 105: stone cost
76                # 106: gold cost
77                # 107: max total projectiles
78                # 108: garrison healing rate
79                # 109: regeneration rate
80            },
81        )),
82        (READ, "unit",          "int16_t"),       # == a
83        (READ, "unit_class_id", "int16_t"),       # == b
84        (READ, "attribute_id",  "int16_t"),       # == c
85        (READ, "amount",        "float"),         # == d
86    ]
87
88
89class Tech(Exportable):  # also called techage in some other tools
90    name_struct        = "tech"
91    name_struct_file   = "tech"
92    struct_description = "a technology definition."
93
94    data_format = [
95        (READ, "name", "char[31]"),                  # always CHUN4 (change unit 4-arg)
96        (READ, "effect_count", "uint16_t"),
97        (READ, "effects", SubdataMember(
98            ref_type=Effect,
99            length="effect_count",
100        )),
101    ]
102
103
104# TODO: add common tech class
105
106class Mode(Exportable):
107    name_struct        = "age_tech_tree"
108    name_struct_file   = "tech"
109    struct_description = "items available when this age was reached."
110
111    data_format = [
112        (READ, "mode", EnumLookupMember(                 # mode for unit_or_research0
113            raw_type = "int32_t",
114            type_name = "building_connection_mode",
115            lookup_dict = {
116                0: "NOTHING",
117                1: "BUILDING",
118                2: "UNIT",
119                3: "RESEARCH",
120            }
121        )),
122    ]
123
124
125class AgeTechTree(Exportable):
126    name_struct        = "age_tech_tree"
127    name_struct_file   = "tech"
128    struct_description = "items available when this age was reached."
129
130    data_format = [
131        (READ, "total_unit_tech_groups", "int32_t"),
132        (READ, "id", "int32_t"),
133        # 0=generic
134        # 1=TODO
135        # 2=default
136        # 3=marks as not available
137        # 4=upgrading, constructing, creating
138        # 5=research completed, building built
139        (READ, "status", "int8_t"),
140    ]
141
142    # TODO: Enable conversion for AOE1; replace 6 values below
143    # ===========================================================================
144    # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions:
145    #     data_format.extend([
146    #         (READ, "building_count", "int8_t"),
147    #         (READ, "buildings", "int32_t[building_count]"),
148    #         (READ, "unit_count", "int8_t"),
149    #         (READ, "units", "int32_t[unit_count]"),
150    #         (READ, "research_count", "int8_t"),
151    #         (READ, "researches", "int32_t[research_count]"),
152    #     ])
153    # else:
154    #     data_format.extend([
155    #         (READ, "building_count", "int8_t"),
156    #         (READ, "buildings", "int32_t[40]"),
157    #         (READ, "unit_count", "int8_t"),
158    #         (READ, "units", "int32_t[40]"),
159    #         (READ, "research_count", "int8_t"),
160    #         (READ, "researches", "int32_t[40]"),
161    #     ])
162    # ===========================================================================
163    data_format.extend([
164        (READ, "building_count", "int8_t"),
165        (READ, "buildings", "int32_t[building_count]"),
166        (READ, "unit_count", "int8_t"),
167        (READ, "units", "int32_t[unit_count]"),
168        (READ, "research_count", "int8_t"),
169        (READ, "researches", "int32_t[research_count]"),
170    ])
171    # ===========================================================================
172
173    data_format.extend([
174        (READ, "slots_used", "int32_t"),
175        (READ, "unit_researches", "int32_t[10]"),
176        (READ, "modes", SubdataMember(
177            ref_type=Mode,
178            length=10,  # number of tile types * 12
179        )),
180
181        (READ, "building_level_count", "int8_t"),
182    ])
183
184    # TODO: Enable conversion for SWGB; replace "buildings_per_zone", "group_length_per_zone"
185    # ===========================================================================
186    # if (GameVersion.swgb_10 or GameVersion.swgb_cc) in game_versions:
187    #     data_format.extend([
188    #         (READ, "buildings_per_zone", "int8_t[20]"),
189    #         (READ, "group_length_per_zone", "int8_t[20]"),
190    #     ])
191    # else:
192    #     data_format.extend([
193    #         (READ, "buildings_per_zone", "int8_t[10]"),
194    #         (READ, "group_length_per_zone", "int8_t[10]"),
195    #     ])
196    # ===========================================================================
197    data_format.extend([
198        (READ, "buildings_per_zone", "int8_t[10]"),
199        (READ, "group_length_per_zone", "int8_t[10]"),
200    ])
201
202    data_format.append((READ, "max_age_length", "int8_t"))
203
204
205class BuildingConnection(Exportable):
206    name_struct        = "building_connection"
207    name_struct_file   = "tech"
208    struct_description = "new available buildings/units/researches when this building was created."
209
210    data_format = [
211        (READ_EXPORT, "id", "int32_t"),                   # unit id of the current building
212        # 0=generic
213        # 1=TODO
214        # 2=default
215        # 3=marks as not available
216        # 4=upgrading, constructing, creating
217        # 5=research completed, building built
218        (READ, "status", "int8_t"),                       # maybe always 2 because we got 2 of them hardcoded below (unit_or_research, mode)
219    ]
220
221    # TODO: Enable conversion for AOE1; replace 6 values below
222    # ===========================================================================
223    # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions:
224    #     data_format.extend([
225    #         (READ_EXPORT, "building_count", "int8_t"),
226    #         (READ, "buildings", "int32_t[building_count]"),   # new buildings available when this building was created
227    #         (READ_EXPORT, "unit_count", "int8_t"),
228    #         (READ, "units", "int32_t[unit_count]"),           # new units
229    #         (READ_EXPORT, "research_count", "int8_t"),
230    #         (READ, "researches", "int32_t[research_count]"),  # new researches
231    #     ])
232    # else:
233    #     data_format.extend([
234    #         (READ_EXPORT, "building_count", "int8_t"),
235    #         (READ, "buildings", "int32_t[40]"),
236    #         (READ_EXPORT, "unit_count", "int8_t"),
237    #         (READ, "units", "int32_t[40]"),
238    #         (READ_EXPORT, "research_count", "int8_t"),
239    #         (READ, "researches", "int32_t[40]"),
240    #     ])
241    # ===========================================================================
242    data_format.extend([
243        (READ_EXPORT, "building_count", "int8_t"),
244        (READ, "buildings", "int32_t[building_count]"),   # new buildings available when this building was created
245        (READ_EXPORT, "unit_count", "int8_t"),
246        (READ, "units", "int32_t[unit_count]"),           # new units
247        (READ_EXPORT, "research_count", "int8_t"),
248        (READ, "researches", "int32_t[research_count]"),  # new researches
249    ])
250    # ===========================================================================
251
252    data_format.extend([
253        (READ, "slots_used", "int32_t"),
254        (READ, "unit_researches", "int32_t[10]"),
255        (READ, "modes", SubdataMember(
256            ref_type=Mode,
257            length=10,  # number of tile types * 12
258        )),
259
260        (READ, "location_in_age", "int8_t"),              # minimum age, in which this building is available
261        (READ, "unit_techs_total", "int8_t[5]"),          # total techs for each age (5 ages, post-imp probably counts as one)
262        (READ, "unit_techs_first", "int8_t[5]"),
263        (READ_EXPORT, "line_mode", "int32_t"),            # 5: >=1 connections, 6: no connections
264        (READ, "enabled_by_research_id", "int32_t"),      # current building is unlocked by this research id, -1=no unlock needed
265    ])
266
267
268class UnitConnection(Exportable):
269    name_struct        = "unit_connection"
270    name_struct_file   = "tech"
271    struct_description = "unit updates to apply when activating the technology."
272
273    data_format = [
274        (READ, "id", "int32_t"),
275        # 0=generic
276        # 1=TODO
277        # 2=default
278        # 3=marks as not available
279        # 4=upgrading, constructing, creating
280        # 5=research completed, building built
281        (READ, "status", "int8_t"),                 # always 2: default
282        (READ, "upper_building", "int32_t"),        # building, where this unit is created
283
284        (READ, "slots_used", "int32_t"),
285        (READ, "unit_researches", "int32_t[10]"),
286        (READ, "modes", SubdataMember(
287            ref_type=Mode,
288            length=10,  # number of tile types * 12
289        )),
290
291        (READ, "vertical_lines", "int32_t"),
292    ]
293
294    # TODO: Enable conversion for AOE1; replace "unit_count", "units"
295    # ===========================================================================
296    # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions:
297    #     data_format.extend([
298    #         (READ, "unit_count", "int8_t"),
299    #         (READ, "units", "int32_t[unit_count]"),
300    #     ])
301    # else:
302    #     data_format.extend([
303    #         (READ, "unit_count", "int8_t"),
304    #         (READ, "units", "int32_t[40]"),
305    #     ])
306    # ===========================================================================
307    data_format.extend([
308        (READ, "unit_count", "int8_t"),
309        (READ, "units", "int32_t[unit_count]"),
310    ])
311
312    data_format.extend([
313        (READ, "location_in_age", "int32_t"),    # 0=hidden, 1=first, 2=second
314        (READ, "required_research", "int32_t"),  # min amount of researches to be discovered for this unit to be available
315        (READ, "line_mode", "int32_t"),          # 0=independent/new in its line, 3=depends on a previous research in its line
316        (READ, "enabling_research", "int32_t"),
317    ])
318
319
320class ResearchConnection(Exportable):
321    name_struct        = "research_connection"
322    name_struct_file   = "tech"
323    struct_description = "research updates to apply when activating the technology."
324
325    data_format = [
326        (READ, "id", "int32_t"),
327        # 0=generic
328        # 1=TODO
329        # 2=default
330        # 3=marks as not available
331        # 4=upgrading, constructing, creating
332        # 5=research completed, building built
333        (READ, "status", "int8_t"),
334        (READ, "upper_building", "int32_t"),
335    ]
336
337    # TODO: Enable conversion for AOE1; replace 6 values below
338    # ===========================================================================
339    # if (GameVersion.aoe_1 or GameVersion.aoe_ror) not in game_versions:
340    #     data_format.extend([
341    #         (READ, "building_count", "int8_t"),
342    #         (READ, "buildings", "int32_t[building_count]"),
343    #         (READ, "unit_count", "int8_t"),
344    #         (READ, "units", "int32_t[unit_count]"),
345    #         (READ, "research_count", "int8_t"),
346    #         (READ, "researches", "int32_t[research_count]"),
347    #     ])
348    # else:
349    #     data_format.extend([
350    #         (READ, "building_count", "int8_t"),
351    #         (READ, "buildings", "int32_t[40]"),
352    #         (READ, "unit_count", "int8_t"),
353    #         (READ, "units", "int32_t[40]"),
354    #         (READ, "research_count", "int8_t"),
355    #         (READ, "researches", "int32_t[40]"),
356    #     ])
357    # ===========================================================================
358    data_format.extend([
359        (READ, "building_count", "int8_t"),
360        (READ, "buildings", "int32_t[building_count]"),
361        (READ, "unit_count", "int8_t"),
362        (READ, "units", "int32_t[unit_count]"),
363        (READ, "research_count", "int8_t"),
364        (READ, "researches", "int32_t[research_count]"),
365    ])
366    # ===========================================================================
367
368    data_format.extend([
369        (READ, "slots_used", "int32_t"),
370        (READ, "unit_researches", "int32_t[10]"),
371        (READ, "modes", SubdataMember(
372            ref_type=Mode,
373            length=10,  # number of tile types * 12
374        )),
375
376        (READ, "vertical_line", "int32_t"),
377        (READ, "location_in_age", "int32_t"),    # 0=hidden, 1=first, 2=second
378        (READ, "line_mode", "int32_t"),          # 0=first age, else other ages.
379    ])
380