Exemplo n.º 1
0
    def init_position(self):

        def equivalent_type(t):
            tn = getattr(t, "type_name", "")
            if rules.get(self.race, tn):
                return self.world.unit_class(rules.get(self.race, tn)[0])
            return t

        self.resources = self.start[0][:]
        normalize_cost_or_resources(self.resources)
        self.gathered_resources = self.resources[:]
        for place, type_ in self.start[1]:
            type_ = equivalent_type(type_)
            if isinstance(type_, str) and type_[0:1] == "-":
                self.forbidden_techs.append(type_[1:])
            elif isinstance(type_, Upgrade):
                self.upgrades.append(type_.type_name) # XXX type_.upgrade_player(self)?
            else:
                place = self.world.grid[place]
                x, y = place.find_and_remove_meadow(type_)
                x, y = place.find_free_space(type_.airground_type, x, y)
                if x is not None:
                    type_(self, place, x, y)
        self.triggers = self.start[2]

        if rules.get(self.race, getattr(self, "AI_type", "")):
            self.set_ai(rules.get(self.race, self.AI_type)[0])
Exemplo n.º 2
0
    def init_position(self):

        def equivalent_type(t):
            tn = getattr(t, "type_name", "")
            if rules.get(self.faction, tn):
                return self.world.unit_class(rules.get(self.faction, tn)[0])
            return t

        self.resources = self.start[0][:]
        normalize_cost_or_resources(self.resources)
        self.gathered_resources = self.resources[:]
        for place, type_ in self.start[1]:
            type_ = equivalent_type(type_)
            if isinstance(type_, str) and type_[0:1] == "-":
                self.forbidden_techs.append(type_[1:])
            elif isinstance(type_, Upgrade):
                self.upgrades.append(type_.type_name) # XXX type_.upgrade_player(self)?
            else:
                place = self.world.grid[place]
                x, y, land = place.find_and_remove_meadow(type_)
                x, y = place.find_free_space(type_.airground_type, x, y)
                if x is not None:
                    unit = type_(self, place, x, y)
                    unit.building_land = land
                        
        self.triggers = self.start[2]

        if rules.get(self.faction, getattr(self, "AI_type", "")):
            self.set_ai(rules.get(self.faction, self.AI_type)[0])
Exemplo n.º 3
0
    def init_position(self):

        def equivalent_type(t):
            tn = getattr(t, "type_name", "")
            if rules.get(self.faction, tn):
                return self.world.unit_class(rules.get(self.faction, tn)[0])
            return t

        self.resources = self.start[0][:]
        normalize_cost_or_resources(self.resources)
        self.gathered_resources = self.resources[:]
        for place, type_ in self.start[1]:
            if self.world.must_apply_equivalent_type:
                type_ = equivalent_type(type_)
            if isinstance(type_, str) and type_[0:1] == "-":
                self.forbidden_techs.append(type_[1:])
            elif isinstance(type_, Upgrade):
                self.upgrades.append(type_.type_name) # type_.upgrade_player(self) would require the units already there
            elif not type_:
                warning("couldn't create an initial unit")
            else:
                place = self.world.grid[place]
                x, y, land = place.find_and_remove_meadow(type_)
                x, y = place.find_free_space(type_.airground_type, x, y)
                if x is not None:
                    unit = type_(self, place, x, y)
                    unit.building_land = land
                        
        self.triggers = self.start[2]

        if rules.get(self.faction, getattr(self, "AI_type", "")):
            self.set_ai(rules.get(self.faction, self.AI_type)[0])
