/** Cargo filter functions */
static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
{
	if (cid == CF_ANY) return true;
	uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true);
	return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
}
/**
 * Check blitter needed by NewGRF config and switch if needed.
 * @return False when nothing changed, true otherwise.
 */
static bool SwitchNewGRFBlitter()
{
	/* Never switch if the blitter was specified by the user. */
	if (!_blitter_autodetected) return false;

	/* Null driver => dedicated server => do nothing. */
	if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return false;

	/* Get preferred depth.
	 *  - depth_wanted_by_base: Depth required by the baseset, i.e. the majority of the sprites.
	 *  - depth_wanted_by_grf:  Depth required by some NewGRF.
	 * Both can force using a 32bpp blitter. depth_wanted_by_base is used to select
	 * between multiple 32bpp blitters, which perform differently with 8bpp sprites.
	 */
	uint depth_wanted_by_base = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP ? 32 : 8;
	uint depth_wanted_by_grf = _support8bpp == S8BPP_NONE ? 32 : 8;
	for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
		if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
		if (c->palette & GRFP_BLT_32BPP) depth_wanted_by_grf = 32;
	}

	/* Search the best blitter. */
	static const struct {
		const char *name;
		uint animation; ///< 0: no support, 1: do support, 2: both
		uint min_base_depth, max_base_depth, min_grf_depth, max_grf_depth;
	} replacement_blitters[] = {
#ifdef WITH_SSE
		{ "32bpp-sse4",      0, 32, 32,  8, 32 },
		{ "32bpp-ssse3",     0, 32, 32,  8, 32 },
		{ "32bpp-sse2",      0, 32, 32,  8, 32 },
		{ "32bpp-sse4-anim", 1, 32, 32,  8, 32 },
#endif
		{ "8bpp-optimized",  2,  8,  8,  8,  8 },
		{ "32bpp-optimized", 0,  8, 32,  8, 32 },
		{ "32bpp-anim",      1,  8, 32,  8, 32 },
	};

	const bool animation_wanted = HasBit(_display_opt, DO_FULL_ANIMATION);
	const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();

	for (uint i = 0; i < lengthof(replacement_blitters); i++) {
		if (animation_wanted && (replacement_blitters[i].animation == 0)) continue;
		if (!animation_wanted && (replacement_blitters[i].animation == 1)) continue;

		if (!IsInsideMM(depth_wanted_by_base, replacement_blitters[i].min_base_depth, replacement_blitters[i].max_base_depth + 1)) continue;
		if (!IsInsideMM(depth_wanted_by_grf, replacement_blitters[i].min_grf_depth, replacement_blitters[i].max_grf_depth + 1)) continue;
		const char *repl_blitter = replacement_blitters[i].name;

		if (strcmp(repl_blitter, cur_blitter) == 0) return false;
		if (BlitterFactory::GetBlitterFactory(repl_blitter) == NULL) continue;

		DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
		Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
		if (new_blitter == NULL) NOT_REACHED();
		DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter);
		break;
	}

	if (!VideoDriver::GetInstance()->AfterBlitterChange()) {
		/* Failed to switch blitter, let's hope we can return to the old one. */
		if (BlitterFactory::SelectBlitter(cur_blitter) == NULL || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
	}

	return true;
}
Beispiel #3
0
void GameLoop()
{
	if (_game_mode == GM_BOOTSTRAP) {
#ifdef ENABLE_NETWORK
		/* Check for UDP stuff */
		if (_network_available) NetworkBackgroundLoop();
#endif
		InputLoop();
		return;
	}

	ProcessAsyncSaveFinish();

	/* autosave game? */
	if (_do_autosave) {
		_do_autosave = false;
		DoAutosave();
		SetWindowDirty(WC_STATUS_BAR, 0);
	}

	/* switch game mode? */
	if (_switch_mode != SM_NONE && !HasModalProgress()) {
		SwitchToMode(_switch_mode);
		_switch_mode = SM_NONE;
	}

	IncreaseSpriteLRU();
	InteractiveRandom();

	extern int _caret_timer;
	_caret_timer += 3;
	CursorTick();

#ifdef ENABLE_NETWORK
	/* Check for UDP stuff */
	if (_network_available) NetworkBackgroundLoop();

	if (_networking && !HasModalProgress()) {
		/* Multiplayer */
		NetworkGameLoop();
	} else {
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
			/* This means that we want to reconnect to the last host
			 * We do this here, because it means that the network is really closed */
			NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR);
		}
		/* Singleplayer */
		StateGameLoop();
	}

	/* Check chat messages roughly once a second. */
	static uint check_message = 0;
	if (++check_message > 1000 / MILLISECONDS_PER_TICK) {
		check_message = 0;
		NetworkChatMessageLoop();
	}
#else
	StateGameLoop();
#endif /* ENABLE_NETWORK */

	if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();

	if (!_pause_mode || _game_mode == GM_EDITOR || _settings_game.construction.command_pause_level > CMDPL_NO_CONSTRUCTION) MoveAllTextEffects();

	InputLoop();

	_sound_driver->MainLoop();
	MusicLoop();
}
Beispiel #4
0
static void HeightMapCurves(uint level)
{
	height_t ht[lengthof(_curve_maps)];
	MemSetT(ht, 0, lengthof(ht));

	/* Set up a grid to choose curve maps based on location */
	uint sx = Clamp(1 << level, 2, 32);
	uint sy = Clamp(1 << level, 2, 32);
	byte *c = AllocaM(byte, sx * sy);

	for (uint i = 0; i < sx * sy; i++) {
		c[i] = Random() % lengthof(_curve_maps);
	}

	/* Apply curves */
	for (uint x = 0; x < _height_map.size_x; x++) {

		/* Get our X grid positions and bi-linear ratio */
		float fx = (float)(sx * x) / _height_map.size_x + 0.5f;
		uint x1 = (uint)fx;
		uint x2 = x1;
		float xr = 2.0f * (fx - x1) - 1.0f;
		xr = sin(xr * M_PI_2);
		xr = sin(xr * M_PI_2);
		xr = 0.5f * (xr + 1.0f);
		float xri = 1.0f - xr;

		if (x1 > 0) {
			x1--;
			if (x2 >= sx) x2--;
		}

		for (uint y = 0; y < _height_map.size_y; y++) {

			/* Get our Y grid position and bi-linear ratio */
			float fy = (float)(sy * y) / _height_map.size_y + 0.5f;
			uint y1 = (uint)fy;
			uint y2 = y1;
			float yr = 2.0f * (fy - y1) - 1.0f;
			yr = sin(yr * M_PI_2);
			yr = sin(yr * M_PI_2);
			yr = 0.5f * (yr + 1.0f);
			float yri = 1.0f - yr;

			if (y1 > 0) {
				y1--;
				if (y2 >= sy) y2--;
			}

			uint corner_a = c[x1 + sx * y1];
			uint corner_b = c[x1 + sx * y2];
			uint corner_c = c[x2 + sx * y1];
			uint corner_d = c[x2 + sx * y2];

			/* Bitmask of which curve maps are chosen, so that we do not bother
			 * calculating a curve which won't be used. */
			uint corner_bits = 0;
			corner_bits |= 1 << corner_a;
			corner_bits |= 1 << corner_b;
			corner_bits |= 1 << corner_c;
			corner_bits |= 1 << corner_d;

			height_t *h = &_height_map.height(x, y);

			/* Apply all curve maps that are used on this tile. */
			for (uint t = 0; t < lengthof(_curve_maps); t++) {
				if (!HasBit(corner_bits, t)) continue;

				const control_point_t *cm = _curve_maps[t].list;
				for (uint i = 0; i < _curve_maps[t].length - 1; i++) {
					const control_point_t &p1 = cm[i];
					const control_point_t &p2 = cm[i + 1];

					if (*h >= p1.x && *h < p2.x) {
						ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
						break;
					}
				}
			}

			/* Apply interpolation of curve map results. */
			*h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr);
		}
	}
}
Beispiel #5
0
/**
 * Determines capacity of a given vehicle from scratch.
 * For aircraft the main capacity is determined. Mail might be present as well.
 * @param v Vehicle of interest; NULL in purchase list
 * @param mail_capacity returns secondary cargo (mail) capacity of aircraft
 * @return Capacity
 */
