/**********************************************************************//** Return TRUE if this is an "unchanging" requirement. This means that if a target can't meet the requirement now, it probably won't ever be able to do so later. This can be used to do requirement filtering when checking if a target may "eventually" become available. Note this isn't absolute. Returning TRUE here just means that the requirement probably can't be met. In some cases (particularly terrains) it may be wrong. ***************************************************************************/ bool is_req_unchanging(const struct requirement *req) { switch (req->source.kind) { /* 0) Really never changing requirements */ /* Constant */ case VUT_NONE: /* Use a single persistent context factor */ case VUT_ACTION: case VUT_OTYPE: case VUT_SPECIALIST: /* Only so long as it's at local range only */ case VUT_IMPR_GENUS: /* Technically, unit may be in a context without unittype and may ever * change its type, but for now it is never seen in the code */ case VUT_UTYPE: case VUT_UTFLAG: case VUT_UCLASS: case VUT_UCFLAG: /* Can't change in-game */ case VUT_AI_LEVEL: case VUT_STYLE: case VUT_TOPO: case VUT_SERVERSETTING: case VUT_MINLATITUDE: /* Might change if more ranges are supported */ case VUT_MAXLATITUDE: /* Might change if more ranges are supported */ return TRUE; /* 1) Changeability calculatable from requirements themselves */ /* A city won't move from a tile as long as it stays. * Other values might change in certain cases. * If the context tile is or (c)adjacent to city_center(city), * other requirements of corresponding range get similary certain. */ case VUT_CITYTILE: return CITYT_CENTER == req->source.value.citytile; /* Player may ally with different ones */ case VUT_NATION: case VUT_NATIONGROUP: return (req->range != REQ_RANGE_ALLIANCE); case VUT_ROADFLAG: case VUT_EXTRAFLAG: case VUT_EXTRA: return REQ_RANGE_LOCAL == req->range; /* 2) Changeability calculatable from global variables */ case VUT_IMPROVEMENT: /* Here we could pass through obsoletion reqs but we don't want * to make this function recursive */ if (req->survives) { return REQ_RANGE_WORLD == req->range && num_world_buildings_total(building) > 0; /* Need to know the player to say have they ever built in other ranges */ } return REQ_RANGE_LOCAL == req->range; /* FIXME: in certain games players never lose techs */ case VUT_ADVANCE: return req->survives && game.info.global_advances [advance_number(req->source.value.advance)]; case VUT_MINTECHS: /* Effectively, it's always "survives" in world range */ return REQ_RANGE_WORLD == req->range /* "None" does not count */ && game.info.global_advance_count > req->source.value.mintechs; case VUT_MINYEAR: /* Once year is reached, it does not change again */ return req->source.value.minyear > game.info.year; /* 3) May be changed by scripts but not by game events * (should it be a TRI_MAYBE of some kind, or configurable?) */ case VUT_ORIGINAL_OWNER: /* return (req->range <= REQ_RANGE_CITY); */ return TRUE; /* 4) Commonly changeable */ case VUT_COUNTER: case VUT_CITYSTATUS: case VUT_TECHFLAG: case VUT_GOVERNMENT: case VUT_ACHIEVEMENT: case VUT_MINSIZE: case VUT_MINCULTURE: /* Remember, it may reduce */ case VUT_MINFOREIGNPCT: case VUT_NATIONALITY: case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: case VUT_DIPLREL_UNITANY: case VUT_DIPLREL_UNITANY_O: case VUT_MAXTILEUNITS: case VUT_MINVETERAN: /* Would belong to group 3) if we knew * if the unit is already a veteran */ case VUT_UNITSTATE: case VUT_ACTIVITY: case VUT_MINMOVES: case VUT_MINHP: case VUT_AGE: /* But: once reached, can't be unreached*/ case VUT_MINCALFRAG: /* cyclically available */ case VUT_TERRAIN: case VUT_GOOD: case VUT_TERRAINCLASS: case VUT_TERRFLAG: case VUT_TERRAINALTER: return FALSE; case VUT_COUNT: break; } fc_assert_msg(FALSE, "Invalid source kind %d.", req->source.kind); return TRUE; } /**********************************************************************//** A more specific variant of is_req_unchanging() that checks if req may be changed as long as it is evaluated to owner and the context city belongs to owner all the time. Still, it's a guess, not an absolute truth. ***************************************************************************/ bool is_req_unchanging_for(const struct requirement *req, const struct player *owner) { fc_assert_ret_val(owner, FALSE); switch (req->source.kind) { case VUT_IMPROVEMENT: /* Here we could pass through obsoletion reqs but we don't want * to make this function recursive */ switch (req->range) { case REQ_RANGE_WORLD: return num_world_buildings_total(req->source.value.building) > 0; case REQ_RANGE_TEAM: /* Teams can be considered immutable but clients don't always know * who has built what (present ones could be seen on the map...) */ if (req->survives && (is_server() || wonder_visible_to_player(req->source.value.building, owner, NULL, TRI_YES))) { players_iterate_alive(plr2) { if (players_on_same_team(owner, plr2) && player_has_ever_built(plr2, req->source.value.building)) { return TRUE; } } players_iterate_alive_end; return FALSE; } fc__fallthrough; case REQ_RANGE_ALLIANCE: /* Players are always allied to themselves */ case REQ_RANGE_PLAYER: if (req->survives) { return player_has_ever_built(owner, req->source.value.building); } case REQ_RANGE_LOCAL: return TRUE; case REQ_RANGE_TRADEROUTE: case REQ_RANGE_CITY: case REQ_RANGE_CONTINENT: case REQ_RANGE_TILE: break; case REQ_RANGE_ADJACENT: case REQ_RANGE_CADJACENT: /* Invalid range */ fc_assert(FALSE); return FALSE; } return is_great_wonder(req->source.value.building) && great_wonder_is_destroyed(req->source.value.building); case VUT_ORIGINAL_OWNER: return TRUE; case VUT_CITYSTATUS: return CITYS_OWNED_BY_ORIGINAL == req->source.value.citystatus; case VUT_AGE: return REQ_RANGE_PLAYER == req->range && req->source.value.age <= player_age(owner); default: /* Nothing special yet for other */ return is_req_unchanging(req); } }