File diff r23952:2d92a4a7c152 → r23953:ba75d2f3b530
src/engine.cpp
Show inline comments
 
@@ -658,201 +658,198 @@ void StartupOneEngine(Engine *e, Date ag
 
	              e->type ^
 
	              e->GetGRFID());
 
	uint32 r = Random();
 

	
 
	e->intro_date = ei->base_intro <= ConvertYMDToDate(_settings_game.game_creation.starting_year + 2, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
 
	if (e->intro_date <= _date) {
 
		e->age = (aging_date - e->intro_date) >> 5;
 
		e->company_avail = (CompanyMask)-1;
 
		e->flags |= ENGINE_AVAILABLE;
 
	}
 

	
 
	e->reliability_start = GB(r, 16, 14) + 0x7AE0;
 
	r = Random();
 
	e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
 
	e->reliability_final = GB(r, 16, 14) + 0x3FFF;
 

	
 
	r = Random();
 
	e->duration_phase_1 = GB(r, 0, 5) + 7;
 
	e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
 
	e->duration_phase_3 = GB(r, 9, 7) + 120;
 

	
 
	e->reliability_spd_dec = ei->decay_speed << 2;
 

	
 
	RestoreRandomSeeds(saved_seeds);
 
	CalcEngineReliability(e);
 

	
 
	/* prevent certain engines from ever appearing. */
 
	if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
 
		e->flags |= ENGINE_AVAILABLE;
 
		e->company_avail = 0;
 
	}
 
}
 

	
 
/**
 
 * Start/initialise all our engines. Must be called whenever there are changes
 
 * to the NewGRF config.
 
 */
 
void StartupEngines()
 
{
 
	Engine *e;
 
	/* Aging of vehicles stops, so account for that when starting late */
 
	const Date aging_date = min(_date, ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
 

	
 
	FOR_ALL_ENGINES(e) {
 
		StartupOneEngine(e, aging_date);
 
	}
 

	
 
	/* Update the bitmasks for the vehicle lists */
 
	Company *c;
 
	FOR_ALL_COMPANIES(c) {
 
	for (Company *c : Company::Iterate()) {
 
		c->avail_railtypes = GetCompanyRailtypes(c->index);
 
		c->avail_roadtypes = GetCompanyRoadTypes(c->index);
 
	}
 

	
 
	/* Invalidate any open purchase lists */
 
	InvalidateWindowClassesData(WC_BUILD_VEHICLE);
 
}
 

	
 
/**
 
 * Company \a company accepts engine \a eid for preview.
 
 * @param eid Engine being accepted (is under preview).
 
 * @param company Current company previewing the engine.
 
 */
 
static void AcceptEnginePreview(EngineID eid, CompanyID company)
 
{
 
	Engine *e = Engine::Get(eid);
 
	Company *c = Company::Get(company);
 

	
 
	SetBit(e->company_avail, company);
 
	if (e->type == VEH_TRAIN) {
 
		assert(e->u.rail.railtype < RAILTYPE_END);
 
		c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
 
	} else if (e->type == VEH_ROAD) {
 
		assert(e->u.road.roadtype < ROADTYPE_END);
 
		c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date);
 
	}
 

	
 
	e->preview_company = INVALID_COMPANY;
 
	e->preview_asked = (CompanyMask)-1;
 
	if (company == _local_company) {
 
		AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
 
	}
 

	
 
	/* Update the toolbar. */
 
	if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
 
	if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
 

	
 
	/* Notify preview window, that it might want to close.
 
	 * Note: We cannot directly close the window.
 
	 *       In singleplayer this function is called from the preview window, so
 
	 *       we have to use the GUI-scope scheduling of InvalidateWindowData.
 
	 */
 
	InvalidateWindowData(WC_ENGINE_PREVIEW, eid);
 
}
 

	
 
/**
 
 * Get the best company for an engine preview.
 
 * @param e Engine to preview.
 
 * @return Best company if it exists, #INVALID_COMPANY otherwise.
 
 */
 
static CompanyID GetPreviewCompany(Engine *e)
 
{
 
	CompanyID best_company = INVALID_COMPANY;
 

	
 
	/* For trains the cargomask has no useful meaning, since you can attach other wagons */
 
	CargoTypes cargomask = e->type != VEH_TRAIN ? GetUnionOfArticulatedRefitMasks(e->index, true) : ALL_CARGOTYPES;
 

	
 
	int32 best_hist = -1;
 
	const Company *c;
 
	FOR_ALL_COMPANIES(c) {
 
	for (const Company *c : Company::Iterate()) {
 
		if (c->block_preview == 0 && !HasBit(e->preview_asked, c->index) &&
 
				c->old_economy[0].performance_history > best_hist) {
 

	
 
			/* Check whether the company uses similar vehicles */
 
			Vehicle *v;
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->owner != c->index || v->type != e->type) continue;
 
				if (!v->GetEngine()->CanCarryCargo() || !HasBit(cargomask, v->cargo_type)) continue;
 

	
 
				best_hist = c->old_economy[0].performance_history;
 
				best_company = c->index;
 
				break;
 
			}
 
		}
 
	}
 

	
 
	return best_company;
 
}
 

	
 