uint Engine::DetermineCapacity(const Vehicle *v, uint16 *mail_capacity) const
{
	assert(v == NULL || this->index == v->engine_type);
	if (mail_capacity != NULL) *mail_capacity = 0;

	if (!this->CanCarryCargo()) return 0;

	bool new_multipliers = HasBit(this->info.misc_flags, EF_NO_DEFAULT_CARGO_MULTIPLIER);
	CargoID default_cargo = this->GetDefaultCargoType();
	CargoID cargo_type = (v != NULL) ? v->cargo_type : default_cargo;

	if (mail_capacity != NULL && this->type == VEH_AIRCRAFT && IsCargoInClass(cargo_type, CC_PASSENGERS)) {
		*mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity, v);
	}

	/* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */
	if (HasBit(this->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
			(new_multipliers || default_cargo != cargo_type || (v != NULL && v->cargo_subtype != 0))) {
		uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, this->index, v);
		if (callback != CALLBACK_FAILED) return callback;
	}

	/* Get capacity according to property resp. CB */
	uint capacity;
	uint extra_mail_cap = 0;
	switch (this->type) {
		case VEH_TRAIN:
			capacity = GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY,        this->u.rail.capacity, v);

			/* In purchase list add the capacity of the second head. Always use the plain property for this. */
			if (v == NULL && this->u.rail.railveh_type == RAILVEH_MULTIHEAD) capacity += this->u.rail.capacity;
			break;

		case VEH_ROAD:
			capacity = GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY,      this->u.road.capacity, v);
			break;

		case VEH_SHIP:
			capacity = GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY,         this->u.ship.capacity, v);
			break;

		case VEH_AIRCRAFT:
			capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->u.air.passenger_capacity, v);
			if (!IsCargoInClass(cargo_type, CC_PASSENGERS)) {
				extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity, v);
			}
			if (!new_multipliers && cargo_type == CT_MAIL) return capacity + extra_mail_cap;
			default_cargo = CT_PASSENGERS; // Always use 'passengers' wrt. cargo multipliers
			break;

		default: NOT_REACHED();
	}

	if (!new_multipliers) {
		/* Use the passenger multiplier for mail as well */
		capacity += extra_mail_cap;
		extra_mail_cap = 0;
	}

	/* Apply multipliers depending on cargo- and vehicletype. */
	if (new_multipliers || (this->type != VEH_SHIP && default_cargo != cargo_type)) {
		uint16 default_multiplier = new_multipliers ? 0x100 : CargoSpec::Get(default_cargo)->multiplier;
		uint16 cargo_multiplier = CargoSpec::Get(cargo_type)->multiplier;
		capacity *= cargo_multiplier;
		if (extra_mail_cap > 0) {
			uint mail_multiplier = CargoSpec::Get(CT_MAIL)->multiplier;
			capacity += (default_multiplier * extra_mail_cap * cargo_multiplier + mail_multiplier / 2) / mail_multiplier;
		}
		capacity = (capacity + default_multiplier / 2) / default_multiplier;
	}

	return capacity;
}
/**
 * Add the remaining articulated parts to the given vehicle.
 * @param first The head of the articulated bit.
 */
void AddArticulatedParts(Vehicle *first)
{
	VehicleType type = first->type;
	if (!HasBit(EngInfo(first->engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;

	Vehicle *v = first;
	for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
		bool flip_image;
		EngineID engine_type = GetNextArticulatedPart(i, first->engine_type, first, &flip_image);
		if (engine_type == INVALID_ENGINE) return;

		/* In the (very rare) case the GRF reported wrong number of articulated parts
		 * and we run out of available vehicles, bail out. */
		if (!Vehicle::CanAllocateItem()) return;

		GroundVehicleCache *gcache = v->GetGroundVehicleCache();
		gcache->first_engine = v->engine_type; // Needs to be set before first callback

		const Engine *e_artic = Engine::Get(engine_type);
		switch (type) {
			default: NOT_REACHED();

			case VEH_TRAIN: {
				Train *front = Train::From(first);
				Train *t = new Train();
				v->SetNext(t);
				v = t;

				t->subtype = 0;
				t->track = front->track;
				t->railtype = front->railtype;

				t->spritenum = e_artic->u.rail.image_index;
				if (e_artic->CanCarryCargo()) {
					t->cargo_type = e_artic->GetDefaultCargoType();
					t->cargo_cap = e_artic->u.rail.capacity;  // Callback 36 is called when the consist is finished
				} else {
					t->cargo_type = front->cargo_type; // Needed for livery selection
					t->cargo_cap = 0;
				}

				t->SetArticulatedPart();
				break;
			}

			case VEH_ROAD: {
				RoadVehicle *front = RoadVehicle::From(first);
				RoadVehicle *rv = new RoadVehicle();
				v->SetNext(rv);
				v = rv;

				rv->subtype = 0;
				gcache->cached_veh_length = VEHICLE_LENGTH; // Callback is called when the consist is finished
				rv->state = RVSB_IN_DEPOT;

				rv->roadtype = front->roadtype;
				rv->compatible_roadtypes = front->compatible_roadtypes;

				rv->spritenum = e_artic->u.road.image_index;
				if (e_artic->CanCarryCargo()) {
					rv->cargo_type = e_artic->GetDefaultCargoType();
					rv->cargo_cap = e_artic->u.road.capacity;  // Callback 36 is called when the consist is finished
				} else {
					rv->cargo_type = front->cargo_type; // Needed for livery selection
					rv->cargo_cap = 0;
				}

				rv->SetArticulatedPart();
				break;
			}
		}

		/* get common values from first engine */
		v->direction = first->direction;
		v->owner = first->owner;
		v->tile = first->tile;
		v->x_pos = first->x_pos;
		v->y_pos = first->y_pos;
		v->z_pos = first->z_pos;
		v->build_year = first->build_year;
		v->vehstatus = first->vehstatus & ~VS_STOPPED;

		v->cargo_subtype = 0;
		v->max_age = 0;
		v->engine_type = engine_type;
		v->value = 0;
		v->cur_image = SPR_IMG_QUERY;
		v->random_bits = VehicleRandomBits();

		if (flip_image) v->spritenum++;

		VehicleUpdatePosition(v);
	}
}
Beispiel #7
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);
}
Beispiel #8
0
/**
 * Check if all GRFs in the GRF config from a savegame can be loaded.
 * @param grfconfig GrfConfig to check
 * @return will return any of the following 3 values:<br>
 * <ul>
 * <li> GLC_ALL_GOOD: No problems occurred, all GRF files were found and loaded
 * <li> GLC_COMPATIBLE: For one or more GRF's no exact match was found, but a
 *     compatible GRF with the same grfid was found and used instead
 * <li> GLC_NOT_FOUND: For one or more GRF's no match was found at all
 * </ul>
 */
GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig)
{
	GRFListCompatibility res = GLC_ALL_GOOD;

	for (GRFConfig *c = grfconfig; c != NULL; c = c->next) {
		const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->ident.md5sum);
		if (f == NULL || HasBit(f->flags, GCF_INVALID)) {
			char buf[256];

			/* If we have not found the exactly matching GRF try to find one with the
			 * same grfid, as it most likely is compatible */
			f = FindGRFConfig(c->ident.grfid, FGCM_COMPATIBLE, NULL, c->version);
			if (f != NULL) {
				md5sumToString(buf, lastof(buf), c->ident.md5sum);
				DEBUG(grf, 1, "NewGRF %08X (%s) not found; checksum %s. Compatibility mode on", BSWAP32(c->ident.grfid), c->filename, buf);
				if (!HasBit(c->flags, GCF_COMPATIBLE)) {
					/* Preserve original_md5sum after it has been assigned */
					SetBit(c->flags, GCF_COMPATIBLE);
					memcpy(c->original_md5sum, c->ident.md5sum, sizeof(c->original_md5sum));
				}

				/* Non-found has precedence over compatibility load */
				if (res != GLC_NOT_FOUND) res = GLC_COMPATIBLE;
				goto compatible_grf;
			}

			/* No compatible grf was found, mark it as disabled */
			md5sumToString(buf, lastof(buf), c->ident.md5sum);
			DEBUG(grf, 0, "NewGRF %08X (%s) not found; checksum %s", BSWAP32(c->ident.grfid), c->filename, buf);

			c->status = GCS_NOT_FOUND;
			res = GLC_NOT_FOUND;
		} else {
compatible_grf:
			DEBUG(grf, 1, "Loading GRF %08X from %s", BSWAP32(f->ident.grfid), f->filename);
			/* The filename could be the filename as in the savegame. As we need
			 * to load the GRF here, we need the correct filename, so overwrite that
			 * in any case and set the name and info when it is not set already.
			 * When the GCF_COPY flag is set, it is certain that the filename is
			 * already a local one, so there is no need to replace it. */
			if (!HasBit(c->flags, GCF_COPY)) {
				free(c->filename);
				c->filename = strdup(f->filename);
				memcpy(c->ident.md5sum, f->ident.md5sum, sizeof(c->ident.md5sum));
				c->name->Release();
				c->name = f->name;
				c->name->AddRef();
				c->info->Release();
				c->info = f->name;
				c->info->AddRef();
				c->error = NULL;
				c->version = f->version;
				c->min_loadable_version = f->min_loadable_version;
				c->num_valid_params = f->num_valid_params;
				c->has_param_defaults = f->has_param_defaults;
				for (uint i = 0; i < f->param_info.Length(); i++) {
					if (f->param_info[i] == NULL) {
						*c->param_info.Append() = NULL;
					} else {
						*c->param_info.Append() = new GRFParameterInfo(*f->param_info[i]);
					}
				}
			}
		}
	}

	return res;
}
Beispiel #9
0
/**
 * @note Used by the resolver to get values for feature 07 deterministic spritegroups.
 */