Exemplo n.º 4
0
    def unit_class(self, s):
        """Get a class-like unit generator from its name.
        
        Example: unit_class("peasant") to get a peasant generator

        At the moment, unit_classes contains also: upgrades, abilities...
        """
        if not self.unit_classes.has_key(s):
            try:
                base = self.unit_base_classes[rules.get(s, "class")[0]]
            except:
                if rules.get(s, "class") != ["faction"]:
                    warning("no class defined for %s", s)
                self.unit_classes[s] = None
                return
            try:
                dct = rules.get_dict(s)
                t = Type(s, (base, ), dct)
                if base is Upgrade:
                    t = base(s, dct)  # factory-prototypes are only for units
                self.unit_classes[s] = t
            except:
                exception("problem with unit_class('%s')", s)
                self.unit_classes[s] = None
                return
        return self.unit_classes[s]
Exemplo n.º 5
0
    def unit_class(self, s):
        """Get a class-like unit generator from its name.
        
        Example: unit_class("peasant") to get a peasant generator

        At the moment, unit_classes contains also: upgrades, abilities...
        """
        if not self.unit_classes.has_key(s):
            try:
                base = self.unit_base_classes[rules.get(s, "class")[0]]
            except:
                if rules.get(s, "class") != ["race"]:
                    warning("no class defined for %s", s)
                self.unit_classes[s] = None
                return
            try:
                dct = rules.get_dict(s)
                t = Type(s, (base,), dct)
                if base is Upgrade:
                    t = base(s, dct) # factory-prototypes are only for units
                self.unit_classes[s] = t
            except:
                exception("problem with unit_class('%s')", s)
                self.unit_classes[s] = None
                return
        return self.unit_classes[s]
Exemplo n.º 6
0
 def build_or_train_or_upgradeto_or_summon(self, t, nb=1):
     if t.__class__ == str:
         t = self.world.unit_class(t)
     type = t.__name__
     makers = self.world.get_makers(type)
     if self._get(1, makers) and self._get_requirements(t):
         for maker in makers:
             # TODO: choose one without orders if possible
             if self.nb(maker):
                 break
         if type in self.world.unit_class(maker).can_upgrade_to:
             if self.nb(maker) >= nb:
                 if self.gather(t.cost, t.food_cost):
                     self.order(nb, maker, ["upgrade_to", type])
             else:
                 self._get(nb, maker)
         elif type in self.world.unit_class(maker).can_build:
             if not self.gather(t.cost, t.food_cost):
                 return
             if t.storable_resource_types:
                 meadow = self.choose(
                     Meadow, resource_type=t.storable_resource_types[0])
                 if meadow is None or meadow.place.shortest_path_distance_to(
                         self._builders_place(
                         )) > self.world.square_width * 3:
                     if self.nb(t):
                         return
                     else:
                         meadow = self.choose(
                             Meadow, starting_place=self._builders_place())
             else:
                 meadow = self.choose(Meadow,
                                      starting_place=self._builders_place())
             if meadow:
                 self.order(4,
                            maker, ["build", type, meadow.id],
                            requisition=True,
                            near=meadow)
         elif type in self.world.unit_class(maker).can_train:
             if (self.nb(Worker) and nb > self.nb(maker) * 3
                     and self.potential(t.cost) > self.nb(maker) * 100):
                 # additional production sites
                 self.build_or_train_or_upgradeto_or_summon(maker)
             if self.gather(t.cost, t.food_cost):
                 self.order(nb, maker, ["train", type])
         elif type in self.world.unit_class(maker).can_research:
             if self.gather(t.cost, t.food_cost):
                 self.order(1, maker, ["research", type])
         else:
             for ability in self.world.unit_class(maker).can_use:
                 effect = rules.get(ability, "effect")
                 if effect and "summon" in effect[:1] and type in effect:
                     if rules.get(ability, "effect_target") == ["ask"]:
                         self.order(1, maker,
                                    ["use", ability, self.units[0].id])
                         # TODO select best place
                     else:
                         self.order(1, maker, ["use", ability])
                     break
Exemplo n.º 7
0
 def _cataclysm_is_efficient(self, a, units):
     type_names = set(u.type_name for u in units)
     e = rules.get(a, "effect")
     if e[0] == "summon":
         for item in e[1:]:
             if rules.get(item, "harm_level"):
                 for t in type_names:
                     if self.world.can_harm(item, t):
                         return True
