Help has moved to the Planio platform. All logins and passwords remained the same. All users will be able to login and use Redmine just as before. Read more...

Bug #951984


Issues with ZOC over non-native terrain

Added by Lexxie L 8 months ago.

Start date:
Due date:
% Done:


Estimated time:


This is not a bug in legacy rulesets and works fine in them, because one of the two terrain classes is a NoZOC flag.

However, in MP2 development, I am experimenting with bringing some limited ZOC mechanics to the water, as consensus really is that some inherent flaws in naval game, especially in 2x movement rulesets, they need it.

The issue is HasZOC units exerting ZOC over non-native terrain. For example warriors in Constantinople prevent Battleships from passing through from Aegean to Black Sea. Or a Trireme on the water prevents Armor from moving along the road on the coast.

Because legacy rulesets made no rule for this case, we're in a kind of void when it comes to looking back on legacy rules for what to do about it. But it's really common sense. If a warrior can't even GO on the water or ATTACK a battleship, it shouldn't exert ZOC over it. If we go for the simplest common-sense without tons of complications and exceptions, I think the rule for exerting ZOC over non-native terrain should be:
I. General non-native ZOC rule
1. A unit who has ZOC can exert ZOC on all adjacent tiles to which it is native.
Super simple and works great, in my opinion.

Next comes the question about occupied city, which is a little bit tougher because no perfection decision.
II. The options for occupied city ZOC:
1. Total consistency to before: occupied cities exert ZOC over all adjacent tiles. OR
2. Cities are considered "native" to land and therefore NEVER exert ZOC over any terrain they can't be built on.
3. Same as above but if there is a unit inside the city who has ZOC native to ocean, it will exert ZOC over adjacent ocean tiles IFF such unit occupies the city.

My thoughts on 1,2,3 are:
1. I don't like it. A worker preventing a legion from movement was a bit questionable but it was also original legacy rules and had some very good reasoning about not exposing what's inside the city. But a worker preventing a Battleship from moving is a bad joke.
2. I like the beautiful consistency of it and it avoids the issues I will explain in #3.
3. In this one we are expecting a HasZOC aircraft or HasZOC ship to give the city ZOC over adjacent ocean IF AND ONLY IF it's inside the city, but the problems are:
a. Ships are usually considered "docked" and not battle-ready with huge defense penalty (BadCityDefender),
b. Aircraft are considered as on the ground or in hangars, scrambling to defend IFF the city tile itself is under attack.
c. This opens up the exploit to see what's inside the city, which is inconsistent with original rules over occupied cities, that you just can't guess from the ZOC it exerts. Now you can guess that the city is occupied by BadCityDefender and do exploits for PEARL HARBOR DAY.

Curious to hear thoughts on all this. FCW has implemented I.1 and II.2.
It was very easy to do in 2 lines of code

bool is_my_zoc(const struct player *pplayer, const struct tile *ptile0,
               const struct civ_map *zmap)
  struct terrain *pterrain;
  bool srv = is_server();

  square_iterate(zmap, ptile0, 1, ptile) {
    struct city *pcity;

    pterrain = tile_terrain(ptile);
    if (T_UNKNOWN == pterrain
        || terrain_has_flag(pterrain, TER_NO_ZOC)) {

    pcity = is_non_allied_city_tile(ptile, pplayer);

    if (pcity != NULL) {
      if (!is_ocean_tile(ptile0)) {
        if ((srv && unit_list_size(ptile->units) > 0)
            || (!srv && (pcity->client.occupied
                        || TILE_KNOWN_UNSEEN == tile_get_known(ptile, pplayer)))) {
          return FALSE;
        } // occupied city: always ZOC over land, never ZOC over sea
    } else {
      unit_list_iterate(ptile->units, punit) {
        if (!pplayers_allied(unit_owner(punit), pplayer)
            && !unit_has_type_flag(punit, UTYF_NOZOC)
            // Can't exert ZOC upon tiles non-native to the unit!
            && is_native_tile_to_class(unit_class_get(punit), ptile0)) {
          return FALSE;
      } unit_list_iterate_end;
  } square_iterate_end;

  return TRUE;

No data to display

Also available in: Atom PDF