/* virtual */ uint32 HouseScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
{
	switch (variable) {
		/* Construction stage. */
		case 0x40: return (IsTileType(this->tile, MP_HOUSE) ? GetHouseBuildingStage(this->tile) : 0) | TileHash2Bit(TileX(this->tile), TileY(this->tile)) << 2;

		/* Building age. */
		case 0x41: return IsTileType(this->tile, MP_HOUSE) ? GetHouseAge(this->tile) : 0;

		/* Town zone */
		case 0x42: return GetTownRadiusGroup(this->town, this->tile);

		/* Terrain type */
		case 0x43: return GetTerrainType(this->tile);

		/* Number of this type of building on the map. */
		case 0x44: return GetNumHouses(this->house_id, this->town);

		/* Whether the town is being created or just expanded. */
		case 0x45: return _generating_world ? 1 : 0;

		/* Current animation frame. */
		case 0x46: return IsTileType(this->tile, MP_HOUSE) ? GetAnimationFrame(this->tile) : 0;

		/* Position of the house */
		case 0x47: return TileY(this->tile) << 16 | TileX(this->tile);

		/* Building counts for old houses with id = parameter. */
		case 0x60: return parameter < NEW_HOUSE_OFFSET ? GetNumHouses(parameter, this->town) : 0;

		/* Building counts for new houses with id = parameter. */
		case 0x61: {
			const HouseSpec *hs = HouseSpec::Get(this->house_id);
			if (hs->grf_prop.grffile == NULL) return 0;

			HouseID new_house = _house_mngr.GetID(parameter, hs->grf_prop.grffile->grfid);
			return new_house == INVALID_HOUSE_ID ? 0 : GetNumHouses(new_house, this->town);
		}

		/* Land info for nearby tiles. */
		case 0x62: return GetNearbyTileInformation(parameter, this->tile, this->ro.grffile->grf_version >= 8);

		/* Current animation frame of nearby house tiles */
		case 0x63: {
			TileIndex testtile = GetNearbyTile(parameter, this->tile);
			return IsTileType(testtile, MP_HOUSE) ? GetAnimationFrame(testtile) : 0;
		}

		/* Cargo acceptance history of nearby stations */
		case 0x64: {
			CargoID cid = GetCargoTranslation(parameter, this->ro.grffile);
			if (cid == CT_INVALID) return 0;

			/* Extract tile offset. */
			int8 x_offs = GB(GetRegister(0x100), 0, 8);
			int8 y_offs = GB(GetRegister(0x100), 8, 8);
			TileIndex testtile = TILE_MASK(this->tile + TileDiffXY(x_offs, y_offs));

			StationFinder stations(TileArea(testtile, 1, 1));
			const StationList *sl = stations.GetStations();

			/* Collect acceptance stats. */
			uint32 res = 0;
			for (Station * const * st_iter = sl->Begin(); st_iter != sl->End(); st_iter++) {
				const Station *st = *st_iter;
				if (HasBit(st->goods[cid].acceptance_pickup, GoodsEntry::GES_EVER_ACCEPTED))    SetBit(res, 0);
				if (HasBit(st->goods[cid].acceptance_pickup, GoodsEntry::GES_LAST_MONTH))       SetBit(res, 1);
				if (HasBit(st->goods[cid].acceptance_pickup, GoodsEntry::GES_CURRENT_MONTH))    SetBit(res, 2);
				if (HasBit(st->goods[cid].acceptance_pickup, GoodsEntry::GES_ACCEPTED_BIGTICK)) SetBit(res, 3);
			}

			/* Cargo triggered CB 148? */
			if (HasBit(this->watched_cargo_triggers, cid)) SetBit(res, 4);

			return res;
		}

		/* Distance test for some house types */
		case 0x65: return GetDistanceFromNearbyHouse(parameter, this->tile, this->house_id);

		/* Class and ID of nearby house tile */
		case 0x66: {
			TileIndex testtile = GetNearbyTile(parameter, this->tile);
			if (!IsTileType(testtile, MP_HOUSE)) return 0xFFFFFFFF;
			HouseSpec *hs = HouseSpec::Get(GetHouseType(testtile));
			/* Information about the grf local classid if the house has a class */
			uint houseclass = 0;
			if (hs->class_id != HOUSE_NO_CLASS) {
				houseclass = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
				houseclass |= _class_mapping[hs->class_id].class_id;
			}
			/* old house type or grf-local houseid */
			uint local_houseid = 0;
			if (this->house_id < NEW_HOUSE_OFFSET) {
				local_houseid = this->house_id;
			} else {
				local_houseid = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
				local_houseid |= hs->grf_prop.local_id;
			}
			return houseclass << 16 | local_houseid;
		}

		/* GRFID of nearby house tile */
		case 0x67: {
			TileIndex testtile = GetNearbyTile(parameter, this->tile);
			if (!IsTileType(testtile, MP_HOUSE)) return 0xFFFFFFFF;
			HouseID house_id = GetHouseType(testtile);
			if (house_id < NEW_HOUSE_OFFSET) return 0;
			/* Checking the grffile information via HouseSpec doesn't work
			 * in case the newgrf was removed. */
			return _house_mngr.GetGRFID(house_id);
		}
	}

	DEBUG(grf, 1, "Unhandled house variable 0x%X", variable);

	*available = false;
	return UINT_MAX;
}
Beispiel #10
0
static void OpenBankFile(const char *filename)
{
	memset(_original_sounds, 0, sizeof(_original_sounds));

	FioOpenFile(SOUND_SLOT, filename);
	size_t pos = FioGetPos();
	uint count = FioReadDword();

	/* The new format has the highest bit always set */
	bool new_format = HasBit(count, 31);
	ClrBit(count, 31);
	count /= 8;

	/* Simple check for the correct number of original sounds. */
	if (count != ORIGINAL_SAMPLE_COUNT) {
		/* Corrupt sample data? Just leave the allocated memory as those tell
		 * there is no sound to play (size = 0 due to calloc). Not allocating
		 * the memory disables valid NewGRFs that replace sounds. */
		DEBUG(misc, 6, "Incorrect number of sounds in '%s', ignoring.", filename);
		return;
	}

	FioSeekTo(pos, SEEK_SET);

	for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
		_original_sounds[i].file_slot = SOUND_SLOT;
		_original_sounds[i].file_offset = GB(FioReadDword(), 0, 31) + pos;
		_original_sounds[i].file_size = FioReadDword();
	}

	for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
		SoundEntry *sound = &_original_sounds[i];
		char name[255];

		FioSeekTo(sound->file_offset, SEEK_SET);

		/* Check for special case, see else case */
		FioReadBlock(name, FioReadByte()); // Read the name of the sound
		if (new_format || strcmp(name, "Corrupt sound") != 0) {
			FioSeekTo(12, SEEK_CUR); // Skip past RIFF header

			/* Read riff tags */
			for (;;) {
				uint32 tag = FioReadDword();
				uint32 size = FioReadDword();

				if (tag == ' tmf') {
					FioReadWord(); // wFormatTag
					sound->channels = FioReadWord();        // wChannels
					sound->rate     = FioReadDword();       // samples per second
					if (!new_format) sound->rate = 11025;   // seems like all old samples should be played at this rate.
					FioReadDword();                         // avg bytes per second
					FioReadWord();                          // alignment
					sound->bits_per_sample = FioReadByte(); // bits per sample
					FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR);
				} else if (tag == 'atad') {
					sound->file_size = size;
					sound->file_slot = SOUND_SLOT;
					sound->file_offset = FioGetPos();
					break;
				} else {
					sound->file_size = 0;
					break;
				}
			}
		} else {
			/*
			 * Special case for the jackhammer sound
			 * (name in sample.cat is "Corrupt sound")
			 * It's no RIFF file, but raw PCM data
			 */
			sound->channels = 1;
			sound->rate = 11025;
			sound->bits_per_sample = 8;
			sound->file_slot = SOUND_SLOT;
			sound->file_offset = FioGetPos();
		}
	}
}
Beispiel #11
0
void UpdateVehicleTimetable(Vehicle *v, bool travelling)
{
	uint timetabled = travelling ? v->current_order.travel_time : v->current_order.wait_time;
	uint time_taken = v->current_order_time;

	v->current_order_time = 0;

	if (v->current_order.IsType(OT_AUTOMATIC)) return; // no timetabling of auto orders

	VehicleOrderID first_manual_order = 0;
	for (Order *o = v->GetFirstOrder(); o != NULL && o->IsType(OT_AUTOMATIC); o = o->next) {
		++first_manual_order;
	}

	bool just_started = false;

	/* This vehicle is arriving at the first destination in the timetable. */
	if (v->cur_real_order_index == first_manual_order && travelling) {
		/* If the start date hasn't been set, or it was set automatically when
		 * the vehicle last arrived at the first destination, update it to the
		 * current time. Otherwise set the late counter appropriately to when
		 * the vehicle should have arrived. */
		just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);

		if (v->timetable_start != 0) {
			v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
			v->timetable_start = 0;
		}

		SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
	}

	if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;

	if (HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) {
		if (travelling && !HasBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) {
			/* Need to clear that now as otherwise we are not able to reduce the wait time */
			v->current_order.wait_time = 0;
		}

		if (just_started) return;

		/* Modify station waiting time only if our new value is larger (this is
		 * always the case when we cleared the timetable). */
		if (!v->current_order.IsType(OT_CONDITIONAL) && (travelling || time_taken > v->current_order.wait_time)) {
			/* Round the time taken up to the nearest day, as this will avoid
			 * confusion for people who are timetabling in days, and can be
			 * adjusted later by people who aren't.
			 * For trains/aircraft multiple movement cycles are done in one
			 * tick. This makes it possible to leave the station and process
			 * e.g. a depot order in the same tick, causing it to not fill
			 * the timetable entry like is done for road vehicles/ships.
			 * Thus always make sure at least one tick is used between the
			 * processing of different orders when filling the timetable. */
			time_taken = CeilDiv(max(time_taken, 1U), DAY_TICKS) * DAY_TICKS;

			ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling);
		}

		if (v->cur_real_order_index == first_manual_order && travelling) {
			/* If we just started we would have returned earlier and have not reached
			 * this code. So obviously, we have completed our round: So turn autofill
			 * off again. */
			ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
			ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
		}
		return;
	}

	if (just_started) return;

	/* Vehicles will wait at stations if they arrive early even if they are not
	 * timetabled to wait there, so make sure the lateness counter is updated
	 * when this happens. */
	if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;

	v->lateness_counter -= (timetabled - time_taken);

	/* When we are more late than this timetabled bit takes we (somewhat expensively)
	 * check how many ticks the (fully filled) timetable has. If a timetable cycle is
	 * shorter than the amount of ticks we are late we reduce the lateness by the
	 * length of a full cycle till lateness is less than the length of a timetable
	 * cycle. When the timetable isn't fully filled the cycle will be INVALID_TICKS. */
	if (v->lateness_counter > (int)timetabled) {
		Ticks cycle = v->orders.list->GetTimetableTotalDuration();
		if (cycle != INVALID_TICKS && v->lateness_counter > cycle) {
			v->lateness_counter %= cycle;
		}
	}

	for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
	}
}
Beispiel #12
0
	virtual void OnClick(Point pt, int widget, int click_count)
	{
		/* clicked a letter */
		if (widget >= WID_OSK_LETTERS) {
			WChar c = _keyboard[this->shift][widget - WID_OSK_LETTERS];

			if (!IsValidChar(c, this->qs->text.afilter)) return;

			if (this->qs->text.InsertChar(c)) this->OnEditboxChanged(WID_OSK_TEXT);

			if (HasBit(_keystate, KEYS_SHIFT)) {
				ToggleBit(_keystate, KEYS_SHIFT);
				this->UpdateOskState();
				this->SetDirty();
			}
			return;
		}

		switch (widget) {
			case WID_OSK_BACKSPACE:
				if (this->qs->text.DeleteChar(WKC_BACKSPACE)) this->OnEditboxChanged(WID_OSK_TEXT);
				break;

			case WID_OSK_SPECIAL:
				/*
				 * Anything device specific can go here.
				 * The button itself is hidden by default, and when you need it you
				 * can not hide it in the create event.
				 */
				break;

			case WID_OSK_CAPS:
				ToggleBit(_keystate, KEYS_CAPS);
				this->UpdateOskState();
				this->SetDirty();
				break;

			case WID_OSK_SHIFT:
				ToggleBit(_keystate, KEYS_SHIFT);
				this->UpdateOskState();
				this->SetDirty();
				break;

			case WID_OSK_SPACE:
				if (this->qs->text.InsertChar(' ')) this->OnEditboxChanged(WID_OSK_TEXT);
				break;

			case WID_OSK_LEFT:
				if (this->qs->text.MovePos(WKC_LEFT)) this->InvalidateData();
				break;

			case WID_OSK_RIGHT:
				if (this->qs->text.MovePos(WKC_RIGHT)) this->InvalidateData();
				break;

			case WID_OSK_OK:
				if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
					/* pass information by simulating a button press on parent window */
					if (this->qs->ok_button >= 0) {
						this->parent->OnClick(pt, this->qs->ok_button, 1);
						/* Window gets deleted when the parent window removes itself. */
						return;
					}
				}
				delete this;
				break;

			case WID_OSK_CANCEL:
				if (this->qs->cancel_button >= 0) { // pass a cancel event to the parent window
					this->parent->OnClick(pt, this->qs->cancel_button, 1);
					/* Window gets deleted when the parent window removes itself. */
					return;
				} else { // or reset to original string
					qs->text.Assign(this->orig_str_buf);
					qs->text.MovePos(WKC_END);
					this->OnEditboxChanged(WID_OSK_TEXT);
					delete this;
				}
				break;
		}
	}