Exemplo n.º 8
0
    def __init__(self, prototype, player, place, x, y, o=90):
        if prototype is not None:
            prototype.init_dict(self)
        self.orders = []
        # transport data
        self.objects = []
        self.world = place.world # XXXXXXXXXX required by transport

        # set a player
        self.set_player(player)
        # stats "with a max"
        self.hp = self.hp_max
        # Start with mana_start
        if self.mana_start > 0:
            self.mana = self.mana_start
        else:
        # start with mana_max
            self.mana = self.mana_max

        # stat defined for the whole game
        self.minimal_damage = rules.get("parameters", "minimal_damage")
        if self.minimal_damage is None:
            self.minimal_damage = DEFAULT_MINIMAL_DAMAGE

        # move to initial place
        Entity.__init__(self, place, x, y, o)

        if self.decay:
            self.time_limit = self.world.time + self.decay
Exemplo n.º 9
0
    def __init__(self, prototype, player, place, x, y, o=90):
        if prototype is not None:
            prototype.init_dict(self)
        self.orders = []

        # attributes required by transports and shelters (inside)
        self.objects = []
        self.world = place.world
        self.neighbors = []
        self.title = []

        self.set_player(player)

        # stats "with a max"
        self.hp = self.hp_max
        if self.mana_start > 0:
            self.mana = self.mana_start
        else:
            self.mana = self.mana_max

        # stat defined for the whole game
        self.minimal_damage = rules.get("parameters", "minimal_damage")
        if self.minimal_damage is None:
            self.minimal_damage = int(.17 * PRECISION)

        # move to initial place
        Entity.__init__(self, place, x, y, o)
        self.position_to_hold = place  # defend the creation place

        if self.decay:
            self.time_limit = self.world.time + self.decay
Exemplo n.º 10
0
    def __init__(self, prototype, player, place, x, y, o=90):
        if prototype is not None:
            prototype.init_dict(self)
        self.orders = []
        # transport data
        self.objects = []
        self.world = place.world # XXXXXXXXXX required by transport

        # set a player
        self.set_player(player)

        # stats "with a max"
        self.hp = self.hp_max
        if self.mana_start > 0:
            self.mana = self.mana_start
        else:
            self.mana = self.mana_max

        # stat defined for the whole game
        self.minimal_damage = rules.get("parameters", "minimal_damage")
        if self.minimal_damage is None:
            self.minimal_damage = DEFAULT_MINIMAL_DAMAGE

        # move to initial place
        Entity.__init__(self, place, x, y, o)

        if self.decay:
            self.time_limit = self.world.time + self.decay
Exemplo n.º 11
0
 def can_make(uc, t):
     for a in ("can_build", "can_train", "can_upgrade_to", "can_research"):
         if t in getattr(uc, a, []):
             return True
     for ability in getattr(uc, "can_use", []):
         effect = rules.get(ability, "effect")
         if effect and "summon" in effect[:1] and t in effect:
             return True
Exemplo n.º 12
0
 def can_make(uc, t):
     for a in ("can_build", "can_train", "can_upgrade_to",
               "can_research"):
         if t in getattr(uc, a, []):
             return True
     for ability in getattr(uc, "can_use", []):
         effect = rules.get(ability, "effect")
         if effect and "summon" in effect[:1] and t in effect:
             return True
