From a6cfe7d4e71e572434970c3f504e56e64ffc15c6 Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Fri, 22 Sep 2023 22:38:35 +0200 Subject: [PATCH 4/4] [WIP] generate_enums.py draft; see osdn #48702 --- common/terrain.h | 117 +------ gen_headers/generate_enums.py | 572 ++++++++++++++++++++++++++++++++ gen_headers/terrain_enums.def | 84 +++++ gen_headers/terrain_enums_gen.h | 93 ++++++ meson.build | 2 +- 5 files changed, 755 insertions(+), 113 deletions(-) create mode 100644 gen_headers/generate_enums.py create mode 100644 gen_headers/terrain_enums.def create mode 100644 gen_headers/terrain_enums_gen.h diff --git a/common/terrain.h b/common/terrain.h index c505ab21d4..3b50862092 100644 --- a/common/terrain.h +++ b/common/terrain.h @@ -26,6 +26,11 @@ extern "C" { #include "name_translation.h" #include "unittype.h" +/* gen_headers */ +#include "terrain_enums_gen.h" + +#define MAX_NUM_USER_TER_FLAGS (TER_USER_LAST - TER_USER_1 + 1) + struct base_type; struct strvec; /* Actually defined in "utility/string_vector.h". */ struct rgbcolor; @@ -63,118 +68,6 @@ struct resource_type { * Used in the network protocol. */ #define MAX_NUM_TERRAINS (96) -/* Used in the network protocol. */ -#define SPECENUM_NAME terrain_class -#define SPECENUM_VALUE0 TC_LAND -/* TRANS: terrain class: used adjectivally */ -#define SPECENUM_VALUE0NAME N_("Land") -#define SPECENUM_VALUE1 TC_OCEAN -/* TRANS: terrain class: used adjectivally */ -#define SPECENUM_VALUE1NAME N_("Oceanic") -#define SPECENUM_COUNT TC_COUNT -#include "specenum_gen.h" - -/* Types of alterations available to terrain. - * This enum is only used in the effects system; the relevant information - * is encoded in other members of the terrain structure. - * - * Used in the network protocol. */ -#define SPECENUM_NAME terrain_alteration -/* Can build irrigation without changing terrain */ -#define SPECENUM_VALUE0 TA_CAN_IRRIGATE -/* TRANS: this and following strings may rarely be presented to the player - * in ruleset help text, to denote the set of terrains which can be altered - * in a particular way */ -#define SPECENUM_VALUE0NAME N_("CanIrrigate") -/* Can build mine without changing terrain */ -#define SPECENUM_VALUE1 TA_CAN_MINE -#define SPECENUM_VALUE1NAME N_("CanMine") -/* Can build roads and/or railroads */ -#define SPECENUM_VALUE2 TA_CAN_ROAD -#define SPECENUM_VALUE2NAME N_("CanRoad") -/* Can build military base */ -#define SPECENUM_VALUE3 TA_CAN_BASE -#define SPECENUM_VALUE3NAME N_("CanBase") -/* Can place extras with infrapoints */ -#define SPECENUM_VALUE4 TA_CAN_PLACE -#define SPECENUM_VALUE4NAME N_("CanPlace") -#define SPECENUM_COUNT TA_COUNT -#include "specenum_gen.h" - -/* Used in the network protocol. */ -#define SPECENUM_NAME terrain_flag_id -/* No barbarians summoned on this terrain. */ -#define SPECENUM_VALUE0 TER_NO_BARBS -/* TRANS: this and following strings are 'terrain flags', which may rarely - * be presented to the player in ruleset help text */ -#define SPECENUM_VALUE0NAME N_("NoBarbs") -/* No cities on this terrain. */ -#define SPECENUM_VALUE1 TER_NO_CITIES -#define SPECENUM_VALUE1NAME N_("NoCities") -/* Players will start on this terrain type. */ -#define SPECENUM_VALUE2 TER_STARTER -#define SPECENUM_VALUE2NAME N_("Starter") -/* Terrains with this type can have road with "River" flag on them. */ -#define SPECENUM_VALUE3 TER_CAN_HAVE_RIVER -#define SPECENUM_VALUE3NAME N_("CanHaveRiver") -/*this tile is not safe as coast, (all ocean / ice) */ -#define SPECENUM_VALUE4 TER_UNSAFE_COAST -#define SPECENUM_VALUE4NAME N_("UnsafeCoast") -/* Fresh water terrain */ -#define SPECENUM_VALUE5 TER_FRESHWATER -#define SPECENUM_VALUE5NAME N_("FreshWater") -/* Map generator does not place this terrain */ -#define SPECENUM_VALUE6 TER_NOT_GENERATED -#define SPECENUM_VALUE6NAME N_("NotGenerated") -/* Units on this terrain are not generating or subject to zoc */ -#define SPECENUM_VALUE7 TER_NO_ZOC -#define SPECENUM_VALUE7NAME N_("NoZoc") -/* Borders on this terrain are not blocking unit movement */ -#define SPECENUM_VALUE8 TER_ENTER_BORDERS -#define SPECENUM_VALUE8NAME N_("EnterBorders") -/* Ice-covered terrain (affects minimap) */ -#define SPECENUM_VALUE9 TER_FROZEN -#define SPECENUM_VALUE9NAME N_("Frozen") -#define SPECENUM_VALUE10 TER_USER_1 -#define SPECENUM_VALUE11 TER_USER_2 -#define SPECENUM_VALUE12 TER_USER_3 -#define SPECENUM_VALUE13 TER_USER_4 -#define SPECENUM_VALUE14 TER_USER_5 -#define SPECENUM_VALUE15 TER_USER_6 -#define SPECENUM_VALUE16 TER_USER_7 -#define SPECENUM_VALUE17 TER_USER_8 -#define SPECENUM_VALUE18 TER_USER_9 -#define SPECENUM_VALUE19 TER_USER_LAST -#define SPECENUM_NAMEOVERRIDE -#define SPECENUM_BITVECTOR bv_terrain_flags -#include "specenum_gen.h" - -#define MAX_NUM_USER_TER_FLAGS (TER_USER_LAST - TER_USER_1 + 1) - -#define SPECENUM_NAME mapgen_terrain_property -#define SPECENUM_VALUE0 MG_MOUNTAINOUS -#define SPECENUM_VALUE0NAME "mountainous" -#define SPECENUM_VALUE1 MG_GREEN -#define SPECENUM_VALUE1NAME "green" -#define SPECENUM_VALUE2 MG_FOLIAGE -#define SPECENUM_VALUE2NAME "foliage" -#define SPECENUM_VALUE3 MG_TROPICAL -#define SPECENUM_VALUE3NAME "tropical" -#define SPECENUM_VALUE4 MG_TEMPERATE -#define SPECENUM_VALUE4NAME "temperate" -#define SPECENUM_VALUE5 MG_COLD -#define SPECENUM_VALUE5NAME "cold" -#define SPECENUM_VALUE6 MG_FROZEN -#define SPECENUM_VALUE6NAME "frozen" -#define SPECENUM_VALUE7 MG_WET -#define SPECENUM_VALUE7NAME "wet" -#define SPECENUM_VALUE8 MG_DRY -#define SPECENUM_VALUE8NAME "dry" -#define SPECENUM_VALUE9 MG_OCEAN_DEPTH -#define SPECENUM_VALUE9NAME "ocean_depth" -#define SPECENUM_COUNT MG_COUNT -#include "specenum_gen.h" - /* * This struct gives data about each terrain type. There are many ways * it could be extended. diff --git a/gen_headers/generate_enums.py b/gen_headers/generate_enums.py new file mode 100644 index 0000000000..7dd53cec19 --- /dev/null +++ b/gen_headers/generate_enums.py @@ -0,0 +1,572 @@ +#!/usr/bin/env python3 + +# +# Freeciv - Copyright (C) 2023 +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +# This script runs under Python 3.6 and up. Please leave it so. +# It might also run under older versions, but no such guarantees are made. + +"""generate_enums.py - code generation script for Freeciv specenums + +This script takes enum definition files and turns them into a C header +defining those enums using specenum_gen.h, which is in turn generated by +utility/generate_specenum.py - the plan is to eventually, once all current +uses of specenum_gen.h have been migrated to this script, fold both scripts +into one. If you're reading this years into the future, it's hopefully +because you're looking through the version history, and *not* because this +convoluted system is still in place. + +See specenum_gen.h or generate_specenum.py for what exactly the various +options mean. + +# Definition file syntax + +Like packets.def, the enum defs can use +# Python-style EOL comments, +// C++-style EOL comments, +/* and C-style comments, + * which can span multiple lines. */ + +Each definition file consists of zero or more enum definitions, which take +the following form, where are placeholders: + +enum ; + + +# ... +end + +The are a comma-separated list of options, possibly with +extra arguments to them. The following options are currently supported: +- count() + Defines SPECENUM_COUNT; defining SPECENUM_COUNTNAME is not supported +- name-override + Defines SPECENUM_NAMEOVERRIDE +- bitvector() + Defines SPECENUM_BITVECTOR +Other specenum_gen.h features will be implemented later.""" + + +import re +import argparse +import sys +from pathlib import Path +from contextlib import contextmanager, ExitStack +from functools import partial +from itertools import chain, combinations, takewhile, zip_longest +from enum import Enum +from abc import ABC, abstractmethod + +try: + from functools import cache +except ImportError: + from functools import lru_cache + cache = lru_cache(None) + del lru_cache + +import typing +T_co = typing.TypeVar("T_co", covariant = True) + + +###################### Parsing Command Line Arguments ###################### + +def file_path(s: "str | Path") -> Path: + """Parse the given path and check basic validity.""" + path = Path(s) + + if path.is_reserved() or not path.name: + raise ValueError(f"not a valid file path: {s!r}") + if path.exists() and not path.is_file(): + raise ValueError(f"not a file: {s!r}") + + return path + + +class ScriptConfig: + """Contains configuration info for the script's execution, along with + functions closely tied to that configuration""" + + def_paths: "list[Path]" + """Paths to definition files, in load order""" + out_path: Path + """Path to the output file""" + + verbose: bool + """Whether to enable verbose logging""" + lazy_overwrite: bool + """Whether to lazily overwrite output files""" + + @staticmethod + def get_argparser() -> argparse.ArgumentParser: + """Construct an argument parser for a packet generation script""" + parser = argparse.ArgumentParser( + description = "Generate specenum header files", + add_help = False, # we'll add a help option explicitly + ) + + # Argument groups + # Note the order: + # We want the path arguments to show up *first* in the help text + + paths = parser.add_argument_group( + "Input and output paths", + "The following parameters decide which files to read and write.", + ) + + script = parser.add_argument_group( + "Script configuration", + "The following parameters change how the script operates.", + ) + + # Individual arguments + # Note the order: + # We want the path arguments to show up *last* in the usage summary + + script.add_argument("-h", "--help", action = "help", + help = "show this help message and exit") + + script.add_argument("-v", "--verbose", action = "store_true", + help = "enable log messages during code generation") + + script.add_argument("--lazy-overwrite", action = "store_true", + help = "only overwrite output files when their" + " contents actually changed") + + paths.add_argument("out_path", type = file_path, + help = "path to write the header file to") + paths.add_argument("def_paths", metavar = "def_path", + nargs = "+", type = file_path, + help = "paths to your enums.def files") + + return parser + + def __init__(self, args: "typing.Sequence[str] | None" = None): + __class__.get_argparser().parse_args(args, namespace = self) + + def log_verbose(self, *args): + """Print the given arguments iff verbose logging is enabled""" + if self.verbose: + print(*args) + + @property + def _root_path(self) -> "Path | None": + """Root Freeciv path, if we can find it.""" + path = Path(__file__).absolute() + root = path.parent.parent + if path != root / "gen_headers" / "generate_enums.py": + self.log_verbose("Warning: couldn't find Freeciv root path") + return None + return root + + def _relative_path(self, path: Path) -> Path: + """Find the relative path from the Freeciv root to the given path. + Return the path unmodified if it's outside the Freeciv root, or if + the Freeciv root could not be found.""" + root = self._root_path + if root is not None: + try: + return path.absolute().relative_to(root) + except ValueError: + self.log_verbose(f"Warning: path {path} outside of Freeciv root") + return path + + @property + def _script_path(self) -> Path: + """Relative path of the executed script. Under normal circumstances, + this will be common/generate_packets.py, but it may differ when this + module is imported from another script.""" + return self._relative_path(Path(sys.argv[0])) + + def _write_disclaimer(self, f: typing.TextIO): + f.write(f"""\ + /************************************************************************** + * THIS FILE WAS GENERATED * + * Script: {self._script_path!s:63} * +""") + + for path in self.def_paths: + f.write(f"""\ + * Input: {self._relative_path(path)!s:63} * +""") + + f.write("""\ + * DO NOT CHANGE THIS FILE * + **************************************************************************/ + +""") + + @contextmanager + def _wrap_header(self, file: typing.TextIO, header_name: str) -> typing.Iterator[None]: + """Add multiple inclusion protection to the given file""" + name = f"FC__{header_name.upper()}_H" + file.write(f"""\ +#ifndef {name} +#define {name} + +""") + + yield + + file.write(f"""\ + +#endif /* {name} */ +""") + + @contextmanager + def open_write(self, path: "str | Path") -> typing.Iterator[typing.TextIO]: + """Open a file for writing, write a disclaimer and add multiple + inclusion protection. + + If enabled, lazily overwrites the given file.""" + path = Path(path) # no-op if path is already a Path object + self.log_verbose(f"writing {path}") + + wrap_header = re.sub(r"[^\w]+", "_", path.name.split(".")[0]).upper() + + with ExitStack() as stack: + if self.lazy_overwrite: + file = stack.enter_context(self.lazy_overwrite_open(path)) + else: + file = stack.enter_context(path.open("w")) + + self._write_disclaimer(file) + stack.enter_context(self._wrap_header(file, wrap_header)) + + yield file + self.log_verbose(f"done writing {path}") + + @contextmanager + def lazy_overwrite_open(self, path: "str | Path", suffix: str = ".tmp") -> typing.Iterator[typing.TextIO]: + """Open a file for writing, but only actually overwrite it if the new + content differs from the old content. + + This creates a temporary file by appending the given suffix to the given + file path. In the event of an error, this temporary file might remain in + the target file's directory.""" + + path = Path(path) + tmp_path = path.with_name(path.name + suffix) + + # if tmp_path already exists, assume it's left over from a previous, + # failed run and can be overwritten without trouble + self.log_verbose(f"lazy: using {tmp_path}") + with tmp_path.open("w") as file: + yield file + + if path.exists() and files_equal(tmp_path, path): + self.log_verbose("lazy: no change, deleting...") + tmp_path.unlink() + else: + self.log_verbose("lazy: content changed, replacing...") + tmp_path.replace(path) + + +################### General helper functions and classes ################### + +def files_equal(path_a: "str | Path", path_b: "str | Path") -> bool: + """Return whether the contents of two text files are identical""" + with Path(path_a).open() as file_a, Path(path_b).open() as file_b: + return all(a == b for a, b in zip_longest(file_a, file_b)) + + +class EnumValue: + """Represents a single specenum constant, including identifier, name, + and potential other properties.""" + + LINE_PATTERN = re.compile(r"^\s*(\w+)(?:\s+(.+?))?\s*$") + """Matches an enum value definition. + + Groups: + - identifier + - (optional) name""" + + identifier: str + """The identifier (SPECENUM_VALUEx) for this constant""" + + name: "str | None" + """The name (SPECENUM_VALUExNAME) for this constant""" + + @classmethod + def parse(cls, line: str) -> "EnumValue": + """Parse a single line defining an enum value""" + mo = cls.LINE_PATTERN.fullmatch(line) + if mo is None: + raise ValueError(f"invalid enum value definition: {line!r}") + return cls(mo.group(1), mo.group(2)) + + def __init__(self, identifier: str, name: "str | None"): + self.identifier = identifier + self.name = name + + def code_parts(self, index: int) -> typing.Iterable[str]: + """Yield code defining this enum""" + yield f"""\ +#define SPECENUM_VALUE{index} {self.identifier} +""" + if self.name is not None: + yield f"""\ +#define SPECENUM_VALUE{index}NAME {self.name} +""" + + +# NB: avoid confusion with Python's Enum class +class Specenum: + """Represents a single enum definition (i.e. a single use of specenum_gen.h)""" + + OPTION_PATTERN = re.compile(r"^\s*([\w-]+)\s*(?:\(\s*(\w*)\s*\)\s*)?$") + """Matches a single enum option. + + Groups: + - the option name + - (optional) the option argument""" + + name: str + """The SPECENUM_NAME of this enum""" + + count: "str | None" = None + """The SPECENUM_COUNT identifier, if given""" + + name_override: bool = False + """Whether to request name override calls""" + + bitvector: "str | None" = None + """The SPECENUM_BITVECTOR name, if given""" + + values: "list[EnumValue]" + """The values of this enum""" + + def __init__(self, name: str, options_text: str, lines: typing.Iterable[str]): + self.name = name + + for option_text in options_text.split(","): + mo = __class__.OPTION_PATTERN.fullmatch(option_text) + if mo is None: + raise ValueError(f"malformed option for enum {self.name}: {option_text.strip()!r}") + + option: str = mo.group(1) + arg: "str | None" = mo.group(2) + + if option == "count": + if arg is None: + raise ValueError(f"option {option!r} for enum {self.name} requires an argument") + self.count = arg + elif option == "name-override": + if arg is not None: + raise ValueError(f"option {option!r} for enum {self.name} does not support an argument") + self.name_override = True + elif option == "bitvector": + if arg is None: + raise ValueError(f"option {option!r} for enum {self.name} requires an argument") + self.bitvector = arg + else: + raise ValueError(f"unrecognized option {option!r} for enum {self.name}") + + self.values = [EnumValue.parse(line) for line in lines] + + def code_parts(self) -> typing.Iterable[str]: + """Yield code defining this enum""" + yield f"""\ +#define SPECENUM_NAME {self.name} +""" + for i, value in enumerate(self.values): + yield from value.code_parts(i) + + if self.count is not None: + yield f"""\ +#define SPECENUM_COUNT {self.count} +""" + if self.name_override: + yield f"""\ +#define SPECENUM_NAMEOVERRIDE +""" + if self.bitvector is not None: + yield f"""\ +#define SPECENUM_BITVECTOR {self.bitvector} +""" + yield f"""\ +#include "specenum_gen.h" +""" + + +class EnumsDefinition(typing.Iterable[Specenum]): + """Represents an entire enums definition file""" + + COMMENT_START_PATTERN = re.compile(r""" + ^\s* # strip initial whitespace + (.*?) # actual content; note the reluctant quantifier + \s* # note: this can cause quadratic backtracking + (?: # match a potential comment + (?: # EOL comment (or just EOL) + (?: + (?:\#|//) # opening # or // + .* + )? + ) | (?: # block comment ~> capture remaining text + /\* # opening /* + [^*]* # text that definitely can't end the block comment + (.*) # remaining text, might contain a closing */ + ) + ) + (?:\n)? # optional newline in case those aren't stripped + $ + """, re.VERBOSE) + """Used to clean lines when not starting inside a block comment. Finds + the start of a block comment, if it exists. + + Groups: + - Actual content before any comment starts; stripped. + - Remaining text after the start of a block comment. Not present if no + block comment starts on this line.""" + + COMMENT_END_PATTERN = re.compile(r""" + ^ + .*? # comment; note the reluctant quantifier + (?: # end of block comment ~> capture remaining text + \*/ # closing */ + \s* # strip whitespace after comment + (.*) # remaining text + )? + (?:\n)? # optional newline in case those aren't stripped + $ + """, re.VERBOSE) + """Used to clean lines when starting inside a block comment. Finds the + end of a block comment, if it exists. + + Groups: + - Remaining text after the end of the block comment; lstripped. Not + present if the block comment doesn't end on this line.""" + + ENUM_HEADER_PATTERN = re.compile(r"^\s*enum\s+(\w+)\s*;\s*(.*?)\s*$") + """Matches the header line of an enum definition + + Groups: + - enum name + - enum options text""" + + ENUM_END_PATTERN = re.compile(r"^\s*end\s*$") + """Matches the "end" line terminating an enum definition""" + + cfg: ScriptConfig + """Configuration used for code generated from this definition""" + + enums: "list[Specenum]" + """List of all defined enums, in order of definition""" + + enums_by_name: "dict[str, Specenum]" + """Maps enum names to their enum definition""" + + @classmethod + def _clean_lines(cls, lines: typing.Iterable[str]) -> typing.Iterator[str]: + """Strip comments and leading/trailing whitespace from the given + lines. If a block comment starts in one line and ends in another, + the remaining parts are joined together and yielded as one line.""" + inside_comment = False + parts = [] + + for line in lines: + while line: + if inside_comment: + # currently inside a block comment ~> look for */ + mo = cls.COMMENT_END_PATTERN.fullmatch(line) + assert mo, repr(line) + # If the group wasn't captured (None), we haven't found + # a */ to end our comment ~> still inside_comment + # Otherwise, group captured remaining line content + line, = mo.groups(None) + inside_comment = line is None + else: + mo = cls.COMMENT_START_PATTERN.fullmatch(line) + assert mo, repr(line) + # If the second group wasn't captured (None), there is + # no /* to start a block comment ~> not inside_comment + part, line = mo.groups(None) + inside_comment = line is not None + if part: parts.append(part) + + if (not inside_comment) and parts: + # when ending a line outside a block comment, yield what + # we've accumulated + yield " ".join(parts) + parts.clear() + + if inside_comment: + raise ValueError("EOF while scanning block comment") + + def parse_lines(self, lines: typing.Iterable[str]): + """Parse the given lines as type and packet definitions.""" + self.parse_clean_lines(self._clean_lines(lines)) + + def parse_clean_lines(self, lines: typing.Iterable[str]): + """Parse the given lines as specenum definitions. Comments + and blank lines must already be removed beforehand.""" + # hold on to the iterator itself + lines_iter = iter(lines) + for line in lines_iter: + mo = self.ENUM_HEADER_PATTERN.fullmatch(line) + if mo is not None: + enum_name, options_text = mo.groups("") + + if enum_name in self.enums_by_name: + raise ValueError("Duplicate enum name: " + enum_name) + + enum = Specenum( + enum_name, options_text, + takewhile( + lambda line: self.ENUM_END_PATTERN.fullmatch(line) is None, + lines_iter, # advance the iterator used by this for loop + ), + ) + + self.enums.append(enum) + self.enums_by_name[enum_name] = enum + continue + + raise ValueError("Unexpected line: " + line) + + def __init__(self, cfg: ScriptConfig): + self.cfg = cfg + self.enums = [] + self.enums_by_name = {} + + def __iter__(self) -> typing.Iterator[Specenum]: + return iter(self.enums) + + +########################### Writing output files ########################### + +def write_header(path: "str | Path | None", enums: EnumsDefinition): + """Write a header with the defined enums to the given path""" + if path is None: + return + with enums.cfg.open_write(path) as output_h: + for specenum in enums: + output_h.write("\n") + output_h.writelines(specenum.code_parts()) + + +def main(raw_args: "typing.Sequence[str] | None" = None): + """Main function. Read the given arguments, or the command line + arguments if raw_args is not given, and run the specenum code generation + script accordingly.""" + script_args = ScriptConfig(raw_args) + + enums = EnumsDefinition(script_args) + for path in script_args.def_paths: + with path.open() as input_file: + enums.parse_lines(input_file) + + write_header(script_args.out_path, enums) + + +if __name__ == "__main__": + main() diff --git a/gen_headers/terrain_enums.def b/gen_headers/terrain_enums.def new file mode 100644 index 0000000000..df40825227 --- /dev/null +++ b/gen_headers/terrain_enums.def @@ -0,0 +1,84 @@ +# Specenum definitions for common/terrain.h +# See gen_headers/generate_enums.py for syntax + +# When modifying this, remember to regenerate the header. +# From the gen_headers directory, this can be done with +# $ ./generate_enums.py terrain_enums_gen.h terrain_enums.def + +/* Used in the network protocol. */ +enum terrain_class; count(TC_COUNT) + /* TRANS: terrain class: used adjectivally */ + TC_LAND N_("Land") + /* TRANS: terrain class: used adjectivally */ + TC_OCEAN N_("Oceanic") +end + +/* Types of alterations available to terrain. + * This enum is only used in the effects system; the relevant information + * is encoded in other members of the terrain structure. + * + * Used in the network protocol. */ +enum terrain_alteration; count(TA_COUNT) + /* Can build irrigation without changing terrain */ + /* TRANS: this and following strings may rarely be presented to the player + * in ruleset help text, to denote the set of terrains which can be altered + * in a particular way */ + TA_CAN_IRRIGATE N_("CanIrrigate") + /* Can build mine without changing terrain */ + TA_CAN_MINE N_("CanMine") + /* Can build roads and/or railroads */ + TA_CAN_ROAD N_("CanRoad") + /* Can build military base */ + TA_CAN_BASE N_("CanBase") + /* Can place extras with infrapoints */ + TA_CAN_PLACE N_("CanPlace") +end + +/* Used in the network protocol. */ +enum terrain_flag_id; name-override, bitvector(bv_terrain_flags) + /* No barbarians summoned on this terrain. */ + /* TRANS: this and following strings are 'terrain flags', which may rarely + * be presented to the player in ruleset help text */ + TER_NO_BARBS N_("NoBarbs") + /* No cities on this terrain. */ + TER_NO_CITIES N_("NoCities") + /* Players will start on this terrain type. */ + TER_STARTER N_("Starter") + /* Terrains with this type can have road with "River" flag on them. */ + TER_CAN_HAVE_RIVER N_("CanHaveRiver") + /* this tile is not safe as coast, (all ocean / ice) */ + TER_UNSAFE_COAST N_("UnsafeCoast") + /* Fresh water terrain */ + TER_FRESHWATER N_("FreshWater") + /* Map generator does not place this terrain */ + TER_NOT_GENERATED N_("NotGenerated") + /* Units on this terrain are not generating or subject to zoc */ + TER_NO_ZOC N_("NoZoc") + /* Borders on this terrain are not blocking unit movement */ + TER_ENTER_BORDERS N_("EnterBorders") + /* Ice-covered terrain (affects minimap) */ + TER_FROZEN N_("Frozen") + TER_USER_1 + TER_USER_2 + TER_USER_3 + TER_USER_4 + TER_USER_5 + TER_USER_6 + TER_USER_7 + TER_USER_8 + TER_USER_9 + TER_USER_LAST +end + +enum mapgen_terrain_property; count(MG_COUNT) + MG_MOUNTAINOUS "mountainous" + MG_GREEN "green" + MG_FOLIAGE "foliage" + MG_TROPICAL "tropical" + MG_TEMPERATE "temperate" + MG_COLD "cold" + MG_FROZEN "frozen" + MG_WET "wet" + MG_DRY "dry" + MG_OCEAN_DEPTH "ocean_depth" +end diff --git a/gen_headers/terrain_enums_gen.h b/gen_headers/terrain_enums_gen.h new file mode 100644 index 0000000000..cab5f26467 --- /dev/null +++ b/gen_headers/terrain_enums_gen.h @@ -0,0 +1,93 @@ + /************************************************************************** + * THIS FILE WAS GENERATED * + * Script: gen_headers/generate_enums.py * + * Input: gen_headers/terrain_enums.def * + * DO NOT CHANGE THIS FILE * + **************************************************************************/ + +#ifndef FC__TERRAIN_ENUMS_GEN_H +#define FC__TERRAIN_ENUMS_GEN_H + + +#define SPECENUM_NAME terrain_class +#define SPECENUM_VALUE0 TC_LAND +#define SPECENUM_VALUE0NAME N_("Land") +#define SPECENUM_VALUE1 TC_OCEAN +#define SPECENUM_VALUE1NAME N_("Oceanic") +#define SPECENUM_COUNT TC_COUNT +#include "specenum_gen.h" + +#define SPECENUM_NAME terrain_alteration +#define SPECENUM_VALUE0 TA_CAN_IRRIGATE +#define SPECENUM_VALUE0NAME N_("CanIrrigate") +#define SPECENUM_VALUE1 TA_CAN_MINE +#define SPECENUM_VALUE1NAME N_("CanMine") +#define SPECENUM_VALUE2 TA_CAN_ROAD +#define SPECENUM_VALUE2NAME N_("CanRoad") +#define SPECENUM_VALUE3 TA_CAN_BASE +#define SPECENUM_VALUE3NAME N_("CanBase") +#define SPECENUM_VALUE4 TA_CAN_PLACE +#define SPECENUM_VALUE4NAME N_("CanPlace") +#define SPECENUM_COUNT TA_COUNT +#include "specenum_gen.h" + +#define SPECENUM_NAME terrain_flag_id +#define SPECENUM_VALUE0 TER_NO_BARBS +#define SPECENUM_VALUE0NAME N_("NoBarbs") +#define SPECENUM_VALUE1 TER_NO_CITIES +#define SPECENUM_VALUE1NAME N_("NoCities") +#define SPECENUM_VALUE2 TER_STARTER +#define SPECENUM_VALUE2NAME N_("Starter") +#define SPECENUM_VALUE3 TER_CAN_HAVE_RIVER +#define SPECENUM_VALUE3NAME N_("CanHaveRiver") +#define SPECENUM_VALUE4 TER_UNSAFE_COAST +#define SPECENUM_VALUE4NAME N_("UnsafeCoast") +#define SPECENUM_VALUE5 TER_FRESHWATER +#define SPECENUM_VALUE5NAME N_("FreshWater") +#define SPECENUM_VALUE6 TER_NOT_GENERATED +#define SPECENUM_VALUE6NAME N_("NotGenerated") +#define SPECENUM_VALUE7 TER_NO_ZOC +#define SPECENUM_VALUE7NAME N_("NoZoc") +#define SPECENUM_VALUE8 TER_ENTER_BORDERS +#define SPECENUM_VALUE8NAME N_("EnterBorders") +#define SPECENUM_VALUE9 TER_FROZEN +#define SPECENUM_VALUE9NAME N_("Frozen") +#define SPECENUM_VALUE10 TER_USER_1 +#define SPECENUM_VALUE11 TER_USER_2 +#define SPECENUM_VALUE12 TER_USER_3 +#define SPECENUM_VALUE13 TER_USER_4 +#define SPECENUM_VALUE14 TER_USER_5 +#define SPECENUM_VALUE15 TER_USER_6 +#define SPECENUM_VALUE16 TER_USER_7 +#define SPECENUM_VALUE17 TER_USER_8 +#define SPECENUM_VALUE18 TER_USER_9 +#define SPECENUM_VALUE19 TER_USER_LAST +#define SPECENUM_NAMEOVERRIDE +#define SPECENUM_BITVECTOR bv_terrain_flags +#include "specenum_gen.h" + +#define SPECENUM_NAME mapgen_terrain_property +#define SPECENUM_VALUE0 MG_MOUNTAINOUS +#define SPECENUM_VALUE0NAME "mountainous" +#define SPECENUM_VALUE1 MG_GREEN +#define SPECENUM_VALUE1NAME "green" +#define SPECENUM_VALUE2 MG_FOLIAGE +#define SPECENUM_VALUE2NAME "foliage" +#define SPECENUM_VALUE3 MG_TROPICAL +#define SPECENUM_VALUE3NAME "tropical" +#define SPECENUM_VALUE4 MG_TEMPERATE +#define SPECENUM_VALUE4NAME "temperate" +#define SPECENUM_VALUE5 MG_COLD +#define SPECENUM_VALUE5NAME "cold" +#define SPECENUM_VALUE6 MG_FROZEN +#define SPECENUM_VALUE6NAME "frozen" +#define SPECENUM_VALUE7 MG_WET +#define SPECENUM_VALUE7NAME "wet" +#define SPECENUM_VALUE8 MG_DRY +#define SPECENUM_VALUE8NAME "dry" +#define SPECENUM_VALUE9 MG_OCEAN_DEPTH +#define SPECENUM_VALUE9NAME "ocean_depth" +#define SPECENUM_COUNT MG_COUNT +#include "specenum_gen.h" + +#endif /* FC__TERRAIN_ENUMS_GEN_H */ diff --git a/meson.build b/meson.build index 9ae2ac0d6c..47b9b08675 100644 --- a/meson.build +++ b/meson.build @@ -987,7 +987,7 @@ common_inc = include_directories(cross_inc_path, lua_inc_path, 'dependencies/luasql/src', 'dependencies/tinycthread', 'dependencies/tolua-5.2/include', 'dependencies/cvercmp', 'utility', 'common', 'common/networking', 'common/scriptcore', - 'common/aicore') + 'common/aicore', 'gen_headers') server_inc = [common_inc, include_directories('server', 'server/advisors', 'server/scripting', 'server/generator', 'server/savegame', -- 2.34.1