Beispiel #13
0
/**
 * Returns the current value of the given flag on the given AyStarNode.
 */
static inline bool NPFGetFlag(const AyStarNode *node, NPFNodeFlag flag)
{
	return HasBit(node->user_data[NPF_NODE_FLAGS], flag);
}
Beispiel #14
0
/* Will just follow the results of GetTileTrackStatus concerning where we can
 * go and where not. Uses AyStar.user_data[NPF_TYPE] as the transport type and
 * an argument to GetTileTrackStatus. Will skip tunnels, meaning that the
 * entry and exit are neighbours. Will fill
 * AyStarNode.user_data[NPF_TRACKDIR_CHOICE] with an appropriate value, and
 * copy AyStarNode.user_data[NPF_NODE_FLAGS] from the parent */
static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
{
	/* We leave src_tile on track src_trackdir in direction src_exitdir */
	Trackdir src_trackdir = current->path.node.direction;
	TileIndex src_tile = current->path.node.tile;
	DiagDirection src_exitdir = TrackdirToExitdir(src_trackdir);

	/* Is src_tile valid, and can be used?
	 * When choosing track on a junction src_tile is the tile neighboured to the junction wrt. exitdir.
	 * But we must not check the validity of this move, as src_tile is totally unrelated to the move, if a roadvehicle reversed on a junction. */
	bool ignore_src_tile = (current->path.parent == NULL && NPFGetFlag(&current->path.node, NPF_FLAG_IGNORE_START_TILE));

	/* Information about the vehicle: TransportType (road/rail/water) and SubType (compatible rail/road types) */
	TransportType type = (TransportType)aystar->user_data[NPF_TYPE];
	uint subtype = aystar->user_data[NPF_SUB_TYPE];

	/* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */
	aystar->num_neighbours = 0;
	DEBUG(npf, 4, "Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile);

	/* We want to determine the tile we arrive, and which choices we have there */
	TileIndex dst_tile;
	TrackdirBits trackdirbits;

	/* Find dest tile */
	if (ignore_src_tile) {
		/* Do not perform any checks that involve src_tile */
		dst_tile = src_tile + TileOffsByDiagDir(src_exitdir);
		trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
	} else if (IsTileType(src_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(src_tile) == src_exitdir) {
		/* We drive through the wormhole and arrive on the other side */
		dst_tile = GetOtherTunnelBridgeEnd(src_tile);
		trackdirbits = TrackdirToTrackdirBits(src_trackdir);
	} else if (ForceReverse(src_tile, src_exitdir, type, subtype)) {
		/* We can only reverse on this tile */
		dst_tile = src_tile;
		src_trackdir = ReverseTrackdir(src_trackdir);
		trackdirbits = TrackdirToTrackdirBits(src_trackdir);
	} else {
		/* We leave src_tile in src_exitdir and reach dst_tile */
		dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir));

		if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, type, subtype, (RailTypes)aystar->user_data[NPF_RAILTYPES], (Owner)aystar->user_data[NPF_OWNER])) dst_tile = INVALID_TILE;

		if (dst_tile == INVALID_TILE) {
			/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
			if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;

			dst_tile = src_tile;
			src_trackdir = ReverseTrackdir(src_trackdir);
		}

		trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);

		if (trackdirbits == 0) {
			/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
			if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;

			dst_tile = src_tile;
			src_trackdir = ReverseTrackdir(src_trackdir);

			trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
		}
	}

	if (NPFGetFlag(&current->path.node, NPF_FLAG_IGNORE_RESERVED)) {
		/* Mask out any reserved tracks. */
		TrackBits reserved = GetReservedTrackbits(dst_tile);
		trackdirbits &= ~TrackBitsToTrackdirBits(reserved);

		uint bits = TrackdirBitsToTrackBits(trackdirbits);
		int i;
		FOR_EACH_SET_BIT(i, bits) {
			if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i);
		}
	}
Beispiel #15
0
static void PollEvent()
{
	poll_mouse();

	bool mouse_action = false;

	/* Mouse buttons */
	static int prev_button_state;
	if (prev_button_state != mouse_b) {
		uint diff = prev_button_state ^ mouse_b;
		while (diff != 0) {
			uint button = FindFirstBit(diff);
			ClrBit(diff, button);
			if (HasBit(mouse_b, button)) {
				/* Pressed mouse button */
				if (_rightclick_emulate && (key_shifts & KB_CTRL_FLAG)) {
					button = RIGHT_BUTTON;
					ClrBit(diff, RIGHT_BUTTON);
				}
				switch (button) {
					case LEFT_BUTTON:
						_left_button_down = true;
						break;

					case RIGHT_BUTTON:
						_right_button_down = true;
						_right_button_clicked = true;
						break;

					default:
						/* ignore rest */
						break;
				}
			} else {
				/* Released mouse button */
				if (_rightclick_emulate) {
					_right_button_down = false;
					_left_button_down = false;
					_left_button_clicked = false;
				} else if (button == LEFT_BUTTON) {
					_left_button_down = false;
					_left_button_clicked = false;
				} else if (button == RIGHT_BUTTON) {
					_right_button_down = false;
				}
			}
		}
		prev_button_state = mouse_b;
		mouse_action = true;
	}

	/* Mouse movement */
	if (_cursor.UpdateCursorPosition(mouse_x, mouse_y, false)) {
		position_mouse(_cursor.pos.x, _cursor.pos.y);
	}
	if (_cursor.delta.x != 0 || _cursor.delta.y) mouse_action = true;

	static int prev_mouse_z = 0;
	if (prev_mouse_z != mouse_z) {
		_cursor.wheel = (prev_mouse_z - mouse_z) < 0 ? -1 : 1;
		prev_mouse_z = mouse_z;
		mouse_action = true;
	}

	if (mouse_action) HandleMouseEvents();

	poll_keyboard();
	if ((key_shifts & KB_ALT_FLAG) && (key[KEY_ENTER] || key[KEY_F])) {
		ToggleFullScreen(!_fullscreen);
	} else if (keypressed()) {
		WChar character;
		uint keycode = ConvertAllegroKeyIntoMy(&character);
		HandleKeypress(keycode, character);
	}
}
void AnimateNewHouseTile(TileIndex tile)
{
	const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
	byte animation_speed = hs->animation_speed;
	bool frame_set_by_callback = false;

	if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_SPEED)) {
		uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_SPEED, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
		if (callback_res != CALLBACK_FAILED) animation_speed = Clamp(callback_res & 0xFF, 2, 16);
	}

	/* An animation speed of 2 means the animation frame changes 4 ticks, and
	 * increasing this value by one doubles the wait. 2 is the minimum value
	 * allowed for animation_speed, which corresponds to 120ms, and 16 is the
	 * maximum, corresponding to around 33 minutes. */
	if (_tick_counter % (1 << animation_speed) != 0) return;

	byte frame      = GetHouseAnimationFrame(tile);
	byte num_frames = GB(hs->animation_frames, 0, 7);

	if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_NEXT_FRAME)) {
		uint32 param = (hs->extra_flags & CALLBACK_1A_RANDOM_BITS) ? Random() : 0;
		uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_NEXT_FRAME, param, 0, GetHouseType(tile), Town::GetByTile(tile), tile);

		if (callback_res != CALLBACK_FAILED) {
			frame_set_by_callback = true;

			switch (callback_res & 0xFF) {
				case 0xFF:
					DeleteAnimatedTile(tile);
					break;
				case 0xFE:
					/* Carry on as normal. */
					frame_set_by_callback = false;
					break;
				default:
					frame = callback_res & 0xFF;
					break;
			}

			/* If the lower 7 bits of the upper byte of the callback
			 * result are not empty, it is a sound effect. */
			if (GB(callback_res, 8, 7) != 0) PlayTileSound(hs->grffile, GB(callback_res, 8, 7), tile);
		}
	}

	if (!frame_set_by_callback) {
		if (frame < num_frames) {
			frame++;
		} else if (frame == num_frames && HasBit(hs->animation_frames, 7)) {
			/* This animation loops, so start again from the beginning */
			frame = 0;
		} else {
			/* This animation doesn't loop, so stay here */
			DeleteAnimatedTile(tile);
		}
	}

	SetHouseAnimationFrame(tile, frame);
	MarkTileDirtyByTile(tile);
}
Beispiel #17
0
bool GameObject::IsEnabled()
{
	return HasBit( m_systemFlags, GO_SYSTEMFLAG_ENABLED );
}
Beispiel #18
0
/**
 * Get the tile location group of a tile.
 * @param t The tile to get the tile location group of.
 * @return The tile location group.
 */