Exemplo n.º 13
0
 def _update_effect_users_and_workers(self):
     self._workers = []
     self._gathered_deposits = {}
     self._building_sites = []
     self._raise_dead_users = []
     self._teleportation_users = []
     self._cataclysm_users = []
     self._detector_users = []
     self._summon_users = []
     for u in self.units:
         if isinstance(u, Worker):
             self._workers.append(u)
             if u.orders and u.orders[0].keyword == "gather":
                 try:
                     self._gathered_deposits[u.orders[0].target] += 1
                 except:
                     self._gathered_deposits[u.orders[0].target] = 1
         elif isinstance(u, BuildingSite):
             self._building_sites.append(u)
         for a in u.can_use:
             if not UseOrder.is_allowed(u, a):
                 continue
             e = rules.get(a, "effect")
             if not e:
                 continue
             elif e[0] == "raise_dead":
                 self._raise_dead_users.append((u, a))
             elif e[0] == "teleportation":
                 self._teleportation_users.append((u, a))
             elif e[0] == "summon":
                 for item in e[1:]:
                     if rules.get(item, "harm_level"):
                         self._cataclysm_users.append((u, a))
                     if rules.get(item, "is_a_detector"):
                         self._detector_users.append((u, a))
                     if rules.get(item, "damage"):
                         self._summon_users.append((u, a))
Exemplo n.º 14
0
 def __init__(self, name, bases, dct):
     self.__name__ = name
     self.type_name = name
     self.cls = bases[0]
     if "cost" not in dct and hasattr(self.cls, "cost"):
         dct["cost"] = [0] * rules.get("parameters", "nb_of_resource_types")
     if "sight_range" in dct and dct["sight_range"] == 1 * PRECISION:
         dct["sight_range"] = 12 * PRECISION
         dct["bonus_height"] = 1
         info("in %s: replacing sight_range 1 with sight_range 12 and bonus_height 1", name)
     if "special_range" in dct:
         del dct["special_range"]
         dct["range"] = 12 * PRECISION
         dct["minimal_range"] = 4 * PRECISION 
         dct["is_ballistic"] = 1
         info("in %s: replacing special_range 1 with range 12, minimal_range 4 and is_ballistic 1", name)
     self.dct = dct
     self.init_dict(self)
Exemplo n.º 15
0
 def __init__(self, name, bases, dct):
     self.__name__ = name
     self.type_name = name
     self.cls = bases[0]
     if "cost" not in dct and hasattr(self.cls, "cost"):
         dct["cost"] = [0] * rules.get("parameters", "nb_of_resource_types")
     if "sight_range" in dct and dct["sight_range"] == 1 * PRECISION:
         dct["sight_range"] = 12 * PRECISION
         dct["bonus_height"] = 1
         info(
             "in %s: replacing sight_range 1 with sight_range 12 and bonus_height 1",
             name)
     if "special_range" in dct:
         del dct["special_range"]
         dct["range"] = 12 * PRECISION
         dct["minimal_range"] = 4 * PRECISION
         dct["is_ballistic"] = 1
         info(
             "in %s: replacing special_range 1 with range 12, minimal_range 4 and is_ballistic 1",
             name)
     self.dct = dct
     self.init_dict(self)
Exemplo n.º 16
0
def normalize_cost_or_resources(lst):
    n = rules.get("parameters", "nb_of_resource_types")
    while len(lst) < n:
        lst += [0]
    while len(lst) > n:
        del lst[-1]
Exemplo n.º 17
0
 def equivalent_type(t):
     tn = getattr(t, "type_name", "")
     if rules.get(self.faction, tn):
         return self.world.unit_class(rules.get(self.faction, tn)[0])
     return t
Exemplo n.º 18
0
 def equivalent(self, tn):
     if rules.get(self.race, tn):
         return rules.get(self.race, tn)[0]
     return tn
Exemplo n.º 19
0
 def equivalent(self, tn):
     if rules.get(self.faction, tn):
         return rules.get(self.faction, tn)[0]
     return tn
Exemplo n.º 20
0
def normalize_cost_or_resources(lst):
    n = int(rules.get("parameters", "nb_of_resource_types")[0])
    while len(lst) < n:
        lst += [0]
    while len(lst) > n:
        del lst[-1]
Exemplo n.º 21
0
 def equivalent_type(t):
     tn = getattr(t, "type_name", "")
     if rules.get(self.faction, tn):
         return self.world.unit_class(rules.get(self.faction, tn)[0])
     return t
