/* virtual */ uint32 RailTypeScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const { if (this->tile == INVALID_TILE) { switch (variable) { case 0x40: return 0; case 0x41: return 0; case 0x42: return 0; case 0x43: return _date; case 0x44: return HZB_TOWN_EDGE; } } switch (variable) { case 0x40: return GetTerrainType(this->tile, this->context); case 0x41: return 0; case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile); case 0x43: if (IsRailDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date; return _date; case 0x44: { const Town *t = NULL; if (IsRailDepotTile(this->tile)) { t = Depot::GetByTile(this->tile)->town; } else if (IsLevelCrossingTile(this->tile)) { t = ClosestTownFromTile(this->tile, UINT_MAX); } return t != NULL ? GetTownRadiusGroup(t, this->tile) : HZB_TOWN_EDGE; } } DEBUG(grf, 1, "Unhandled rail type tile variable 0x%X", variable); *available = false; return UINT_MAX; }
static void Ptrs_WAYP() { for (OldWaypoint *wp = _old_waypoints.Begin(); wp != _old_waypoints.End(); wp++) { SlObject(wp, _old_waypoint_desc); if (IsSavegameVersionBefore(12)) { wp->town_cn = (wp->string_id & 0xC000) == 0xC000 ? (wp->string_id >> 8) & 0x3F : 0; wp->town = ClosestTownFromTile(wp->xy, UINT_MAX); } else if (IsSavegameVersionBefore(122)) {
/** * Get the town scope associated with a station, if it exists. * On the first call, the town scope is created (if possible). * @return Town scope, if available. */ TownScopeResolver *StationResolverObject::GetTown() { if (this->town_scope == NULL) { Town *t = NULL; if (this->station_scope.st != NULL) { t = this->station_scope.st->town; } else if (this->station_scope.tile != INVALID_TILE) { t = ClosestTownFromTile(this->station_scope.tile, UINT_MAX); } if (t == NULL) return NULL; this->town_scope = new TownScopeResolver(*this, t, this->station_scope.st == NULL); } return this->town_scope; }
static void GenerateCompanyName(Company *c) { TileIndex tile; Town *t; StringID str; Company *cc; uint32 strp; /* Reserve space for extra unicode character. We need to do this to be able * to detect too long company name. */ char buffer[MAX_LENGTH_COMPANY_NAME_BYTES + MAX_CHAR_LENGTH]; if (c->name_1 != STR_SV_UNNAMED) return; tile = c->last_build_coordinate; if (tile == 0) return; t = ClosestTownFromTile(tile, UINT_MAX); if (t->name == NULL && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) { str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_PLAYERNAME_START; strp = t->townnameparts; verify_name:; /* No companies must have this name already */ FOR_ALL_COMPANIES(cc) { if (cc->name_1 == str && cc->name_2 == strp) goto bad_town_name; } GetString(buffer, str, lastof(buffer)); if (strlen(buffer) >= MAX_LENGTH_COMPANY_NAME_BYTES) goto bad_town_name; set_name:; c->name_1 = str; c->name_2 = strp; MarkWholeScreenDirty(); if (c->is_ai) { CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1); cni->FillData(c); SetDParam(0, STR_NEWS_COMPANY_LAUNCH_TITLE); SetDParam(1, STR_NEWS_COMPANY_LAUNCH_DESCRIPTION); SetDParamStr(2, cni->company_name); SetDParam(3, t->index); AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NS_COMPANY_NEW, NR_TILE, c->last_build_coordinate, NR_NONE, UINT32_MAX, cni); } AI::BroadcastNewEvent(new AIEventCompanyNew(c->index), c->index); return; }
static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags) { uint num; if (Company::IsValidID(_current_company)) { Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); if (t != NULL) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM, flags); } num = GetTreeCount(tile); if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4; if (flags & DC_EXEC) DoClearSquare(tile); return CommandCost(EXPENSES_CONSTRUCTION, num * _price[PR_CLEAR_TREES]); }
/** * Set the default name for a waypoint * @param wp Waypoint to work on */ static void MakeDefaultWaypointName(Waypoint *wp) { uint32 used = 0; // bitmap of used waypoint numbers, sliding window with 'next' as base uint32 next = 0; // first waypoint number in the bitmap StationID idx = 0; // index where we will stop wp->town = ClosestTownFromTile(wp->xy, UINT_MAX); /* Find first unused waypoint number belonging to this town. This can never fail, * as long as there can be at most 65535 waypoints in total. * * This does 'n * m' search, but with 32bit 'used' bitmap, it needs at most 'n * (1 + ceil(m / 32))' * steps (n - number of waypoints in pool, m - number of waypoints near this town). * Usually, it needs only 'n' steps. * * If it wasn't using 'used' and 'idx', it would just search for increasing 'next', * but this way it is faster */ StationID cid = 0; // current index, goes to Waypoint::GetPoolSize()-1, then wraps to 0 do { Waypoint *lwp = Waypoint::GetIfValid(cid); /* check only valid waypoints... */ if (lwp != NULL && wp != lwp) { /* only waypoints with 'generic' name within the same city */ if (lwp->name == NULL && lwp->town == wp->town && lwp->string_id == wp->string_id) { /* if lwp->town_cn < next, uint will overflow to '+inf' */ uint i = (uint)lwp->town_cn - next; if (i < 32) { SetBit(used, i); // update bitmap if (i == 0) { /* shift bitmap while the lowest bit is '1'; * increase the base of the bitmap too */ do { used >>= 1; next++; } while (HasBit(used, 0)); /* when we are at 'idx' again at end of the loop and * 'next' hasn't changed, then no waypoint had town_cn == next, * so we can safely use it */ idx = cid; } } } }
/** * Set the right DParams to get the name of an owner. * @param owner the owner to get the name of. * @param tile optional tile to get the right town. * @pre if tile == 0, then owner can't be OWNER_TOWN. */ void GetNameOfOwner(Owner owner, TileIndex tile) { SetDParam(2, owner); if (owner != OWNER_TOWN) { if (!Company::IsValidID(owner)) { SetDParam(0, STR_COMPANY_SOMEONE); } else { SetDParam(0, STR_COMPANY_NAME); SetDParam(1, owner); } } else { assert(tile != 0); const Town *t = ClosestTownFromTile(tile, UINT_MAX); SetDParam(0, STR_TOWN_NAME); SetDParam(1, t->index); } }
/** Build a ship depot. * @param tile tile where ship depot is built * @param flags type of operation * @param p1 bit 0 depot orientation (Axis) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { TileIndex tile2; CommandCost ret; Axis axis = Extract<Axis, 0>(p1); tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); if (!IsWaterTile(tile) || !IsWaterTile(tile2)) { return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); } if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) { /* Prevent depots on rapids */ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); } WaterClass wc1 = GetWaterClass(tile); WaterClass wc2 = GetWaterClass(tile2); ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; if (!Depot::CanAllocateItem()) return CMD_ERROR; if (flags & DC_EXEC) { Depot *depot = new Depot(tile); depot->town_index = ClosestTownFromTile(tile, UINT_MAX)->index; MakeShipDepot(tile, _current_company, depot->index, DEPOT_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); }
virtual void OnInit() { Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); /* Because build_date is not set yet in every TileDesc, we make sure it is empty */ TileDesc td; td.build_date = INVALID_DATE; /* Most tiles have only one owner, but * - drivethrough roadstops can be build on town owned roads (up to 2 owners) and * - roads can have up to four owners (railroad, road, tram, 3rd-roadtype "highway"). */ td.owner_type[0] = STR_LAND_AREA_INFORMATION_OWNER; // At least one owner is displayed, though it might be "N/A". td.owner_type[1] = STR_NULL; // STR_NULL results in skipping the owner td.owner_type[2] = STR_NULL; td.owner_type[3] = STR_NULL; td.owner[0] = OWNER_NONE; td.owner[1] = OWNER_NONE; td.owner[2] = OWNER_NONE; td.owner[3] = OWNER_NONE; td.station_class = STR_NULL; td.station_name = STR_NULL; td.airport_class = STR_NULL; td.airport_name = STR_NULL; td.airport_tile_name = STR_NULL; td.rail_speed = 0; td.grf = NULL; CargoArray acceptance; AddAcceptedCargo(tile, acceptance, NULL); GetTileDesc(tile, &td); uint line_nr = 0; /* Tiletype */ SetDParam(0, td.dparam[0]); GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr])); line_nr++; /* Up to four owners */ for (uint i = 0; i < 4; i++) { if (td.owner_type[i] == STR_NULL) continue; SetDParam(0, STR_LAND_AREA_INFORMATION_OWNER_N_A); if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile); GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr])); line_nr++; } /* Cost to clear/revenue when cleared */ StringID str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A; Company *c = Company::GetIfValid(_local_company); if (c != NULL) { Money old_money = c->money; c->money = INT64_MAX; assert(_current_company == _local_company); CommandCost costclear = DoCommand(tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR); c->money = old_money; if (costclear.Succeeded()) { Money cost = costclear.GetCost(); if (cost < 0) { cost = -cost; // Negate negative cost to a positive revenue str = STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED; } else { str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR; } SetDParam(0, cost); } } GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr])); line_nr++; /* Location */ char tmp[16]; snprintf(tmp, lengthof(tmp), "0x%.4X", tile); SetDParam(0, TileX(tile)); SetDParam(1, TileY(tile)); SetDParam(2, GetTileZ(tile)); SetDParamStr(3, tmp); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr])); line_nr++; /* Local authority */ SetDParam(0, STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE); if (t != NULL) { SetDParam(0, STR_TOWN_NAME); SetDParam(1, t->index); } GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr])); line_nr++; /* Build date */ if (td.build_date != INVALID_DATE) { SetDParam(0, td.build_date); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_BUILD_DATE, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Station class */ if (td.station_class != STR_NULL) { SetDParam(0, td.station_class); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_CLASS, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Station type name */ if (td.station_name != STR_NULL) { SetDParam(0, td.station_name); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_TYPE, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Airport class */ if (td.airport_class != STR_NULL) { SetDParam(0, td.airport_class); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_AIRPORT_CLASS, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Airport name */ if (td.airport_name != STR_NULL) { SetDParam(0, td.airport_name); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_AIRPORT_NAME, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Airport tile name */ if (td.airport_tile_name != STR_NULL) { SetDParam(0, td.airport_tile_name); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_AIRPORTTILE_NAME, lastof(this->landinfo_data[line_nr])); line_nr++; } /* Rail speed limit */ if (td.rail_speed != 0) { SetDParam(0, td.rail_speed); GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT, lastof(this->landinfo_data[line_nr])); line_nr++; } /* NewGRF name */ if (td.grf != NULL) { SetDParamStr(0, td.grf); GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_NEWGRF_NAME, lastof(this->landinfo_data[line_nr])); line_nr++; } assert(line_nr < LAND_INFO_CENTERED_LINES); /* Mark last line empty */ this->landinfo_data[line_nr][0] = '\0'; /* Cargo acceptance is displayed in a extra multiline */ char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); bool found = false; for (CargoID i = 0; i < NUM_CARGO; ++i) { if (acceptance[i] > 0) { /* Add a comma between each item. */ if (found) { *strp++ = ','; *strp++ = ' '; } found = true; /* If the accepted value is less than 8, show it in 1/8:ths */ if (acceptance[i] < 8) { SetDParam(0, acceptance[i]); SetDParam(1, CargoSpec::Get(i)->name); strp = GetString(strp, STR_LAND_AREA_INFORMATION_CARGO_EIGHTS, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); } else { strp = GetString(strp, CargoSpec::Get(i)->name, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); } } } if (!found) this->landinfo_data[LAND_INFO_MULTICENTER_LINE][0] = '\0'; }
static uint32 StationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) { const BaseStation *st = object->u.station.st; TileIndex tile = object->u.station.tile; if (object->scope == VSG_SCOPE_PARENT) { /* Pass the request on to the town of the station */ const Town *t; if (st != NULL) { t = st->town; } else if (tile != INVALID_TILE) { t = ClosestTownFromTile(tile, UINT_MAX); } else { *available = false; return UINT_MAX; } return TownGetVariable(variable, parameter, available, t); } if (st == NULL) { /* Station does not exist, so we're in a purchase list */ switch (variable) { case 0x40: case 0x41: case 0x46: case 0x47: case 0x49: return 0x2110000; // Platforms, tracks & position case 0x42: return 0; // Rail type (XXX Get current type from GUI?) case 0x43: return _current_company; // Station owner case 0x44: return 2; // PBS status case 0xFA: return Clamp(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Build date, clamped to a 16 bit value } *available = false; return UINT_MAX; } switch (variable) { /* Calculated station variables */ case 0x40: if (!HasBit(_svc.valid, 0)) { _svc.v40 = GetPlatformInfoHelper(tile, false, false, false); SetBit(_svc.valid, 0); } return _svc.v40; case 0x41: if (!HasBit(_svc.valid, 1)) { _svc.v41 = GetPlatformInfoHelper(tile, true, false, false); SetBit(_svc.valid, 1); } return _svc.v41; case 0x42: return GetTerrainType(tile) | (GetReverseRailTypeTranslation(GetRailType(tile), object->u.station.statspec->grf_prop.grffile) << 8); case 0x43: return st->owner; // Station owner case 0x44: return HasStationReservation(tile) ? 7 : 4; // PBS status case 0x45: if (!HasBit(_svc.valid, 2)) { _svc.v45 = GetRailContinuationInfo(tile); SetBit(_svc.valid, 2); } return _svc.v45; case 0x46: if (!HasBit(_svc.valid, 3)) { _svc.v46 = GetPlatformInfoHelper(tile, false, false, true); SetBit(_svc.valid, 3); } return _svc.v46; case 0x47: if (!HasBit(_svc.valid, 4)) { _svc.v47 = GetPlatformInfoHelper(tile, true, false, true); SetBit(_svc.valid, 4); } return _svc.v47; case 0x49: if (!HasBit(_svc.valid, 5)) { _svc.v49 = GetPlatformInfoHelper(tile, false, true, false); SetBit(_svc.valid, 5); } return _svc.v49; case 0x4A: // Animation frame of tile return GetAnimationFrame(tile); /* Variables which use the parameter */ /* Variables 0x60 to 0x65 are handled separately below */ case 0x66: // Animation frame of nearby tile if (parameter != 0) tile = GetNearbyTile(parameter, tile); return st->TileBelongsToRailStation(tile) ? GetAnimationFrame(tile) : UINT_MAX; case 0x67: { // Land info of nearby tile Axis axis = GetRailStationAxis(tile); if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required Slope tileh = GetTileSlope(tile, NULL); bool swap = (axis == AXIS_Y && HasBit(tileh, CORNER_W) != HasBit(tileh, CORNER_E)); return GetNearbyTileInformation(tile) ^ (swap ? SLOPE_EW : 0); } case 0x68: { // Station info of nearby tiles TileIndex nearby_tile = GetNearbyTile(parameter, tile); if (!HasStationTileRail(nearby_tile)) return 0xFFFFFFFF; uint32 grfid = st->speclist[GetCustomStationSpecIndex(tile)].grfid; bool perpendicular = GetRailStationAxis(tile) != GetRailStationAxis(nearby_tile); bool same_station = st->TileBelongsToRailStation(nearby_tile); uint32 res = GB(GetStationGfx(nearby_tile), 1, 2) << 12 | !!perpendicular << 11 | !!same_station << 10; if (IsCustomStationSpecIndex(nearby_tile)) { const StationSpecList ssl = BaseStation::GetByTile(nearby_tile)->speclist[GetCustomStationSpecIndex(nearby_tile)]; res |= 1 << (ssl.grfid != grfid ? 9 : 8) | ssl.localidx; } return res; } /* General station variables */ case 0x82: return 50; case 0x84: return st->string_id; case 0x86: return 0; case 0xF0: return st->facilities; case 0xFA: return Clamp(st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); } return st->GetNewGRFVariable(object, variable, parameter, available); }
/** * Plant a tree. * @param tile start tile of area-drag of tree plantation * @param flags type of operation * @param p1 tree type, TREE_INVALID means random. * @param p2 end tile of area-drag * @param text unused * @return the cost of this operation or an error */ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { StringID msg = INVALID_STRING_ID; CommandCost cost(EXPENSES_OTHER); const byte tree_to_plant = GB(p1, 0, 8); // We cannot use Extract as min and max are climate specific. if (p2 >= MapSize()) return CMD_ERROR; /* Check the tree type within the current climate */ if (tree_to_plant != TREE_INVALID && !IsInsideBS(tree_to_plant, _tree_base_by_landscape[_settings_game.game_creation.landscape], _tree_count_by_landscape[_settings_game.game_creation.landscape])) return CMD_ERROR; TileArea ta(tile, p2); TILE_AREA_LOOP(tile, ta) { switch (GetTileType(tile)) { case MP_TREES: /* no more space for trees? */ if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 4) { msg = STR_ERROR_TREE_ALREADY_HERE; continue; } if (flags & DC_EXEC) { AddTreeCount(tile, 1); MarkTileDirtyByTile(tile); } /* 2x as expensive to add more trees to an existing tile */ cost.AddCost(_price[PR_BUILD_TREES] * 2); break; case MP_WATER: if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL))) { msg = STR_ERROR_CAN_T_BUILD_ON_WATER; continue; } /* FALL THROUGH */ case MP_CLEAR: { if (IsBridgeAbove(tile)) { msg = STR_ERROR_SITE_UNSUITABLE; continue; } TreeType treetype = (TreeType)tree_to_plant; /* Be a bit picky about which trees go where. */ if (_settings_game.game_creation.landscape == LT_TROPIC && treetype != TREE_INVALID && ( /* No cacti outside the desert */ (treetype == TREE_CACTUS && GetTropicZone(tile) != TROPICZONE_DESERT) || /* No rain forest trees outside the rain forest, except in the editor mode where it makes those tiles rain forest tile */ (IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS) && GetTropicZone(tile) != TROPICZONE_RAINFOREST && _game_mode != GM_EDITOR) || /* And no subtropical trees in the desert/rain forest */ (IsInsideMM(treetype, TREE_SUB_TROPICAL, TREE_TOYLAND) && GetTropicZone(tile) != TROPICZONE_NORMAL))) { msg = STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE; continue; } if (IsTileType(tile, MP_CLEAR)) { /* Remove fields or rocks. Note that the ground will get barrened */ switch (GetRawClearGround(tile)) { case CLEAR_FIELDS: case CLEAR_ROCKS: { CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); break; } default: break; } } if (_game_mode != GM_EDITOR && Company::IsValidID(_current_company)) { Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM, flags); } if (flags & DC_EXEC) { if (treetype == TREE_INVALID) { treetype = GetRandomTreeType(tile, GB(Random(), 24, 8)); if (treetype == TREE_INVALID) treetype = TREE_CACTUS; } /* Plant full grown trees in scenario editor */ PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0); MarkTileDirtyByTile(tile); /* When planting rainforest-trees, set tropiczone to rainforest in editor. */ if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) { SetTropicZone(tile, TROPICZONE_RAINFOREST); } } cost.AddCost(_price[PR_BUILD_TREES]); break; } default: msg = STR_ERROR_SITE_UNSUITABLE; break; } } if (cost.GetCost() == 0) { return_cmd_error(msg); } else { return cost; } }