static inline TLG GetTLG(TileIndex t)
{
	return (TLG)((HasBit(TileX(t), 0) << 1) + HasBit(TileY(t), 0));
}
/**
 * Does a NewGRF report that this should be an articulated vehicle?
 * @param engine_type The engine to check.
 * @return True iff the articulated engine callback flag is set.
 */
bool IsArticulatedEngine(EngineID engine_type)
{
	return HasBit(EngInfo(engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE);
}
Beispiel #20
0
	FORCEINLINE bool IsTram() {return IsRoadTT() && HasBit(RoadVehicle::From(m_veh)->compatible_roadtypes, ROADTYPE_TRAM);}
Beispiel #21
0
/**
 * Check whether a rail station tile is NOT traversable.
 * @param tile %Tile to test.
 * @return Station tile is blocked.
 * @note This could be cached (during build) in the map array to save on all the dereferencing.
 */
bool IsStationTileBlocked(TileIndex tile)
{
	const StationSpec *statspec = GetStationSpec(tile);

	return statspec != NULL && HasBit(statspec->blocked, GetStationGfx(tile));
}
Beispiel #22
0
/**
 * Tries to find a suitable destination for the given source and cargo.
 * @param cid      Subsidized cargo.
 * @param src_type Type of \a src.
 * @param src      Index of source.
 * @return True iff the subsidy was created.
 */
bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
{
	/* Choose a random destination. Only consider towns if they can accept the cargo. */
	SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;

	CargoSourceSink *src_sink = (src_type == ST_TOWN) ? (CargoSourceSink *) Town::Get(src) : (CargoSourceSink *) Industry::Get(src);

	SourceID dst;
	switch (dst_type) {
		case ST_TOWN: {
			/* Select a random town. */
			const Town *dst_town = NULL;

			if (CargoHasDestinations(cid)) {
				/* Try to get a town from the demand destinations. */
				CargoLink *link = src_sink->GetRandomLink(cid, false);
				if (link == src_sink->cargo_links[cid].End()) return NULL;
				if (link->dest != NULL && link->dest->GetType() != dst_type) return NULL;
				dst_town = static_cast<const Town *>(link->dest);
			}
			if (dst_town == NULL) dst_town = Town::GetRandom();

			/* Check if the town can accept this cargo. */
			if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;

			dst = dst_town->index;
			break;
		}

		case ST_INDUSTRY: {
			/* Select a random industry. */
			const Industry *dst_ind = Industry::GetRandom();

			if (CargoHasDestinations(cid)) {
				/* Try to get a town from the demand destinations. */
				CargoLink *link = src_sink->GetRandomLink(cid, false);
				if (link == src_sink->cargo_links[cid].End()) return NULL;
				if (link->dest != NULL && link->dest->GetType() != dst_type) return NULL;
				dst_ind = static_cast<const Industry *>(link->dest);
			}
			if (dst_ind == NULL) dst_ind = Industry::GetRandom();

			dst = dst_ind->index;

			/* The industry must accept the cargo */
			if (dst_ind == NULL || (src_type == dst_type && src == dst) ||
					(cid != dst_ind->accepts_cargo[0] &&
					 cid != dst_ind->accepts_cargo[1] &&
					 cid != dst_ind->accepts_cargo[2])) {
				return false;
			}
			break;
		}

		default: NOT_REACHED();
	}

	/* Check that the source and the destination are not the same. */
	if (src_type == dst_type && src == dst) return false;

	/* Check distance between source and destination. */
	if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;

	/* Avoid duplicate subsidies. */
	if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;

	CreateSubsidy(cid, src_type, src, dst_type, dst);

	return true;
}
Beispiel #23
0
/**
 * Checks whether the engine is a valid (non-articulated part of an) engine.
 * @return true if enabled
 */
bool Engine::IsEnabled() const
{
	return this->info.string_id != STR_NEWGRF_INVALID_ENGINE && HasBit(this->info.climates, _settings_game.game_creation.landscape);
}
Beispiel #24
0
/**
 * Perform all steps to upgrade from the old waypoints to the new version
 * that uses station. This includes some old saveload mechanics.
 */
void MoveWaypointsToBaseStations()
{
	/* In version 17, ground type is moved from m2 to m4 for depots and
	 * waypoints to make way for storing the index in m2. The custom graphics
	 * id which was stored in m4 is now saved as a grf/id reference in the
	 * waypoint struct. */
	if (IsSavegameVersionBefore(17)) {
		for (OldWaypoint *wp = _old_waypoints.Begin(); wp != _old_waypoints.End(); wp++) {
			if (wp->delete_ctr != 0) continue; // The waypoint was deleted

			/* Waypoint indices were not added to the map prior to this. */
			_m[wp->xy].m2 = (StationID)wp->index;

			if (HasBit(_m[wp->xy].m3, 4)) {
				wp->spec = StationClass::Get(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1);
			}
		}
	} else {
		/* As of version 17, we recalculate the custom graphic ID of waypoints
		 * from the GRF ID / station index. */
		for (OldWaypoint *wp = _old_waypoints.Begin(); wp != _old_waypoints.End(); wp++) {
			for (uint i = 0; i < StationClass::GetCount(STAT_CLASS_WAYP); i++) {
				const StationSpec *statspec =  StationClass::Get(STAT_CLASS_WAYP, i);
				if (statspec != NULL && statspec->grf_prop.grffile->grfid == wp->grfid && statspec->grf_prop.local_id == wp->localidx) {
					wp->spec = statspec;
					break;
				}
			}
		}
	}

	if (!Waypoint::CanAllocateItem(_old_waypoints.Length())) SlError(STR_ERROR_TOO_MANY_STATIONS_LOADING);

	/* All saveload conversions have been done. Create the new waypoints! */
	for (OldWaypoint *wp = _old_waypoints.Begin(); wp != _old_waypoints.End(); wp++) {
		Waypoint *new_wp = new Waypoint(wp->xy);
		new_wp->town       = wp->town;
		new_wp->town_cn    = wp->town_cn;
		new_wp->name       = wp->name;
		new_wp->delete_ctr = 0; // Just reset delete counter for once.
		new_wp->build_date = wp->build_date;
		new_wp->owner      = wp->owner;

		new_wp->string_id = STR_SV_STNAME_WAYPOINT;

		TileIndex t = wp->xy;
		if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _m[t].m2 == wp->index) {
			/* The tile might've been reserved! */
			bool reserved = !IsSavegameVersionBefore(100) && HasBit(_m[t].m5, 4);

			/* The tile really has our waypoint, so reassign the map array */
			MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_m[t].m5, 0, 1), 0, GetRailType(t));
			new_wp->facilities |= FACIL_TRAIN;
			new_wp->owner = GetTileOwner(t);

			SetRailStationReservation(t, reserved);

			if (wp->spec != NULL) {
				SetCustomStationSpecIndex(t, AllocateSpecToStation(wp->spec, new_wp, true));
			}
			new_wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE);
		}

		wp->new_index = new_wp->index;
	}

	/* Update the orders of vehicles */
	OrderList *ol;
	FOR_ALL_ORDER_LISTS(ol) {
		if (ol->GetFirstSharedVehicle()->type != VEH_TRAIN) continue;

		for (Order *o = ol->GetFirstOrder(); o != NULL; o = o->next) UpdateWaypointOrder(o);
	}

	Vehicle *v;
	FOR_ALL_VEHICLES(v) {
		if (v->type != VEH_TRAIN) continue;

		UpdateWaypointOrder(&v->current_order);
	}

	_old_waypoints.Reset();
}
Beispiel #25
0
	bool IsCompatibleRailType(RailType rt)
	{
		return HasBit(m_compatible_railtypes, rt);
	}