/**
 
 * Checks if a vehicle type is disabled for all/ai companies.
 
 * @param type The vehicle type which shall be checked.
 
 * @param ai If true, check if the type is disabled for AI companies, otherwise check if
 
 *           the vehicle type is disabled for human companies.
 
 * @return Whether or not a vehicle type is disabled.
 
 */
 
static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
 
{
 
	switch (type) {
 
		case VEH_TRAIN:    return _settings_game.vehicle.max_trains == 0   || (ai && _settings_game.ai.ai_disable_veh_train);
 
		case VEH_ROAD:     return _settings_game.vehicle.max_roadveh == 0  || (ai && _settings_game.ai.ai_disable_veh_roadveh);
 
		case VEH_SHIP:     return _settings_game.vehicle.max_ships == 0    || (ai && _settings_game.ai.ai_disable_veh_ship);
 
		case VEH_AIRCRAFT: return _settings_game.vehicle.max_aircraft == 0 || (ai && _settings_game.ai.ai_disable_veh_aircraft);
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/** Daily check to offer an exclusive engine preview to the companies. */
 
void EnginesDailyLoop()
 
{
 
	Company *c;
 
	FOR_ALL_COMPANIES(c) {
 
	for (Company *c : Company::Iterate()) {
 
		c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date);
 
		c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, _date);
 
	}
 

	
 
	if (_cur_year >= _year_engine_aging_stops) return;
 

	
 
	Engine *e;
 
	FOR_ALL_ENGINES(e) {
 
		EngineID i = e->index;
 
		if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
 
			if (e->preview_company != INVALID_COMPANY) {
 
				if (!--e->preview_wait) {
 
					DeleteWindowById(WC_ENGINE_PREVIEW, i);
 
					e->preview_company = INVALID_COMPANY;
 
				}
 
			} else if (CountBits(e->preview_asked) < MAX_COMPANIES) {
 
				e->preview_company = GetPreviewCompany(e);
 

	
 
				if (e->preview_company == INVALID_COMPANY) {
 
					e->preview_asked = (CompanyMask)-1;
 
					continue;
 
				}
 

	
 
				SetBit(e->preview_asked, e->preview_company);
 
				e->preview_wait = 20;
 
				/* AIs are intentionally not skipped for preview even if they cannot build a certain
 
				 * vehicle type. This is done to not give poor performing human companies an "unfair"
 
				 * boost that they wouldn't have gotten against other human companies. The check on
 
				 * the line below is just to make AIs not notice that they have a preview if they
 
				 * cannot build the vehicle. */
 
				if (!IsVehicleTypeDisabled(e->type, true)) AI::NewEvent(e->preview_company, new ScriptEventEnginePreview(i));
 
				if (IsInteractiveCompany(e->preview_company)) ShowEnginePreviewWindow(i);
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Clear the 'hidden' flag for all engines of a new company.
 
 * @param cid Company being created.
 
 */
 
void ClearEnginesHiddenFlagOfCompany(CompanyID cid)
 
{
 
	Engine *e;
 
	FOR_ALL_ENGINES(e) {
 
		SB(e->company_hidden, cid, 1, 0);
 
	}
 
}
 
@@ -863,141 +860,140 @@ void ClearEnginesHiddenFlagOfCompany(Com
 
 * @param flags Operation to perform.
 
 * @param p1 Unused.
 
 * @param p2 Bit 31: 0=visible, 1=hidden, other bits for the #EngineID.
 
 * @param text Unused.
 
 * @return The cost of this operation or an error.
 
 */
 
CommandCost CmdSetVehicleVisibility(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	Engine *e = Engine::GetIfValid(GB(p2, 0, 31));
 
	if (e == nullptr || _current_company >= MAX_COMPANIES) return CMD_ERROR;
 
	if (!IsEngineBuildable(e->index, e->type, _current_company)) return CMD_ERROR;
 

	
 
	if ((flags & DC_EXEC) != 0) {
 
		SB(e->company_hidden, _current_company, 1, GB(p2, 31, 1));
 
		AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Accept an engine prototype. XXX - it is possible that the top-company
 
 * changes while you are waiting to accept the offer? Then it becomes invalid
 
 * @param tile unused
 
 * @param flags operation to perform
 
 * @param p1 engine-prototype offered
 
 * @param p2 unused
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdWantEnginePreview(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	Engine *e = Engine::GetIfValid(p1);
 
	if (e == nullptr || !(e->flags & ENGINE_EXCLUSIVE_PREVIEW) || e->preview_company != _current_company) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * An engine has become available for general use.
 
 * Also handle the exclusive engine preview contract.
 
 * @param e Engine generally available as of now.
 
 */
 
static void NewVehicleAvailable(Engine *e)
 
{
 
	Vehicle *v;
 
	Company *c;
 
	EngineID index = e->index;
 

	
 
	/* In case the company didn't build the vehicle during the intro period,
 
	 * prevent that company from getting future intro periods for a while. */
 
	if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
 
		FOR_ALL_COMPANIES(c) {
 
		for (Company *c : Company::Iterate()) {
 
			uint block_preview = c->block_preview;
 

	
 
			if (!HasBit(e->company_avail, c->index)) continue;
 

	
 
			/* We assume the user did NOT build it.. prove me wrong ;) */
 
			c->block_preview = 20;
 

	
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
 
						(v->type == VEH_AIRCRAFT && Aircraft::From(v)->IsNormalAircraft())) {
 
					if (v->owner == c->index && v->engine_type == index) {
 
						/* The user did prove me wrong, so restore old value */
 
						c->block_preview = block_preview;
 
						break;
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
 
	AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
 

	
 
	/* Now available for all companies */
 
	e->company_avail = (CompanyMask)-1;
 

	
 
	/* Do not introduce new rail wagons */
 
	if (IsWagon(index)) return;
 

	
 
	if (e->type == VEH_TRAIN) {
 
		/* maybe make another rail type available */
 
		RailType railtype = e->u.rail.railtype;
 
		assert(railtype < RAILTYPE_END);
 
		FOR_ALL_COMPANIES(c) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
 
		for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
 
	} else if (e->type == VEH_ROAD) {
 
		/* maybe make another road type available */
 
		assert(e->u.road.roadtype < ROADTYPE_END);
 
		FOR_ALL_COMPANIES(c) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date);
 
		for (Company* c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date);
 
	}
 

	
 
	/* Only broadcast event if AIs are able to build this vehicle type. */
 
	if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index));
 

	
 
	/* Only provide the "New Vehicle available" news paper entry, if engine can be built. */
 
	if (!IsVehicleTypeDisabled(e->type, false)) {
 
		SetDParam(0, GetEngineCategoryName(index));
 
		SetDParam(1, index);
 
		AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NT_NEW_VEHICLES, NF_VEHICLE, NR_ENGINE, index);
 
	}
 

	
 
	/* Update the toolbar. */
 
	if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
 
	if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
 

	
 
	/* Close pending preview windows */
 
	DeleteWindowById(WC_ENGINE_PREVIEW, index);
 
}
 

	
 
/** Monthly update of the availability, reliability, and preview offers of the engines. */
 
void EnginesMonthlyLoop()
 
{
 
	if (_cur_year < _year_engine_aging_stops) {
 
		Engine *e;
 
		FOR_ALL_ENGINES(e) {
 
			/* Age the vehicle */
 
			if ((e->flags & ENGINE_AVAILABLE) && e->age != MAX_DAY) {
 
				e->age++;
 
				CalcEngineReliability(e);
 
			}
 

	
 
			/* Do not introduce invalid engines */
 
			if (!e->IsEnabled()) continue;
 

	
 
			if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + DAYS_IN_YEAR)) {
 
				/* Introduce it to all companies */
 
				NewVehicleAvailable(e);
 
			} else if (!(e->flags & (ENGINE_AVAILABLE | ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
 
				/* Introduction date has passed...
 
				 * Check if it is allowed to build this vehicle type at all
 
				 * based on the current game settings. If not, it does not
 
				 * make sense to show the preview dialog to any company. */
 
				if (IsVehicleTypeDisabled(e->type, false)) continue;
 

	
 
				/* Do not introduce new rail wagons */
 
				if (IsWagon(e->index)) continue;