Exemplo n.º 22
0
 def equivalent(self, tn):
     if rules.get(self.faction, tn):
         return rules.get(self.faction, tn)[0]
     return tn
Exemplo n.º 23
0
 def additional_condition(cls, unused_unit, type_name):
     e = rules.get(type_name, "effect")
     return e and hasattr(cls, "execute_%s" % e[0])
Exemplo n.º 24
0
 def get_races(self):
     return [c for c in rules.classnames()
             if rules.get(c, "class") == ["race"]]
Exemplo n.º 25
0
    def _load_map(self, map):
        def random_choice_repl(matchobj):
            return worldrandom.choice(
                matchobj.group(1).split("\n#end_choice\n"))

        def check_squares(squares):
            for sq in squares:
                if re.match("^[a-z]+[0-9]+$", sq) is None:
                    map_error(line, "%s is not a square" % sq)

        self.objective = []
        self.intro = []
        self.timer_coefficient = 1
        triggers = []

        self.map_objects = []

        self.computers_starts = []
        self.players_starts = []
        self.starting_units = []

        squares_words = [
            "starting_squares", "additional_meadows", "remove_meadows",
            "high_grounds"
        ]

        self.square_width = 12  # default value
        self.nb_lines = 0
        self.nb_columns = 0
        self.nb_rows = 0  # deprecated (was incorrectly used for columns instead of lines)
        self.nb_meadows_by_square = 0

        self.west_east = []
        self.south_north = []

        # "squares words"
        self.starting_squares = []
        self.additional_meadows = []
        self.remove_meadows = []
        self.high_grounds = []

        self.starting_resources = [0 for _ in range(self.nb_res)]
        self.nb_players_min = 1
        self.nb_players_max = 1

        s = map.read()  # "universal newlines"
        s = re.sub("(?m);.*$", "", s)  # remove comments
        s = re.sub("(?m)^[ \t]*$\n", "", s)  # remove empty lines
        s = re.sub(r"(?m)\\[ \t]*$\n", " ", s)  # join lines ending with "\"
        s = s.replace("(", " ( ")
        s = s.replace(")", " ) ")
        s = re.sub(r"\s*\n\s*", r"\n", s)  # strip lines
        s = re.sub(r"(?ms)^#random_choice\n(.*?)\n#end_random_choice$",
                   random_choice_repl, s)
        s = re.sub(r"(?m)^(goldmine|wood)s\s+([0-9]+)\s+(.*)$", r"\1 \2 \3", s)
        s = re.sub(r"(south_north|west_east)_paths", r"\1 path", s)
        s = re.sub(r"(south_north|west_east)_bridges", r"\1 bridge", s)
        for line in s.split("\n"):  # TODO: error msg
            words = line.strip().split()
            if not words:
                continue  # empty line
            w = words[0]
            if w[0:1] == ";":
                continue  # comment
            for _w in words[1:]:
                if w in ["south_north", "west_east"]:
                    continue  # TODO: check that the exit type_name is defined in style
                for _w in _w.split(","):
                    if _w and _w[0] == "-": _w = _w[1:]
                    if re.match("^([a-z]+[0-9]+|[0-9]+(.[0-9]*)?|.[0-9]+)$", _w) is None and \
                       not hasattr(Player, "lang_" + _w) and \
                       _w not in rules.classnames() and \
                       _w not in get_ai_names() and \
                       _w not in ["(", ")", "all", "players", "computers"] and \
                       _w not in ORDERS_DICT:
                        map_error(line, "unknown: %s" % _w)
            if w in ["title", "objective", "intro"]:
                setattr(self, w,
                        [int(x)
                         for x in words[1:]])  # TODO: error msg (sounds)
            elif w in [
                    "square_width", "nb_rows", "nb_columns", "nb_lines",
                    "nb_players_min", "nb_players_max", "scenario",
                    "nb_meadows_by_square", "global_food_limit",
                    "timer_coefficient"
            ]:
                try:
                    setattr(self, w, int(words[1]))
                    if w == "nb_rows":
                        self.nb_columns = self.nb_rows
                        warning(
                            "nb_rows is deprecated, use nb_columns instead")
                except:
                    map_error(line, "%s must be an integer" % w)
            elif w in ["south_north", "west_east"]:
                squares = words[2:]
                check_squares(squares)
                getattr(self, w).append((words[1], squares))
            elif w in squares_words:
                squares = words[1:]
                check_squares(squares)
                getattr(self, w).extend(squares)
            elif w in ["starting_resources"]:
                self.starting_resources = []
                for c in words[1:]:
                    try:
                        self.starting_resources.append(to_int(c))
                    except:
                        map_error(line, "expected an integer but found %s" % c)
            elif rules.get(w, "class") == ["deposit"]:
                for sq in words[2:]:  # TODO: error msg (squares)
                    self.map_objects.append([sq, w, words[1]])
            elif w in ["starting_units"]:
                getattr(self, w).extend(words[1:])  # TODO: error msg (types)
            elif w in ["player", "computer_only", "computer"]:
                self._add_start(w, words, line)
            elif w == "trigger":
                triggers.append(words[1:])
            else:
                map_error(line, "unknown command: %s" % w)
        # build self.players_starts
        for sq in self.starting_squares:
            self._add_start_to(self.players_starts, self.starting_resources,
                               self.starting_units, sq)
        if self.nb_players_min > self.nb_players_max:
            map_error("", "nb_players_min > nb_players_max")
        if len(self.players_starts) < self.nb_players_max:
            map_error("", "not enough starting places for nb_players_max")
        # 2 multiplayer map types: with or without standard triggers
        # TODO: select in a menu: User Map Settings, melee, free for all, etc
        if not triggers and self.default_triggers:
            triggers = self.default_triggers
        for t in triggers:
            self._add_trigger(t)