Beispiel #26
0
static void ShipController(Ship *v)
{
	uint32 r;
	const byte *b;
	Direction dir;
	Track track;
	TrackBits tracks;

	v->tick_counter++;
	v->current_order_time++;

	if (v->breakdown_ctr != 0) {
		if (v->breakdown_ctr <= 2) {
			HandleBrokenShip(v);
			return;
		}
		if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
	}

	if (v->vehstatus & VS_STOPPED) return;

	ProcessOrders(v);
	v->HandleLoading();

	if (v->current_order.IsType(OT_LOADING)) return;

	CheckShipLeaveDepot(v);

	if (!ShipAccelerate(v)) return;

	GetNewVehiclePosResult gp = GetNewVehiclePos(v);
	if (v->state != TRACK_BIT_WORMHOLE) {
		/* Not on a bridge */
		if (gp.old_tile == gp.new_tile) {
			/* Staying in tile */
			if (v->IsInDepot()) {
				gp.x = v->x_pos;
				gp.y = v->y_pos;
			} else {
				/* Not inside depot */
				r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
				if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;

				/* A leave station order only needs one tick to get processed, so we can
				 * always skip ahead. */
				if (v->current_order.IsType(OT_LEAVESTATION)) {
					v->current_order.Free();
					SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
				} else if (v->dest_tile != 0) {
					/* We have a target, let's see if we reached it... */
					if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
							DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
						/* We got within 3 tiles of our target buoy, so let's skip to our
						 * next order */
						UpdateVehicleTimetable(v, true);
						v->IncrementOrderIndex();
						v->current_order.MakeDummy();
					} else {
						/* Non-buoy orders really need to reach the tile */
						if (v->dest_tile == gp.new_tile) {
							if (v->current_order.IsType(OT_GOTO_DEPOT)) {
								if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
									VehicleEnterDepot(v);
									return;
								}
							} else if (v->current_order.IsType(OT_GOTO_STATION)) {
								v->last_station_visited = v->current_order.GetDestination();

								/* Process station in the orderlist. */
								Station *st = Station::Get(v->current_order.GetDestination());
								if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
									ShipArrivesAt(v, st);
									v->BeginLoading();
								} else { // leave stations without docks right aways
									v->current_order.MakeLeaveStation();
									v->IncrementOrderIndex();
								}
							}
						}
					}
				}
			}
		} else {
			DiagDirection diagdir;
			/* New tile */
			if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
				goto reverse_direction;
			}

			dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
			assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
			diagdir = DirToDiagDir(dir);
			tracks = GetAvailShipTracks(gp.new_tile, diagdir);
			if (tracks == TRACK_BIT_NONE) goto reverse_direction;

			/* Choose a direction, and continue if we find one */
			track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
			if (track == INVALID_TRACK) goto reverse_direction;

			b = _ship_subcoord[diagdir][track];

			gp.x = (gp.x & ~0xF) | b[0];
			gp.y = (gp.y & ~0xF) | b[1];

			/* Call the landscape function and tell it that the vehicle entered the tile */
			r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
			if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;

			if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
				v->tile = gp.new_tile;
				v->state = TrackToTrackBits(track);
			}

			v->direction = (Direction)b[2];
		}
	} else {
		/* On a bridge */
		if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
			v->x_pos = gp.x;
			v->y_pos = gp.y;
			VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
			return;
		}
	}

	/* update image of ship, as well as delta XY */
	dir = ShipGetNewDirection(v, gp.x, gp.y);
	v->x_pos = gp.x;
	v->y_pos = gp.y;
	v->z_pos = GetSlopeZ(gp.x, gp.y);

getout:
	v->UpdateViewport(true, true);
	return;

reverse_direction:
	dir = ReverseDir(v->direction);
	v->direction = dir;
	goto getout;
}
const Sprite *GetGlyph(FontSize size, WChar key)
{
	FT_Face face = GetFontFace(size);
	FT_GlyphSlot slot;
	GlyphEntry new_glyph;
	GlyphEntry *glyph;
	SpriteLoader::Sprite sprite;
	int width;
	int height;
	int x;
	int y;

	assert(IsPrintable(key));

	/* Bail out if no face loaded, or for our special characters */
	if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
		SpriteID sprite = GetUnicodeGlyph(size, key);
		if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
		return GetSprite(sprite, ST_FONT);
	}

	/* Check for the glyph in our cache */
	glyph = GetGlyphPtr(size, key);
	if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;

	slot = face->glyph;

	bool aa = GetFontAAState(size);

	FT_UInt glyph_index = FT_Get_Char_Index(face, key);
	if (glyph_index == 0) {
		if (key == '?') {
			/* The font misses the '?' character. Use sprite font. */
			SpriteID sprite = GetUnicodeGlyph(size, key);
			Sprite *spr = (Sprite*)GetRawSprite(sprite, ST_FONT, AllocateFont);
			assert(spr != NULL);
			new_glyph.sprite = spr;
			new_glyph.width  = spr->width + (size != FS_NORMAL);
			SetGlyphPtr(size, key, &new_glyph, false);
			return new_glyph.sprite;
		} else {
			/* Use '?' for missing characters. */
			GetGlyph(size, '?');
			glyph = GetGlyphPtr(size, '?');
			SetGlyphPtr(size, key, glyph, true);
			return glyph->sprite;
		}
	}
	FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
	FT_Render_Glyph(face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);

	/* Despite requesting a normal glyph, FreeType may have returned a bitmap */
	aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);

	/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
	width  = max(1, slot->bitmap.width + (size == FS_NORMAL));
	height = max(1, slot->bitmap.rows  + (size == FS_NORMAL));

	/* Limit glyph size to prevent overflows later on. */
	if (width > 256 || height > 256) usererror("Font glyph is too large");

	/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
	sprite.AllocateData(width * height);
	sprite.width = width;
	sprite.height = height;
	sprite.x_offs = slot->bitmap_left;
	sprite.y_offs = _ascender[size] - slot->bitmap_top;

	/* Draw shadow for medium size */
	if (size == FS_NORMAL) {
		for (y = 0; y < slot->bitmap.rows; y++) {
			for (x = 0; x < slot->bitmap.width; x++) {
				if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
					sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR;
					sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
				}
			}
		}
	}

	for (y = 0; y < slot->bitmap.rows; y++) {
		for (x = 0; x < slot->bitmap.width; x++) {
			if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
				sprite.data[x + y * sprite.width].m = FACE_COLOUR;
				sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
			}
		}
	}

	new_glyph.sprite = BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, AllocateFont);
	new_glyph.width  = slot->advance.x >> 6;

	SetGlyphPtr(size, key, &new_glyph);

	return new_glyph.sprite;
}
/* static */ bool AITown::IsActionAvailable(TownID town_id, TownAction town_action)
{
	if (!IsValidTown(town_id)) return false;

	return HasBit(::GetMaskOfTownActions(NULL, _current_company, ::Town::Get(town_id)), town_action);
}
Beispiel #29
0
/**
 * Additional map variety is provided by applying different curve maps
 * to different parts of the map. A randomized low resolution grid contains
 * which curve map to use on each part of the make. This filtered non-linearly
 * to smooth out transitions between curves, so each tile could have between
 * 100% of one map applied or 25% of four maps.
 *
 * The curve maps define different land styles, i.e. lakes, low-lands, hills
 * and mountain ranges, although these are dependent on the landscape style
 * chosen as well.
 *
 * The level parameter dictates the resolution of the grid. A low resolution
 * grid will result in larger continuous areas of a land style, a higher
 * resolution grid splits the style into smaller areas.
 * @param level Rough indication of the size of the grid sections to style. Small level means large grid sections.
 */
static void HeightMapCurves(uint level)
{
	height_t mh = TGPGetMaxHeight() - I2H(1); // height levels above sea level only

	/** Basically scale height X to height Y. Everything in between is interpolated. */
	struct control_point_t {
		height_t x; ///< The height to scale from.
		height_t y; ///< The height to scale to.
	};
	/* Scaled curve maps; value is in height_ts. */
#define F(fraction) ((height_t)(fraction * mh))
	const control_point_t curve_map_1[] = { { F(0.0), F(0.0) },                       { F(0.8), F(0.13) },                       { F(1.0), F(0.4)  } };
	const control_point_t curve_map_2[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.13) }, { F(0.8), F(0.27) },                       { F(1.0), F(0.6)  } };
	const control_point_t curve_map_3[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.27) }, { F(0.8), F(0.57) },                       { F(1.0), F(0.8)  } };
	const control_point_t curve_map_4[] = { { F(0.0), F(0.0) }, { F(0.4),  F(0.3)  }, { F(0.7), F(0.8)  }, { F(0.92), F(0.99) }, { F(1.0), F(0.99) } };
#undef F

	/** Helper structure to index the different curve maps. */
	struct control_point_list_t {
		size_t length;               ///< The length of the curve map.
		const control_point_t *list; ///< The actual curve map.
	};
	const control_point_list_t curve_maps[] = {
		{ lengthof(curve_map_1), curve_map_1 },
		{ lengthof(curve_map_2), curve_map_2 },
		{ lengthof(curve_map_3), curve_map_3 },
		{ lengthof(curve_map_4), curve_map_4 },
	};

	height_t ht[lengthof(curve_maps)];
	MemSetT(ht, 0, lengthof(ht));

	/* Set up a grid to choose curve maps based on location; attempt to get a somewhat square grid */
	float factor = sqrt((float)_height_map.size_x / (float)_height_map.size_y);
	uint sx = Clamp((int)(((1 << level) * factor) + 0.5), 1, 128);
	uint sy = Clamp((int)(((1 << level) / factor) + 0.5), 1, 128);
	byte *c = AllocaM(byte, sx * sy);

	for (uint i = 0; i < sx * sy; i++) {
		c[i] = Random() % lengthof(curve_maps);
	}

	/* Apply curves */
	for (int x = 0; x < _height_map.size_x; x++) {

		/* Get our X grid positions and bi-linear ratio */
		float fx = (float)(sx * x) / _height_map.size_x + 1.0f;
		uint x1 = (uint)fx;
		uint x2 = x1;
		float xr = 2.0f * (fx - x1) - 1.0f;
		xr = sin(xr * M_PI_2);
		xr = sin(xr * M_PI_2);
		xr = 0.5f * (xr + 1.0f);
		float xri = 1.0f - xr;

		if (x1 > 0) {
			x1--;
			if (x2 >= sx) x2--;
		}

		for (int y = 0; y < _height_map.size_y; y++) {

			/* Get our Y grid position and bi-linear ratio */
			float fy = (float)(sy * y) / _height_map.size_y + 1.0f;
			uint y1 = (uint)fy;
			uint y2 = y1;
			float yr = 2.0f * (fy - y1) - 1.0f;
			yr = sin(yr * M_PI_2);
			yr = sin(yr * M_PI_2);
			yr = 0.5f * (yr + 1.0f);
			float yri = 1.0f - yr;

			if (y1 > 0) {
				y1--;
				if (y2 >= sy) y2--;
			}

			uint corner_a = c[x1 + sx * y1];
			uint corner_b = c[x1 + sx * y2];
			uint corner_c = c[x2 + sx * y1];
			uint corner_d = c[x2 + sx * y2];

			/* Bitmask of which curve maps are chosen, so that we do not bother
			 * calculating a curve which won't be used. */
			uint corner_bits = 0;
			corner_bits |= 1 << corner_a;
			corner_bits |= 1 << corner_b;
			corner_bits |= 1 << corner_c;
			corner_bits |= 1 << corner_d;

			height_t *h = &_height_map.height(x, y);

			/* Do not touch sea level */
			if (*h < I2H(1)) continue;

			/* Only scale above sea level */
			*h -= I2H(1);

			/* Apply all curve maps that are used on this tile. */
			for (uint t = 0; t < lengthof(curve_maps); t++) {
				if (!HasBit(corner_bits, t)) continue;

				bool found = false;
				const control_point_t *cm = curve_maps[t].list;
				for (uint i = 0; i < curve_maps[t].length - 1; i++) {
					const control_point_t &p1 = cm[i];
					const control_point_t &p2 = cm[i + 1];

					if (*h >= p1.x && *h < p2.x) {
						ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
						found = true;
						break;
					}
				}
				assert(found);
			}

			/* Apply interpolation of curve map results. */
			*h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr);

			/* Readd sea level */
			*h += I2H(1);
		}
	}
}
Beispiel #30
0
void UpdateVehicleTimetable(Vehicle *v, bool travelling)
{
	uint timetabled = travelling ? v->current_order.travel_time : v->current_order.wait_time;
	uint time_taken = v->current_order_time;

	v->current_order_time = 0;

	if (!_settings_game.order.timetabling) return;

	bool just_started = false;

	/* This vehicle is arriving at the first destination in the timetable. */
	if (v->cur_order_index == 0 && travelling) {
		/* If the start date hasn't been set, or it was set automatically when
		 * the vehicle last arrived at the first destination, update it to the
		 * current time. Otherwise set the late counter appropriately to when
		 * the vehicle should have arrived. */
		just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);

		if (v->timetable_start != 0) {
			v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
			v->timetable_start = 0;
		}

		SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
	}

	if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;

	if (HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) {
		if (travelling && !HasBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) {
			/* Need to clear that now as otherwise we are not able to reduce the wait time */
			v->current_order.wait_time = 0;
		}

		if (just_started) return;

		/* Modify station waiting time only if our new value is larger (this is
		 * always the case when we cleared the timetable). */
		if (!v->current_order.IsType(OT_CONDITIONAL) && (travelling || time_taken > v->current_order.wait_time)) {
			/* Round the time taken up to the nearest day, as this will avoid
			 * confusion for people who are timetabling in days, and can be
			 * adjusted later by people who aren't. */
			time_taken = CeilDiv(time_taken, DAY_TICKS) * DAY_TICKS;

			ChangeTimetable(v, v->cur_order_index, time_taken, travelling);
		}

		if (v->cur_order_index == 0 && travelling) {
			/* If we just started we would have returned earlier and have not reached
			 * this code. So obviously, we have completed our round: So turn autofill
			 * off again. */
			ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
			ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
		}
		return;
	}

	if (just_started) return;

	/* Vehicles will wait at stations if they arrive early even if they are not
	 * timetabled to wait there, so make sure the lateness counter is updated
	 * when this happens. */
	if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;

	v->lateness_counter -= (timetabled - time_taken);

	for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
	}
}