Exemplo n.º 26
0
 def get_factions(self):
     return [c for c in rules.classnames()
             if rules.get(c, "class") == ["faction"]]
Exemplo n.º 27
0
 def additional_condition(cls, unused_unit, type_name):
     e = rules.get(type_name, "effect")
     return e and hasattr(cls, "execute_%s" % e[0])
Exemplo n.º 28
0
 def nb_res(self):
     return int(rules.get("parameters", "nb_of_resource_types")[0])
Exemplo n.º 29
0
 def nb_res(self):
     return int(rules.get("parameters", "nb_of_resource_types")[0])
Exemplo n.º 30
0
 def get_factions(self):
     return [
         c for c in rules.classnames()
         if rules.get(c, "class") == ["faction"]
     ]
Exemplo n.º 31
0
    def _load_map(self, map):
        
        def random_choice_repl(matchobj):
            return worldrandom.choice(matchobj.group(1).split("\n#end_choice\n"))

        def check_squares(squares):
            for sq in squares:
                if re.match("^[a-z]+[0-9]+$", sq) is None:
                    map_error(line, "%s is not a square" % sq)

        self.objective = []
        self.intro = []
        self.timer_coefficient = 1
        triggers = []

        self.map_objects = []

        self.computers_starts = []
        self.players_starts = []
        self.starting_units = []

        squares_words = ["starting_squares",
                         "additional_meadows", "remove_meadows",
                         "high_grounds"]

        self.square_width = 12 # default value
        self.nb_lines = 0
        self.nb_columns = 0
        self.nb_rows = 0 # deprecated (was incorrectly used for columns instead of lines)
        self.nb_meadows_by_square = 0

        self.west_east = []
        self.south_north = []

        # "squares words"
        self.starting_squares = []
        self.additional_meadows = []
        self.remove_meadows = []
        self.high_grounds = []

        self.starting_resources = [0 for _ in range(self.nb_res)]
        self.nb_players_min = 1
        self.nb_players_max = 1

        s = map.read() # "universal newlines"
        s = re.sub("(?m);.*$", "", s) # remove comments
        s = re.sub("(?m)^[ \t]*$\n", "", s) # remove empty lines
        s = re.sub(r"(?m)\\[ \t]*$\n", " ", s) # join lines ending with "\"
        s = s.replace("(", " ( ")
        s = s.replace(")", " ) ")
        s = re.sub(r"\s*\n\s*", r"\n", s) # strip lines
        s = re.sub(r"(?ms)^#random_choice\n(.*?)\n#end_random_choice$", random_choice_repl, s)
        s = re.sub(r"(?m)^(goldmine|wood)s\s+([0-9]+)\s+(.*)$", r"\1 \2 \3", s)
        s = re.sub(r"(south_north|west_east)_paths", r"\1 path", s)
        s = re.sub(r"(south_north|west_east)_bridges", r"\1 bridge", s)
        for line in s.split("\n"): # TODO: error msg
            words = line.strip().split()
            if not words:
                continue # empty line
            w = words[0]
            if w[0:1] == ";":
                continue # comment
            for _w in words[1:]:
                if w in ["south_north", "west_east"]:
                    continue # TODO: check that the exit type_name is defined in style
                for _w in _w.split(","):
                    if _w and _w[0] == "-": _w = _w[1:]
                    if re.match("^([a-z]+[0-9]+|[0-9]+(.[0-9]*)?|.[0-9]+)$", _w) is None and \
                       not hasattr(Player, "lang_" + _w) and \
                       _w not in rules.classnames() and \
                       _w not in get_ai_names() and \
                       _w not in ["(", ")", "all", "players", "computers"] and \
                       _w not in ORDERS_DICT:
                        map_error(line, "unknown: %s" % _w)
            if w in ["title", "objective", "intro"]:
                setattr(self, w, [int(x) for x in words[1:]]) # TODO: error msg (sounds)
            elif w in ["square_width", "nb_rows", "nb_columns", "nb_lines",
                       "nb_players_min", "nb_players_max", "scenario",
                       "nb_meadows_by_square",
                       "global_food_limit",
                       "timer_coefficient"]:
                try:
                    setattr(self, w, int(words[1]))
                    if w == "nb_rows":
                        self.nb_columns = self.nb_rows
                        warning("nb_rows is deprecated, use nb_columns instead")
                except:
                    map_error(line, "%s must be an integer" % w)
            elif w in ["south_north", "west_east"]:
                squares = words[2:]
                check_squares(squares)
                getattr(self, w).append((words[1], squares))
            elif w in squares_words:
                squares = words[1:]
                check_squares(squares)
                getattr(self, w).extend(squares)
            elif w in ["starting_resources"]:
                self.starting_resources = []
                for c in words[1:]:
                    try:
                        self.starting_resources.append(to_int(c))
                    except:
                        map_error(line, "expected an integer but found %s" % c)
            elif rules.get(w, "class") == ["deposit"]:
                for sq in words[2:]: # TODO: error msg (squares)
                    self.map_objects.append([sq, w, words[1]])
            elif w in ["starting_units"]:
                getattr(self, w).extend(words[1:]) # TODO: error msg (types)
            elif w in ["player", "computer_only", "computer"]:
                self._add_start(w, words, line)
            elif w == "trigger":
                triggers.append(words[1:])
            else:
                map_error(line, "unknown command: %s" % w)
        # build self.players_starts
        for sq in self.starting_squares:
            self._add_start_to(self.players_starts,
                               self.starting_resources, self.starting_units, sq)
        if self.nb_players_min > self.nb_players_max:
            map_error("", "nb_players_min > nb_players_max")
        if len(self.players_starts) < self.nb_players_max:
            map_error("", "not enough starting places for nb_players_max")
        # 2 multiplayer map types: with or without standard triggers
        # TODO: select in a menu: User Map Settings, melee, free for all, etc
        if not triggers and self.default_triggers:
            triggers = self.default_triggers
        for t in triggers:
            self._add_trigger(t)