Merge branch 'master' into cpp-11
# Conflicts: # config.lib # projects/openttd_vs100.vcxproj # projects/openttd_vs100.vcxproj.filters # projects/openttd_vs80.vcproj # projects/openttd_vs90.vcproj # src/saveload/saveload.cpp
This commit is contained in:
2
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
2
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
@@ -115,7 +115,7 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty
|
||||
SQInteger found = 0;
|
||||
for(SQInteger i=0; i<16; i++)
|
||||
{
|
||||
SQInteger mask = 0x00000001 << i;
|
||||
SQInteger mask = 0x00000001LL << i;
|
||||
if(typemask & (mask)) {
|
||||
if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes);
|
||||
found ++;
|
||||
|
2
src/3rdparty/squirrel/squirrel/squtils.h
vendored
2
src/3rdparty/squirrel/squirrel/squtils.h
vendored
@@ -90,7 +90,7 @@ public:
|
||||
{
|
||||
_vals[idx].~T();
|
||||
if(idx < (_size - 1)) {
|
||||
memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1));
|
||||
memmove(static_cast<void *>(&_vals[idx]), &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1));
|
||||
}
|
||||
_size--;
|
||||
}
|
||||
|
6
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
6
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
@@ -378,8 +378,7 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege
|
||||
}
|
||||
|
||||
if (!tailcall) {
|
||||
CallInfo lc;
|
||||
memset(&lc, 0, sizeof(lc));
|
||||
CallInfo lc = {};
|
||||
lc._generator = NULL;
|
||||
lc._etraps = 0;
|
||||
lc._prevstkbase = (SQInt32) ( stackbase - _stackbase );
|
||||
@@ -1159,8 +1158,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackb
|
||||
SQInteger oldtop = _top;
|
||||
SQInteger oldstackbase = _stackbase;
|
||||
_top = stackbase + nargs;
|
||||
CallInfo lci;
|
||||
memset(&lci, 0, sizeof(lci));
|
||||
CallInfo lci = {};
|
||||
lci._closure = nclosure;
|
||||
lci._generator = NULL;
|
||||
lci._etraps = 0;
|
||||
|
@@ -32,9 +32,9 @@ static bool CheckAPIVersion(const char *api_version)
|
||||
strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
#undef GetClassName
|
||||
#endif /* WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
|
||||
|
||||
/* static */ void AIInfo::RegisterAPI(Squirrel *engine)
|
||||
|
@@ -45,6 +45,8 @@ enum AirVehicleFlags {
|
||||
* landscape at a fixed altitude. This only has effect when there are more than 15 height levels. */
|
||||
VAF_IN_MAX_HEIGHT_CORRECTION = 1, ///< The vehicle is currently lowering its altitude because it hit the upper bound.
|
||||
VAF_IN_MIN_HEIGHT_CORRECTION = 2, ///< The vehicle is currently raising its altitude because it hit the lower bound.
|
||||
|
||||
VAF_HELI_DIRECT_DESCENT = 3, ///< The helicopter is descending directly at its destination (helipad or in front of hangar)
|
||||
};
|
||||
|
||||
static const int ROTOR_Z_OFFSET = 5; ///< Z Offset between helicopter- and rotorsprite.
|
||||
@@ -90,7 +92,7 @@ struct Aircraft FINAL : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
|
||||
virtual ~Aircraft() { this->PreDestructor(); }
|
||||
|
||||
void MarkDirty();
|
||||
void UpdateDeltaXY(Direction direction);
|
||||
void UpdateDeltaXY();
|
||||
ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_AIRCRAFT_INC : EXPENSES_AIRCRAFT_RUN; }
|
||||
bool IsPrimaryVehicle() const { return this->IsNormalAircraft(); }
|
||||
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const;
|
||||
@@ -137,7 +139,7 @@ struct Aircraft FINAL : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
|
||||
};
|
||||
|
||||
/**
|
||||
* Macro for iterating over all aircrafts.
|
||||
* Macro for iterating over all aircraft.
|
||||
*/
|
||||
#define FOR_ALL_AIRCRAFT(var) FOR_ALL_VEHICLES_OF_TYPE(Aircraft, var)
|
||||
|
||||
|
@@ -37,12 +37,13 @@
|
||||
#include "core/backup_type.hpp"
|
||||
#include "zoom_func.h"
|
||||
#include "disaster_vehicle.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
void Aircraft::UpdateDeltaXY(Direction direction)
|
||||
void Aircraft::UpdateDeltaXY()
|
||||
{
|
||||
this->x_offs = -1;
|
||||
this->y_offs = -1;
|
||||
@@ -247,7 +248,7 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff
|
||||
* @param flags type of operation.
|
||||
* @param e the engine to build.
|
||||
* @param data unused.
|
||||
* @param ret[out] the vehicle that has been built.
|
||||
* @param[out] ret the vehicle that has been built.
|
||||
* @return the cost of this operation or an error.
|
||||
*/
|
||||
CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
|
||||
@@ -303,10 +304,10 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
|
||||
u->engine_type = e->index;
|
||||
|
||||
v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
|
||||
u->subtype = AIR_SHADOW;
|
||||
u->UpdateDeltaXY(INVALID_DIR);
|
||||
u->UpdateDeltaXY();
|
||||
|
||||
v->reliability = e->reliability;
|
||||
v->reliability_spd_dec = e->reliability_spd_dec;
|
||||
@@ -363,7 +364,7 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
|
||||
w->random_bits = VehicleRandomBits();
|
||||
/* Use rotor's air.state to store the rotor animation frame */
|
||||
w->state = HRS_ROTOR_STOPPED;
|
||||
w->UpdateDeltaXY(INVALID_DIR);
|
||||
w->UpdateDeltaXY();
|
||||
|
||||
u->SetNext(w);
|
||||
w->UpdatePosition();
|
||||
@@ -634,6 +635,12 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE,
|
||||
* and take-off speeds being too low. */
|
||||
speed_limit *= _settings_game.vehicle.plane_speed;
|
||||
|
||||
/* adjust speed for broken vehicles */
|
||||
if (v->vehstatus & VS_AIRCRAFT_BROKEN) {
|
||||
if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false;
|
||||
speed_limit = min(speed_limit, SPEED_LIMIT_BROKEN);
|
||||
}
|
||||
|
||||
if (v->vcache.cached_max_speed < speed_limit) {
|
||||
if (v->cur_speed < speed_limit) hard_limit = false;
|
||||
speed_limit = v->vcache.cached_max_speed;
|
||||
@@ -653,9 +660,6 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE,
|
||||
|
||||
spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
|
||||
|
||||
/* adjust speed for broken vehicles */
|
||||
if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
|
||||
|
||||
/* updates statusbar only if speed have changed to save CPU time */
|
||||
if (spd != v->cur_speed) {
|
||||
v->cur_speed = spd;
|
||||
@@ -693,9 +697,9 @@ int GetTileHeightBelowAircraft(const Vehicle *v)
|
||||
* When the maximum is reached the vehicle should consider descending.
|
||||
* When the minimum is reached the vehicle should consider ascending.
|
||||
*
|
||||
* @param v The vehicle to get the flight levels for.
|
||||
* @param [out] min_level The minimum bounds for flight level.
|
||||
* @param [out] max_level The maximum bounds for flight level.
|
||||
* @param v The vehicle to get the flight levels for.
|
||||
* @param[out] min_level The minimum bounds for flight level.
|
||||
* @param[out] max_level The maximum bounds for flight level.
|
||||
*/
|
||||
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
|
||||
{
|
||||
@@ -727,7 +731,7 @@ void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_lev
|
||||
|
||||
/**
|
||||
* Gets the maximum 'flight level' for the holding pattern of the aircraft,
|
||||
* in pixels 'z_pos' 0, depending on terrain below..
|
||||
* in pixels 'z_pos' 0, depending on terrain below.
|
||||
*
|
||||
* @param v The aircraft that may or may not need to decrease its altitude.
|
||||
* @return Maximal aircraft holding altitude, while in normal flight, in pixels.
|
||||
@@ -749,7 +753,7 @@ int GetAircraftFlightLevel(T *v, bool takeoff)
|
||||
GetAircraftFlightLevelBounds(v, &aircraft_min_altitude, &aircraft_max_altitude);
|
||||
int aircraft_middle_altitude = (aircraft_min_altitude + aircraft_max_altitude) / 2;
|
||||
|
||||
/* If those assumptions would be violated, aircrafts would behave fairly strange. */
|
||||
/* If those assumptions would be violated, aircraft would behave fairly strange. */
|
||||
assert(aircraft_min_altitude < aircraft_middle_altitude);
|
||||
assert(aircraft_middle_altitude < aircraft_max_altitude);
|
||||
|
||||
@@ -914,6 +918,8 @@ static bool AircraftController(Aircraft *v)
|
||||
|
||||
/* Helicopter landing. */
|
||||
if (amd.flag & AMED_HELI_LOWER) {
|
||||
SetBit(v->flags, VAF_HELI_DIRECT_DESCENT);
|
||||
|
||||
if (st == NULL) {
|
||||
/* FIXME - AircraftController -> if station no longer exists, do not land
|
||||
* helicopter will circle until sign disappears, then go to next order
|
||||
@@ -934,7 +940,10 @@ static bool AircraftController(Aircraft *v)
|
||||
Vehicle *u = v->Next()->Next();
|
||||
|
||||
/* Increase speed of rotors. When speed is 80, we've landed. */
|
||||
if (u->cur_speed >= 80) return true;
|
||||
if (u->cur_speed >= 80) {
|
||||
ClrBit(v->flags, VAF_HELI_DIRECT_DESCENT);
|
||||
return true;
|
||||
}
|
||||
u->cur_speed += 4;
|
||||
} else {
|
||||
count = UpdateAircraftSpeed(v);
|
||||
@@ -1361,7 +1370,7 @@ static void AircraftEntersTerminal(Aircraft *v)
|
||||
*/
|
||||
static void AircraftLandAirplane(Aircraft *v)
|
||||
{
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
|
||||
if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
|
||||
SndPlayVehicleFx(SND_17_SKID_PLANE, v);
|
||||
@@ -1553,7 +1562,7 @@ static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc
|
||||
static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
|
||||
{
|
||||
v->state = ENDTAKEOFF;
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
}
|
||||
|
||||
static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
|
||||
@@ -1566,7 +1575,7 @@ static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *
|
||||
static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
|
||||
{
|
||||
v->state = FLYING;
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
|
||||
/* get the next position to go to, differs per airport */
|
||||
AircraftNextAirportPos_and_Order(v);
|
||||
@@ -1599,6 +1608,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
|
||||
uint16 tsubspeed = v->subspeed;
|
||||
if (!AirportHasBlock(v, current, apc)) {
|
||||
v->state = landingtype; // LANDING / HELILANDING
|
||||
if (v->state == HELILANDING) SetBit(v->flags, VAF_HELI_DIRECT_DESCENT);
|
||||
/* it's a bit dirty, but I need to set position to next position, otherwise
|
||||
* if there are multiple runways, plane won't know which one it took (because
|
||||
* they all have heading LANDING). And also occupy that block! */
|
||||
@@ -1632,7 +1642,7 @@ static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc
|
||||
static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
|
||||
{
|
||||
v->state = HELIENDLANDING;
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
}
|
||||
|
||||
static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
|
||||
@@ -1888,7 +1898,7 @@ static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
|
||||
|
||||
/**
|
||||
* Get the number of terminals at the airport.
|
||||
* @param afc Airport description.
|
||||
* @param apc Airport description.
|
||||
* @return Number of terminals.
|
||||
*/
|
||||
static uint GetNumTerminals(const AirportFTAClass *apc)
|
||||
@@ -2038,6 +2048,8 @@ bool Aircraft::Tick()
|
||||
{
|
||||
if (!this->IsNormalAircraft()) return true;
|
||||
|
||||
PerformanceAccumulator framerate(PFE_GL_AIRCRAFT);
|
||||
|
||||
this->tick_counter++;
|
||||
|
||||
if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "core/smallvec_type.hpp"
|
||||
#include "tile_cmd.h"
|
||||
#include "viewport_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
@@ -50,6 +51,8 @@ void AddAnimatedTile(TileIndex tile)
|
||||
*/
|
||||
void AnimateAnimatedTiles()
|
||||
{
|
||||
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
|
||||
|
||||
const TileIndex *ti = _animated_tiles.Begin();
|
||||
while (ti < _animated_tiles.End()) {
|
||||
const TileIndex curr = *ti;
|
||||
|
@@ -119,12 +119,12 @@ static inline uint16 GetVehicleDefaultCapacity(EngineID engine, CargoID *cargo_t
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit set of CargoIDs
|
||||
*/
|
||||
static inline uint32 GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type)
|
||||
static inline CargoTypes GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
if (!e->CanCarryCargo()) return 0;
|
||||
|
||||
uint32 cargoes = e->info.refit_mask;
|
||||
CargoTypes cargoes = e->info.refit_mask;
|
||||
|
||||
if (include_initial_cargo_type) {
|
||||
SetBit(cargoes, e->GetDefaultCargoType());
|
||||
@@ -169,7 +169,7 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine)
|
||||
* @param[out] cargoes Total amount of units that can be transported, summed by cargo.
|
||||
* @param[out] refits Whether a (possibly partial) refit for each cargo is possible.
|
||||
*/
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, uint32 *refits)
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, CargoTypes *refits)
|
||||
{
|
||||
cargoes->Clear();
|
||||
*refits = 0;
|
||||
@@ -228,12 +228,12 @@ bool IsArticulatedVehicleRefittable(EngineID engine)
|
||||
* @param union_mask returns bit mask of CargoIDs which are a refit option for at least one articulated part
|
||||
* @param intersection_mask returns bit mask of CargoIDs which are a refit option for every articulated part (with default capacity > 0)
|
||||
*/
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, uint32 *union_mask, uint32 *intersection_mask)
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
uint32 veh_cargoes = GetAvailableVehicleCargoTypes(engine, include_initial_cargo_type);
|
||||
CargoTypes veh_cargoes = GetAvailableVehicleCargoTypes(engine, include_initial_cargo_type);
|
||||
*union_mask = veh_cargoes;
|
||||
*intersection_mask = (veh_cargoes != 0) ? veh_cargoes : UINT32_MAX;
|
||||
*intersection_mask = (veh_cargoes != 0) ? veh_cargoes : ALL_CARGOTYPES;
|
||||
|
||||
if (!e->IsGroundVehicle()) return;
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
@@ -254,9 +254,9 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type,
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit mask of CargoIDs which are a refit option for at least one articulated part
|
||||
*/
|
||||
uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
uint32 union_mask, intersection_mask;
|
||||
CargoTypes union_mask, intersection_mask;
|
||||
GetArticulatedRefitMasks(engine, include_initial_cargo_type, &union_mask, &intersection_mask);
|
||||
return union_mask;
|
||||
}
|
||||
@@ -267,9 +267,9 @@ uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_car
|
||||
* @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
|
||||
* @return bit mask of CargoIDs which are a refit option for every articulated part (with default capacity > 0)
|
||||
*/
|
||||
uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
CargoTypes GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
|
||||
{
|
||||
uint32 union_mask, intersection_mask;
|
||||
CargoTypes union_mask, intersection_mask;
|
||||
GetArticulatedRefitMasks(engine, include_initial_cargo_type, &union_mask, &intersection_mask);
|
||||
return intersection_mask;
|
||||
}
|
||||
@@ -314,16 +314,16 @@ void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
|
||||
{
|
||||
const Engine *engine = v->GetEngine();
|
||||
|
||||
uint32 purchase_refit_union, purchase_refit_intersection;
|
||||
CargoTypes purchase_refit_union, purchase_refit_intersection;
|
||||
GetArticulatedRefitMasks(v->engine_type, true, &purchase_refit_union, &purchase_refit_intersection);
|
||||
CargoArray purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type);
|
||||
|
||||
uint32 real_refit_union = 0;
|
||||
uint32 real_refit_intersection = UINT_MAX;
|
||||
CargoTypes real_refit_union = 0;
|
||||
CargoTypes real_refit_intersection = ALL_CARGOTYPES;
|
||||
CargoArray real_default_capacity;
|
||||
|
||||
do {
|
||||
uint32 refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, true);
|
||||
CargoTypes refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, true);
|
||||
real_refit_union |= refit_mask;
|
||||
if (refit_mask != 0) real_refit_intersection &= refit_mask;
|
||||
|
||||
|
@@ -18,9 +18,9 @@
|
||||
uint CountArticulatedParts(EngineID engine_type, bool purchase_window);
|
||||
CargoArray GetCapacityOfArticulatedParts(EngineID engine);
|
||||
void AddArticulatedParts(Vehicle *first);
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, uint32 *union_mask, uint32 *intersection_mask);
|
||||
uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask);
|
||||
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
CargoTypes GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type);
|
||||
bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type);
|
||||
bool IsArticulatedVehicleRefittable(EngineID engine);
|
||||
bool IsArticulatedEngine(EngineID engine_type);
|
||||
|
@@ -32,13 +32,12 @@ extern void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index);
|
||||
* Figure out if two engines got at least one type of cargo in common (refitting if needed)
|
||||
* @param engine_a one of the EngineIDs
|
||||
* @param engine_b the other EngineID
|
||||
* @param type the type of the engines
|
||||
* @return true if they can both carry the same type of cargo (or at least one of them got no capacity at all)
|
||||
*/
|
||||
static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
|
||||
{
|
||||
uint32 available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
|
||||
uint32 available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
|
||||
CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
|
||||
CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
|
||||
return (available_cargoes_a == 0 || available_cargoes_b == 0 || (available_cargoes_a & available_cargoes_b) != 0);
|
||||
}
|
||||
|
||||
@@ -173,9 +172,8 @@ static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chai
|
||||
*/
|
||||
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
|
||||
{
|
||||
|
||||
uint32 union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
|
||||
uint32 union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
||||
CargoTypes union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
|
||||
CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
||||
|
||||
const Order *o;
|
||||
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
||||
@@ -201,7 +199,7 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty
|
||||
*/
|
||||
static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
|
||||
{
|
||||
uint32 available_cargo_types, union_mask;
|
||||
CargoTypes available_cargo_types, union_mask;
|
||||
GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
|
||||
|
||||
if (union_mask == 0) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity
|
||||
@@ -238,7 +236,7 @@ static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool
|
||||
* @param v The vehicle to find a replacement for
|
||||
* @param c The vehicle's owner (it's faster to forward the pointer than refinding it)
|
||||
* @param always_replace Always replace, even if not old.
|
||||
* @param [out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
|
||||
* @param[out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
|
||||
* @return Error if the engine to build is not available
|
||||
*/
|
||||
static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
|
||||
|
@@ -81,7 +81,7 @@ class ReplaceVehicleWindow : public Window {
|
||||
EngineID sel_engine[2]; ///< Selected engine left and right.
|
||||
GUIEngineList engines[2]; ///< Left and right list of engines.
|
||||
bool replace_engines; ///< If \c true, engines are replaced, if \c false, wagons are replaced (only for trains).
|
||||
bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right (#update_left and/or #update_right) and no valid engine selected.
|
||||
bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right and no valid engine selected.
|
||||
GroupID sel_group; ///< Group selected to replace.
|
||||
int details_height; ///< Minimal needed height of the details panels (found so far).
|
||||
byte sort_criteria; ///< Criteria of sorting vehicles.
|
||||
|
@@ -26,6 +26,7 @@ struct ContentInfo;
|
||||
struct MD5File {
|
||||
/** The result of a checksum check */
|
||||
enum ChecksumResult {
|
||||
CR_UNKNOWN, ///< The file has not been checked yet
|
||||
CR_MATCH, ///< The file did exist and the md5 checksum did match
|
||||
CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match
|
||||
CR_NO_FILE, ///< The file did not exist
|
||||
@@ -34,6 +35,7 @@ struct MD5File {
|
||||
const char *filename; ///< filename
|
||||
uint8 hash[16]; ///< md5 sum of the file
|
||||
const char *missing_warning; ///< warning when this file is missing
|
||||
ChecksumResult check_result; ///< cached result of md5 check
|
||||
|
||||
ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const;
|
||||
};
|
||||
@@ -285,11 +287,32 @@ static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS;
|
||||
/** Maximum number of songs in the (custom) playlist */
|
||||
static const uint NUM_SONGS_PLAYLIST = 32;
|
||||
|
||||
/* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */
|
||||
char *GetMusicCatEntryName(const char *filename, size_t entrynum);
|
||||
byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen);
|
||||
|
||||
enum MusicTrackType {
|
||||
MTT_STANDARDMIDI, ///< Standard MIDI file
|
||||
MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file)
|
||||
};
|
||||
|
||||
/** Metadata about a music track. */
|
||||
struct MusicSongInfo {
|
||||
char songname[32]; ///< name of song displayed in UI
|
||||
byte tracknr; ///< track number of song displayed in UI
|
||||
const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file)
|
||||
MusicTrackType filetype; ///< decoder required for song file
|
||||
int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI
|
||||
bool loop; ///< song should play in a tight loop if possible, never ending
|
||||
int override_start; ///< MIDI ticks to skip over in beginning
|
||||
int override_end; ///< MIDI tick to end the song at (0 if no override)
|
||||
};
|
||||
|
||||
/** All data of a music set. */
|
||||
struct MusicSet : BaseSet<MusicSet, NUM_SONGS_AVAILABLE, false> {
|
||||
/** The name of the different songs. */
|
||||
char song_name[NUM_SONGS_AVAILABLE][32];
|
||||
byte track_nr[NUM_SONGS_AVAILABLE];
|
||||
/** Data about individual songs in set. */
|
||||
MusicSongInfo songinfo[NUM_SONGS_AVAILABLE];
|
||||
/** Number of valid songs in set. */
|
||||
byte num_available;
|
||||
|
||||
bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename);
|
||||
|
@@ -40,8 +40,6 @@
|
||||
template <class T, size_t Tnum_files, bool Tsearch_in_tars>
|
||||
bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
IniGroup *metadata = ini->GetGroup("metadata");
|
||||
IniItem *item;
|
||||
|
||||
@@ -129,7 +127,11 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
|
||||
file->missing_warning = stredup(item->value);
|
||||
}
|
||||
|
||||
switch (T::CheckMD5(file, BASESET_DIR)) {
|
||||
file->check_result = T::CheckMD5(file, BASESET_DIR);
|
||||
switch (file->check_result) {
|
||||
case MD5File::CR_UNKNOWN:
|
||||
break;
|
||||
|
||||
case MD5File::CR_MATCH:
|
||||
this->valid_files++;
|
||||
this->found_files++;
|
||||
|
@@ -71,7 +71,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
|
||||
uint16 random_bits; ///< Random bits assigned to this station
|
||||
byte waiting_triggers; ///< Waiting triggers (NewGRF) for this station
|
||||
uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
|
||||
uint32 cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask
|
||||
CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask
|
||||
|
||||
TileArea train_station; ///< Tile area the train 'station' part covers
|
||||
StationRect rect; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions
|
||||
|
@@ -22,7 +22,7 @@ static FBlitter_32bppAnim iFBlitter_32bppAnim;
|
||||
|
||||
Blitter_32bppAnim::~Blitter_32bppAnim()
|
||||
{
|
||||
free(this->anim_buf);
|
||||
free(this->anim_alloc);
|
||||
}
|
||||
|
||||
template <BlitterMode mode>
|
||||
@@ -39,8 +39,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
|
||||
}
|
||||
|
||||
Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'bp->dst' into an 'anim_buf' offset below.
|
||||
uint16 *anim = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_pitch + bp->left;
|
||||
uint16 *anim = this->anim_buf + this->ScreenToAnimOffset((uint32 *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left;
|
||||
|
||||
const byte *remap = bp->remap; // store so we don't have to access it via bp everytime
|
||||
|
||||
@@ -280,8 +279,7 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
||||
}
|
||||
|
||||
Colour *udst = (Colour *)dst;
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'dst' into an 'anim_buf' offset below.
|
||||
uint16 *anim = this->anim_buf + ((uint32 *)dst - (uint32 *)_screen.dst_ptr);
|
||||
uint16 *anim = this->anim_buf + this->ScreenToAnimOffset((uint32 *)dst);
|
||||
|
||||
if (pal == PALETTE_TO_TRANSPARENT) {
|
||||
do {
|
||||
@@ -319,8 +317,8 @@ void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8 colour)
|
||||
|
||||
/* Set the colour in the anim-buffer too, if we are rendering to the screen */
|
||||
if (_screen_disable_anim) return;
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'video' into an 'anim_buf' offset below.
|
||||
this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_pitch] = colour | (DEFAULT_BRIGHTNESS << 8);
|
||||
|
||||
this->anim_buf[this->ScreenToAnimOffset((uint32 *)video) + x + y * this->anim_buf_pitch] = colour | (DEFAULT_BRIGHTNESS << 8);
|
||||
}
|
||||
|
||||
void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colour)
|
||||
@@ -332,8 +330,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colou
|
||||
}
|
||||
|
||||
Colour colour32 = LookupColourInPalette(colour);
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'video' into an 'anim_buf' offset below.
|
||||
uint16 *anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
uint16 *anim_line = this->ScreenToAnimOffset((uint32 *)video) + this->anim_buf;
|
||||
|
||||
do {
|
||||
Colour *dst = (Colour *)video;
|
||||
@@ -357,15 +354,14 @@ void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width,
|
||||
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
|
||||
Colour *dst = (Colour *)video;
|
||||
const uint32 *usrc = (const uint32 *)src;
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'video' into an 'anim_buf' offset below.
|
||||
uint16 *anim_line = ((uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
uint16 *anim_line = this->ScreenToAnimOffset((uint32 *)video) + this->anim_buf;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
/* We need to keep those for palette animation. */
|
||||
Colour *dst_pal = dst;
|
||||
uint16 *anim_pal = anim_line;
|
||||
|
||||
memcpy(dst, usrc, width * sizeof(uint32));
|
||||
memcpy(static_cast<void *>(dst), usrc, width * sizeof(uint32));
|
||||
usrc += width;
|
||||
dst += _screen.pitch;
|
||||
/* Copy back the anim-buffer */
|
||||
@@ -401,8 +397,7 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
|
||||
|
||||
if (this->anim_buf == NULL) return;
|
||||
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'video' into an 'anim_buf' offset below.
|
||||
const uint16 *anim_line = ((const uint32 *)video - (uint32 *)_screen.dst_ptr) + this->anim_buf;
|
||||
const uint16 *anim_line = this->ScreenToAnimOffset((const uint32 *)video) + this->anim_buf;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32));
|
||||
@@ -485,18 +480,22 @@ void Blitter_32bppAnim::PaletteAnimate(const Palette &palette)
|
||||
Colour *dst = (Colour *)_screen.dst_ptr;
|
||||
|
||||
/* Let's walk the anim buffer and try to find the pixels */
|
||||
const int width = this->anim_buf_width;
|
||||
const int pitch_offset = _screen.pitch - width;
|
||||
const int anim_pitch_offset = this->anim_buf_pitch - width;
|
||||
for (int y = this->anim_buf_height; y != 0 ; y--) {
|
||||
for (int x = this->anim_buf_width; x != 0 ; x--) {
|
||||
uint colour = GB(*anim, 0, 8);
|
||||
for (int x = width; x != 0 ; x--) {
|
||||
uint16 value = *anim;
|
||||
uint8 colour = GB(value, 0, 8);
|
||||
if (colour >= PALETTE_ANIM_START) {
|
||||
/* Update this pixel */
|
||||
*dst = this->AdjustBrightness(LookupColourInPalette(colour), GB(*anim, 8, 8));
|
||||
*dst = this->AdjustBrightness(LookupColourInPalette(colour), GB(value, 8, 8));
|
||||
}
|
||||
dst++;
|
||||
anim++;
|
||||
}
|
||||
dst += _screen.pitch - this->anim_buf_width;
|
||||
anim += this->anim_buf_pitch - this->anim_buf_width;
|
||||
dst += pitch_offset;
|
||||
anim += anim_pitch_offset;
|
||||
}
|
||||
|
||||
/* Make sure the backend redraws the whole screen */
|
||||
@@ -513,10 +512,13 @@ void Blitter_32bppAnim::PostResize()
|
||||
if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height ||
|
||||
_screen.pitch != this->anim_buf_pitch) {
|
||||
/* The size of the screen changed; we can assume we can wipe all data from our buffer */
|
||||
free(this->anim_buf);
|
||||
free(this->anim_alloc);
|
||||
this->anim_buf_width = _screen.width;
|
||||
this->anim_buf_height = _screen.height;
|
||||
this->anim_buf_pitch = _screen.pitch;
|
||||
this->anim_buf = CallocT<uint16>(this->anim_buf_height * this->anim_buf_pitch);
|
||||
this->anim_buf_pitch = (_screen.width + 7) & ~7;
|
||||
this->anim_alloc = CallocT<uint16>(this->anim_buf_pitch * this->anim_buf_height + 8);
|
||||
|
||||
/* align buffer to next 16 byte boundary */
|
||||
this->anim_buf = reinterpret_cast<uint16 *>((reinterpret_cast<uintptr_t>(this->anim_alloc) + 0xF) & (~0xF));
|
||||
}
|
||||
}
|
||||
|
@@ -18,14 +18,16 @@
|
||||
class Blitter_32bppAnim : public Blitter_32bppOptimized {
|
||||
protected:
|
||||
uint16 *anim_buf; ///< In this buffer we keep track of the 8bpp indexes so we can do palette animation
|
||||
void *anim_alloc; ///< The raw allocated buffer, not necessarily aligned correctly
|
||||
int anim_buf_width; ///< The width of the animation buffer.
|
||||
int anim_buf_height; ///< The height of the animation buffer.
|
||||
int anim_buf_pitch; ///< The pitch of the animation buffer.
|
||||
int anim_buf_pitch; ///< The pitch of the animation buffer (width rounded up to 16 byte boundary).
|
||||
Palette palette; ///< The current palette.
|
||||
|
||||
public:
|
||||
Blitter_32bppAnim() :
|
||||
anim_buf(NULL),
|
||||
anim_alloc(NULL),
|
||||
anim_buf_width(0),
|
||||
anim_buf_height(0),
|
||||
anim_buf_pitch(0)
|
||||
@@ -58,6 +60,15 @@ public:
|
||||
return this->palette.palette[index];
|
||||
}
|
||||
|
||||
inline int ScreenToAnimOffset(const uint32 *video)
|
||||
{
|
||||
int raw_offset = video - (const uint32 *)_screen.dst_ptr;
|
||||
if (_screen.pitch == this->anim_buf_pitch) return raw_offset;
|
||||
int lines = raw_offset / _screen.pitch;
|
||||
int across = raw_offset % _screen.pitch;
|
||||
return across + (lines * this->anim_buf_pitch);
|
||||
}
|
||||
|
||||
template <BlitterMode mode> void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
|
||||
};
|
||||
|
||||
|
100
src/blitter/32bpp_anim_sse2.cpp
Normal file
100
src/blitter/32bpp_anim_sse2.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_anim.cpp Implementation of a partially SSSE2 32bpp blitter with animation support. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../video/video_driver.hpp"
|
||||
#include "32bpp_anim_sse2.hpp"
|
||||
#include "32bpp_sse_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/** Instantiation of the partially SSSE2 32bpp with animation blitter factory. */
|
||||
static FBlitter_32bppSSE2_Anim iFBlitter_32bppSSE2_Anim;
|
||||
|
||||
void Blitter_32bppSSE2_Anim::PaletteAnimate(const Palette &palette)
|
||||
{
|
||||
assert(!_screen_disable_anim);
|
||||
|
||||
this->palette = palette;
|
||||
/* If first_dirty is 0, it is for 8bpp indication to send the new
|
||||
* palette. However, only the animation colours might possibly change.
|
||||
* Especially when going between toyland and non-toyland. */
|
||||
assert(this->palette.first_dirty == PALETTE_ANIM_START || this->palette.first_dirty == 0);
|
||||
|
||||
const uint16 *anim = this->anim_buf;
|
||||
Colour *dst = (Colour *)_screen.dst_ptr;
|
||||
|
||||
bool screen_dirty = false;
|
||||
|
||||
/* Let's walk the anim buffer and try to find the pixels */
|
||||
const int width = this->anim_buf_width;
|
||||
const int screen_pitch = _screen.pitch;
|
||||
const int anim_pitch = this->anim_buf_pitch;
|
||||
__m128i anim_cmp = _mm_set1_epi16(PALETTE_ANIM_START - 1);
|
||||
__m128i brightness_cmp = _mm_set1_epi16(Blitter_32bppBase::DEFAULT_BRIGHTNESS);
|
||||
__m128i colour_mask = _mm_set1_epi16(0xFF);
|
||||
for (int y = this->anim_buf_height; y != 0 ; y--) {
|
||||
Colour *next_dst_ln = dst + screen_pitch;
|
||||
const uint16 *next_anim_ln = anim + anim_pitch;
|
||||
int x = width;
|
||||
while (x > 0) {
|
||||
__m128i data = _mm_load_si128((const __m128i *) anim);
|
||||
|
||||
/* low bytes only, shifted into high positions */
|
||||
__m128i colour_data = _mm_and_si128(data, colour_mask);
|
||||
|
||||
/* test if any colour >= PALETTE_ANIM_START */
|
||||
int colour_cmp_result = _mm_movemask_epi8(_mm_cmpgt_epi16(colour_data, anim_cmp));
|
||||
if (colour_cmp_result) {
|
||||
/* test if any brightness is unexpected */
|
||||
if (x < 8 || colour_cmp_result != 0xFFFF ||
|
||||
_mm_movemask_epi8(_mm_cmpeq_epi16(_mm_srli_epi16(data, 8), brightness_cmp)) != 0xFFFF) {
|
||||
/* slow path: < 8 pixels left or unexpected brightnesses */
|
||||
for (int z = min<int>(x, 8); z != 0 ; z--) {
|
||||
int value = _mm_extract_epi16(data, 0);
|
||||
uint8 colour = GB(value, 0, 8);
|
||||
if (colour >= PALETTE_ANIM_START) {
|
||||
/* Update this pixel */
|
||||
*dst = AdjustBrightneSSE(LookupColourInPalette(colour), GB(value, 8, 8));
|
||||
screen_dirty = true;
|
||||
}
|
||||
data = _mm_srli_si128(data, 2);
|
||||
dst++;
|
||||
}
|
||||
} else {
|
||||
/* medium path: 8 pixels to animate all of expected brightnesses */
|
||||
for (int z = 0; z < 8; z++) {
|
||||
*dst = LookupColourInPalette(_mm_extract_epi16(colour_data, 0));
|
||||
colour_data = _mm_srli_si128(colour_data, 2);
|
||||
dst++;
|
||||
}
|
||||
screen_dirty = true;
|
||||
}
|
||||
} else {
|
||||
/* fast path, no animation */
|
||||
dst += 8;
|
||||
}
|
||||
anim += 8;
|
||||
x -= 8;
|
||||
}
|
||||
dst = next_dst_ln;
|
||||
anim = next_anim_ln;
|
||||
}
|
||||
|
||||
if (screen_dirty) {
|
||||
/* Make sure the backend redraws the whole screen */
|
||||
VideoDriver::GetInstance()->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WITH_SSE */
|
43
src/blitter/32bpp_anim_sse2.hpp
Normal file
43
src/blitter/32bpp_anim_sse2.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_anim.hpp A partially SSE2 32 bpp blitter with animation support. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE2_ANIM_HPP
|
||||
#define BLITTER_32BPP_SSE2_ANIM_HPP
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
#ifndef SSE_VERSION
|
||||
#define SSE_VERSION 2
|
||||
#endif
|
||||
|
||||
#ifndef FULL_ANIMATION
|
||||
#define FULL_ANIMATION 1
|
||||
#endif
|
||||
|
||||
#include "32bpp_anim.hpp"
|
||||
#include "32bpp_sse2.hpp"
|
||||
|
||||
/** A partially 32 bpp blitter with palette animation. */
|
||||
class Blitter_32bppSSE2_Anim : public Blitter_32bppAnim {
|
||||
public:
|
||||
/* virtual */ void PaletteAnimate(const Palette &palette);
|
||||
/* virtual */ const char *GetName() { return "32bpp-sse2-anim"; }
|
||||
};
|
||||
|
||||
/** Factory for the partially 32bpp blitter with animation. */
|
||||
class FBlitter_32bppSSE2_Anim : public BlitterFactory {
|
||||
public:
|
||||
FBlitter_32bppSSE2_Anim() : BlitterFactory("32bpp-sse2-anim", "32bpp partially SSE2 Animation Blitter (palette animation)", HasCPUIDFlag(1, 3, 26)) {}
|
||||
/* virtual */ Blitter *CreateInstance() { return new Blitter_32bppSSE2_Anim(); }
|
||||
};
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_ANIM_HPP */
|
@@ -7,7 +7,7 @@
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4_anim.cpp Implementation of the SSE4 32 bpp blitter with animation support. */
|
||||
/** @file 32bpp_anim_sse4.cpp Implementation of the SSE4 32 bpp blitter with animation support. */
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
@@ -35,8 +35,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams *bp, ZoomL
|
||||
{
|
||||
const byte * const remap = bp->remap;
|
||||
Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left;
|
||||
assert(_screen.pitch == this->anim_buf_pitch); // precondition for translating 'bp->dst' into an 'anim_buf' offset below.
|
||||
uint16 *anim_line = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_pitch + bp->left;
|
||||
uint16 *anim_line = this->anim_buf + this->ScreenToAnimOffset((uint32 *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left;
|
||||
int effective_width = bp->width;
|
||||
|
||||
/* Find where to start reading in the source sprite. */
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse4_anim.hpp A SSE4 32 bpp blitter with animation support. */
|
||||
/** @file 32bpp_anim_sse4.hpp A SSE4 32 bpp blitter with animation support. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE4_ANIM_HPP
|
||||
#define BLITTER_32BPP_SSE4_ANIM_HPP
|
||||
@@ -23,13 +23,14 @@
|
||||
#endif
|
||||
|
||||
#include "32bpp_anim.hpp"
|
||||
#include "32bpp_anim_sse2.hpp"
|
||||
#include "32bpp_sse4.hpp"
|
||||
|
||||
#undef MARGIN_NORMAL_THRESHOLD
|
||||
#define MARGIN_NORMAL_THRESHOLD 4
|
||||
|
||||
/** The SSE4 32 bpp blitter with palette animation. */
|
||||
class Blitter_32bppSSE4_Anim FINAL : public Blitter_32bppAnim, public Blitter_32bppSSE_Base {
|
||||
class Blitter_32bppSSE4_Anim FINAL : public Blitter_32bppSSE2_Anim, public Blitter_32bppSSE_Base {
|
||||
private:
|
||||
|
||||
public:
|
||||
|
@@ -143,6 +143,36 @@ void Blitter_32bppBase::PaletteAnimate(const Palette &palette)
|
||||
/* By default, 32bpp doesn't have palette animation */
|
||||
}
|
||||
|
||||
Colour Blitter_32bppBase::ReallyAdjustBrightness(Colour colour, uint8 brightness)
|
||||
{
|
||||
assert(DEFAULT_BRIGHTNESS == 1 << 7);
|
||||
|
||||
uint64 combined = (((uint64) colour.r) << 32) | (((uint64) colour.g) << 16) | ((uint64) colour.b);
|
||||
combined *= brightness;
|
||||
|
||||
uint16 r = GB(combined, 39, 9);
|
||||
uint16 g = GB(combined, 23, 9);
|
||||
uint16 b = GB(combined, 7, 9);
|
||||
|
||||
if ((combined & 0x800080008000L) == 0L) {
|
||||
return Colour(r, g, b, colour.a);
|
||||
}
|
||||
|
||||
uint16 ob = 0;
|
||||
/* Sum overbright */
|
||||
if (r > 255) ob += r - 255;
|
||||
if (g > 255) ob += g - 255;
|
||||
if (b > 255) ob += b - 255;
|
||||
|
||||
/* Reduce overbright strength */
|
||||
ob /= 2;
|
||||
return Colour(
|
||||
r >= 255 ? 255 : min(r + ob * (255 - r) / 256, 255),
|
||||
g >= 255 ? 255 : min(g + ob * (255 - g) / 256, 255),
|
||||
b >= 255 ? 255 : min(b + ob * (255 - b) / 256, 255),
|
||||
colour.a);
|
||||
}
|
||||
|
||||
Blitter::PaletteAnimation Blitter_32bppBase::UsePaletteAnimation()
|
||||
{
|
||||
return Blitter::PALETTE_ANIMATION_NONE;
|
||||
|
@@ -146,30 +146,14 @@ public:
|
||||
|
||||
static const int DEFAULT_BRIGHTNESS = 128;
|
||||
|
||||
static Colour ReallyAdjustBrightness(Colour colour, uint8 brightness);
|
||||
|
||||
static inline Colour AdjustBrightness(Colour colour, uint8 brightness)
|
||||
{
|
||||
/* Shortcut for normal brightness */
|
||||
if (brightness == DEFAULT_BRIGHTNESS) return colour;
|
||||
|
||||
uint16 ob = 0;
|
||||
uint16 r = colour.r * brightness / DEFAULT_BRIGHTNESS;
|
||||
uint16 g = colour.g * brightness / DEFAULT_BRIGHTNESS;
|
||||
uint16 b = colour.b * brightness / DEFAULT_BRIGHTNESS;
|
||||
|
||||
/* Sum overbright */
|
||||
if (r > 255) ob += r - 255;
|
||||
if (g > 255) ob += g - 255;
|
||||
if (b > 255) ob += b - 255;
|
||||
|
||||
if (ob == 0) return Colour(r, g, b, colour.a);
|
||||
|
||||
/* Reduce overbright strength */
|
||||
ob /= 2;
|
||||
return Colour(
|
||||
r >= 255 ? 255 : min(r + ob * (255 - r) / 256, 255),
|
||||
g >= 255 ? 255 : min(g + ob * (255 - g) / 256, 255),
|
||||
b >= 255 ? 255 : min(b + ob * (255 - b) / 256, 255),
|
||||
colour.a);
|
||||
return ReallyAdjustBrightness(colour, brightness);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -7,10 +7,10 @@
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file 32bpp_sse_type.hpp Types related to SSE 32 bpp blitter. */
|
||||
/** @file 32bpp_sse_type.h Types related to SSE 32 bpp blitter. */
|
||||
|
||||
#ifndef BLITTER_32BPP_SSE_TYPE_HPP
|
||||
#define BLITTER_32BPP_SSE_TYPE_HPP
|
||||
#ifndef BLITTER_32BPP_SSE_TYPE_H
|
||||
#define BLITTER_32BPP_SSE_TYPE_H
|
||||
|
||||
#ifdef WITH_SSE
|
||||
|
||||
@@ -55,4 +55,4 @@ typedef union ALIGN(16) um128i {
|
||||
#define TRANSPARENT_NOM_BASE _mm_setr_epi16(256, 256, 256, 256, 256, 256, 256, 256)
|
||||
|
||||
#endif /* WITH_SSE */
|
||||
#endif /* BLITTER_32BPP_SSE_TYPE_HPP */
|
||||
#endif /* BLITTER_32BPP_SSE_TYPE_H */
|
||||
|
@@ -220,7 +220,7 @@ bool HandleBootstrap()
|
||||
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure;
|
||||
|
||||
/* If there is no network or no freetype, then there is nothing we can do. Go straight to failure. */
|
||||
#if defined(ENABLE_NETWORK) && defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(WIN32) || defined(__APPLE__))
|
||||
#if defined(ENABLE_NETWORK) && defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(_WIN32) || defined(__APPLE__))
|
||||
if (!_network_available) goto failure;
|
||||
|
||||
/* First tell the game we're bootstrapping. */
|
||||
|
@@ -55,7 +55,7 @@ typedef GUIList<BuildBridgeData> GUIBridgeList; ///< List of bridges, used in #B
|
||||
* @param p1 packed start tile coords (~ dx)
|
||||
* @param p2 various bitstuffed elements
|
||||
* - p2 = (bit 0- 7) - bridge type (hi bh)
|
||||
* - p2 = (bit 8-11) - rail type or road types.
|
||||
* - p2 = (bit 8-13) - rail type or road types.
|
||||
* - p2 = (bit 15-16) - transport type.
|
||||
*/
|
||||
void CcBuildBridge(const CommandCost &result, TileIndex end_tile, uint32 p1, uint32 p2)
|
||||
|
@@ -56,7 +56,7 @@ TileIndex GetSouthernBridgeEnd(TileIndex t)
|
||||
|
||||
/**
|
||||
* Starting at one bridge end finds the other bridge end
|
||||
* @param t the bridge ramp tile to find the other bridge ramp for
|
||||
* @param tile the bridge ramp tile to find the other bridge ramp for
|
||||
*/
|
||||
TileIndex GetOtherBridgeEnd(TileIndex tile)
|
||||
{
|
||||
@@ -66,7 +66,7 @@ TileIndex GetOtherBridgeEnd(TileIndex tile)
|
||||
|
||||
/**
|
||||
* Get the height ('z') of a bridge.
|
||||
* @param tile the bridge ramp tile to get the bridge height from
|
||||
* @param t the bridge ramp tile to get the bridge height from
|
||||
* @return the height of the bridge.
|
||||
*/
|
||||
int GetBridgeHeight(TileIndex t)
|
||||
|
@@ -131,11 +131,12 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D
|
||||
SetTileType(t, MP_TUNNELBRIDGE);
|
||||
SetTileOwner(t, o);
|
||||
_m[t].m2 = 0;
|
||||
_m[t].m3 = rt;
|
||||
_m[t].m3 = 0;
|
||||
_m[t].m4 = 0;
|
||||
_m[t].m5 = 1 << 7 | tt << 2 | d;
|
||||
SB(_me[t].m6, 2, 4, bridgetype);
|
||||
_me[t].m7 = 0;
|
||||
_me[t].m8 = rt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -524,7 +524,7 @@ const StringID _engine_sort_listing[][12] = {{
|
||||
static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
|
||||
{
|
||||
if (cid == CF_ANY) return true;
|
||||
uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true) & _standard_cargo_mask;
|
||||
CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true) & _standard_cargo_mask;
|
||||
return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ static GUIEngineList::FilterFunction * const _filter_funcs[] = {
|
||||
static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine)
|
||||
{
|
||||
CargoArray cap;
|
||||
uint32 refits;
|
||||
CargoTypes refits;
|
||||
GetArticulatedVehicleCargoesAndRefits(engine, &cap, &refits);
|
||||
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
|
@@ -22,7 +22,7 @@
|
||||
typedef byte CargoID;
|
||||
|
||||
/** Available types of cargo */
|
||||
enum CargoTypes {
|
||||
enum CargoType {
|
||||
/* Temperate */
|
||||
CT_PASSENGERS = 0,
|
||||
CT_COAL = 1,
|
||||
@@ -63,13 +63,22 @@ enum CargoTypes {
|
||||
CT_PLASTIC = 10,
|
||||
CT_FIZZY_DRINKS = 11,
|
||||
|
||||
NUM_CARGO = 32, ///< Maximal number of cargo types in a game.
|
||||
NUM_CARGO = 64, ///< Maximal number of cargo types in a game.
|
||||
|
||||
CT_AUTO_REFIT = 0xFD, ///< Automatically choose cargo type when doing auto refitting.
|
||||
CT_NO_REFIT = 0xFE, ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-new).
|
||||
CT_INVALID = 0xFF, ///< Invalid cargo type.
|
||||
};
|
||||
|
||||
/** Test whether cargo type is not CT_INVALID */
|
||||
inline bool IsCargoTypeValid(CargoType t) { return t != CT_INVALID; }
|
||||
/** Test whether cargo type is not CT_INVALID */
|
||||
inline bool IsCargoIDValid(CargoID t) { return t != CT_INVALID; }
|
||||
|
||||
typedef uint64 CargoTypes;
|
||||
|
||||
static const CargoTypes ALL_CARGOTYPES = (CargoTypes)UINT64_MAX;
|
||||
|
||||
/** Class for storing amounts of cargo */
|
||||
struct CargoArray {
|
||||
private:
|
||||
|
@@ -602,7 +602,7 @@ uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList:
|
||||
* Returns reserved cargo to the station and removes it from the cache.
|
||||
* @param max_move Maximum amount of cargo to move.
|
||||
* @param dest Station the cargo is returned to.
|
||||
* @param ID of next the station the cargo wants to go next.
|
||||
* @param next ID of the next station the cargo wants to go to.
|
||||
* @return Amount of cargo actually returned.
|
||||
*/
|
||||
uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
|
||||
@@ -668,7 +668,7 @@ uint VehicleCargoList::Truncate(uint max_move)
|
||||
* @param dest List to prepend the cargo to.
|
||||
* @param avoid Station to exclude from routing and current next hop of packets to reroute.
|
||||
* @param avoid2 Additional station to exclude from routing.
|
||||
* @oaram ge GoodsEntry to get the routing info from.
|
||||
* @param ge GoodsEntry to get the routing info from.
|
||||
*/
|
||||
uint VehicleCargoList::Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
|
||||
{
|
||||
@@ -859,7 +859,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex loa
|
||||
* @param dest List to append the cargo to.
|
||||
* @param avoid Station to exclude from routing and current next hop of packets to reroute.
|
||||
* @param avoid2 Additional station to exclude from routing.
|
||||
* @oaram ge GoodsEntry to get the routing info from.
|
||||
* @param ge GoodsEntry to get the routing info from.
|
||||
*/
|
||||
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
|
||||
{
|
||||
|
@@ -28,12 +28,12 @@ CargoSpec CargoSpec::array[NUM_CARGO];
|
||||
* Bitmask of cargo types available. This includes phony cargoes like regearing cargoes.
|
||||
* Initialized during a call to #SetupCargoForClimate.
|
||||
*/
|
||||
uint32 _cargo_mask;
|
||||
CargoTypes _cargo_mask;
|
||||
|
||||
/**
|
||||
* Bitmask of real cargo types available. Phony cargoes like regearing cargoes are excluded.
|
||||
*/
|
||||
uint32 _standard_cargo_mask;
|
||||
CargoTypes _standard_cargo_mask;
|
||||
|
||||
/**
|
||||
* Set up the default cargo types for the given landscape type.
|
||||
|
@@ -129,8 +129,8 @@ private:
|
||||
friend void SetupCargoForClimate(LandscapeID l);
|
||||
};
|
||||
|
||||
extern uint32 _cargo_mask;
|
||||
extern uint32 _standard_cargo_mask;
|
||||
extern CargoTypes _cargo_mask;
|
||||
extern CargoTypes _standard_cargo_mask;
|
||||
|
||||
void SetupCargoForClimate(LandscapeID l);
|
||||
CargoID GetCargoIDByLabel(CargoLabel cl);
|
||||
@@ -156,7 +156,7 @@ static inline bool IsCargoInClass(CargoID c, CargoClass cc)
|
||||
if ((var = CargoSpec::Get(cargospec_index))->IsValid())
|
||||
#define FOR_ALL_CARGOSPECS(var) FOR_ALL_CARGOSPECS_FROM(var, 0)
|
||||
|
||||
#define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, uint, cargo_bits)
|
||||
#define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, CargoTypes, cargo_bits)
|
||||
|
||||
/**
|
||||
* Loop header for iterating over cargoes, sorted by name. This includes phony cargoes like regearing cargoes.
|
||||
|
@@ -269,6 +269,7 @@ static inline void MakeClear(TileIndex t, ClearGround g, uint density)
|
||||
SetClearGroundDensity(t, g, density); // Sets m5
|
||||
_me[t].m6 = 0;
|
||||
_me[t].m7 = 0;
|
||||
_me[t].m8 = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -289,6 +290,7 @@ static inline void MakeField(TileIndex t, uint field_type, IndustryID industry)
|
||||
SetClearGroundDensity(t, CLEAR_FIELDS, 3);
|
||||
SB(_me[t].m6, 2, 4, 0);
|
||||
_me[t].m7 = 0;
|
||||
_me[t].m8 = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -605,7 +605,6 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac
|
||||
/**
|
||||
* Helper to deduplicate the code for returning.
|
||||
* @param cmd the command cost to return.
|
||||
* @param clear whether to keep the storage changes or not.
|
||||
*/
|
||||
#define return_dcpi(cmd) { _docommand_recursive = 0; return cmd; }
|
||||
|
||||
|
@@ -67,8 +67,6 @@ struct CompanyProperties {
|
||||
|
||||
byte colour; ///< Company colour.
|
||||
|
||||
RailTypes avail_railtypes; ///< Rail types available to the company.
|
||||
|
||||
byte block_preview; ///< Number of quarters that the company is not allowed to get new exclusive engine previews (see CompaniesGenStatistics).
|
||||
|
||||
TileIndex location_of_HQ; ///< Northern tile of HQ; #INVALID_TILE when there is none.
|
||||
@@ -93,12 +91,18 @@ struct CompanyProperties {
|
||||
*/
|
||||
bool is_ai;
|
||||
|
||||
Money yearly_expenses[3][EXPENSES_END]; ///< Expenses of the company for the last three years, in every #Expenses category.
|
||||
Money yearly_expenses[3][EXPENSES_END]; ///< Expenses of the company for the last three years, in every #ExpensesType category.
|
||||
CompanyEconomyEntry cur_economy; ///< Economic data of the company of this quarter.
|
||||
CompanyEconomyEntry old_economy[MAX_HISTORY_QUARTERS]; ///< Economic data of the company of the last #MAX_HISTORY_QUARTERS quarters.
|
||||
byte num_valid_stat_ent; ///< Number of valid statistical entries in #old_economy.
|
||||
|
||||
CompanyProperties() : name(NULL), president_name(NULL) {}
|
||||
// TODO: Change some of these member variables to use relevant INVALID_xxx constants
|
||||
CompanyProperties()
|
||||
: name_2(0), name_1(0), name(NULL), president_name_1(0), president_name_2(0), president_name(NULL),
|
||||
face(0), money(0), money_fraction(0), current_loan(0), colour(0), block_preview(0),
|
||||
location_of_HQ(0), last_build_coordinate(0), share_owners(), inaugurated_year(0),
|
||||
months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0),
|
||||
terraform_limit(0), clear_limit(0), tree_limit(0), is_ai(false) {}
|
||||
|
||||
~CompanyProperties()
|
||||
{
|
||||
@@ -112,6 +116,7 @@ struct Company : CompanyPool::PoolItem<&_company_pool>, CompanyProperties {
|
||||
~Company();
|
||||
|
||||
Livery livery[LS_END];
|
||||
RailTypes avail_railtypes; ///< Rail types available to this company.
|
||||
RoadTypes avail_roadtypes; ///< Road types available to this company.
|
||||
|
||||
class AIInstance *ai_instance;
|
||||
|
@@ -188,7 +188,7 @@ void InvalidateCompanyWindows(const Company *company)
|
||||
|
||||
/**
|
||||
* Verify whether the company can pay the bill.
|
||||
* @param cost [inout] Money to pay, is changed to an error if the company does not have enough money.
|
||||
* @param[in,out] cost Money to pay, is changed to an error if the company does not have enough money.
|
||||
* @return Function returns \c true if the company has enough money, else it returns \c false.
|
||||
*/
|
||||
bool CheckCompanyHasMoney(CommandCost &cost)
|
||||
|
@@ -268,10 +268,7 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Window class displaying the company finances.
|
||||
* @todo #money_width should be calculated dynamically.
|
||||
*/
|
||||
/** Window class displaying the company finances. */
|
||||
struct CompanyFinancesWindow : Window {
|
||||
static Money max_money; ///< The maximum amount of money a company has had this 'run'
|
||||
bool small; ///< Window is toggled to 'small'.
|
||||
@@ -1509,9 +1506,6 @@ static WindowDesc _select_company_manager_face_desc(
|
||||
* Open the simple/advanced company manager face selection window
|
||||
*
|
||||
* @param parent the parent company window
|
||||
* @param adv simple or advanced face selection window
|
||||
* @param top previous top position of the window
|
||||
* @param left previous left position of the window
|
||||
*/
|
||||
static void DoSelectCompanyManagerFace(Window *parent)
|
||||
{
|
||||
|
@@ -235,8 +235,8 @@ void IConsoleAddSorted(T **base, T *item_new)
|
||||
|
||||
/**
|
||||
* Remove underscores from a string; the string will be modified!
|
||||
* @param name The string to remove the underscores from.
|
||||
* @return #name.
|
||||
* @param[in,out] name String to remove the underscores from.
|
||||
* @return \a name, with its contents modified.
|
||||
*/
|
||||
char *RemoveUnderscores(char *name)
|
||||
{
|
||||
|
@@ -62,7 +62,7 @@ public:
|
||||
|
||||
/**
|
||||
* (Re-)validate the file storage cache. Only makes a change if the storage was invalid, or if \a force_reload.
|
||||
* @param Always reload the file storage cache.
|
||||
* @param force_reload Always reload the file storage cache.
|
||||
*/
|
||||
void ValidateFileList(bool force_reload = false)
|
||||
{
|
||||
@@ -256,8 +256,8 @@ DEF_CONSOLE_CMD(ConResetTile)
|
||||
|
||||
/**
|
||||
* Scroll to a tile on the map.
|
||||
* @param arg1 tile tile number or tile x coordinate.
|
||||
* @param arg2 optionally tile y coordinate.
|
||||
* param x tile number or tile x coordinate.
|
||||
* param y optional y coordinate.
|
||||
* @note When only one argument is given it is intepreted as the tile number.
|
||||
* When two arguments are given, they are interpreted as the tile's x
|
||||
* and y coordinates.
|
||||
@@ -304,7 +304,7 @@ DEF_CONSOLE_CMD(ConScrollToTile)
|
||||
|
||||
/**
|
||||
* Save the map to a file.
|
||||
* @param filename the filename to save the map to.
|
||||
* param filename the filename to save the map to.
|
||||
* @return True when help was displayed or the file attempted to be saved.
|
||||
*/
|
||||
DEF_CONSOLE_CMD(ConSave)
|
||||
@@ -1895,6 +1895,37 @@ static void IConsoleDebugLibRegister()
|
||||
}
|
||||
#endif
|
||||
|
||||
DEF_CONSOLE_CMD(ConFramerate)
|
||||
{
|
||||
extern void ConPrintFramerate(); // framerate_gui.cpp
|
||||
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Show frame rate and game speed information");
|
||||
return true;
|
||||
}
|
||||
|
||||
ConPrintFramerate();
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD(ConFramerateWindow)
|
||||
{
|
||||
extern void ShowFramerateWindow();
|
||||
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Open the frame rate window");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_network_dedicated) {
|
||||
IConsoleError("Can not open frame rate window on a dedicated server");
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowFramerateWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* console command registration
|
||||
*******************************/
|
||||
@@ -2025,6 +2056,8 @@ void IConsoleStdLibRegister()
|
||||
#ifdef _DEBUG
|
||||
IConsoleDebugLibRegister();
|
||||
#endif
|
||||
IConsoleCmdRegister("fps", ConFramerate);
|
||||
IConsoleCmdRegister("fps_wnd", ConFramerateWindow);
|
||||
|
||||
/* NewGRF development stuff */
|
||||
IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
|
||||
|
@@ -125,7 +125,7 @@ static inline T *ReallocT(T *t_ptr, size_t num_elements)
|
||||
/* Ensure the size does not overflow. */
|
||||
CheckAllocationConstraints<T>(num_elements);
|
||||
|
||||
t_ptr = (T*)realloc(t_ptr, num_elements * sizeof(T));
|
||||
t_ptr = (T*)realloc(static_cast<void *>(t_ptr), num_elements * sizeof(T));
|
||||
if (t_ptr == NULL) ReallocError(num_elements * sizeof(T));
|
||||
return t_ptr;
|
||||
}
|
||||
|
@@ -53,7 +53,7 @@ struct Backup {
|
||||
{
|
||||
/* We cannot assert here, as missing restoration is 'normal' when exceptions are thrown.
|
||||
* Exceptions are especially used to abort world generation. */
|
||||
DEBUG(misc, 0, "%s:%d: Backupped value was not restored!", this->file, this->line);
|
||||
DEBUG(misc, 0, "%s:%d: Backed-up value was not restored!", this->file, this->line);
|
||||
this->Restore();
|
||||
}
|
||||
}
|
||||
|
@@ -302,6 +302,7 @@ static inline bool HasAtMostOneBit(T value)
|
||||
template <typename T>
|
||||
static inline T ROL(const T x, const uint8 n)
|
||||
{
|
||||
if (n == 0) return x;
|
||||
return (T)(x << n | x >> (sizeof(x) * 8 - n));
|
||||
}
|
||||
|
||||
@@ -317,6 +318,7 @@ static inline T ROL(const T x, const uint8 n)
|
||||
template <typename T>
|
||||
static inline T ROR(const T x, const uint8 n)
|
||||
{
|
||||
if (n == 0) return x;
|
||||
return (T)(x >> n | x << (sizeof(x) * 8 - n));
|
||||
}
|
||||
|
||||
|
@@ -26,15 +26,22 @@
|
||||
#define TTD_BIG_ENDIAN 1
|
||||
|
||||
/* Windows has always LITTLE_ENDIAN */
|
||||
#if defined(WIN32) || defined(__OS2__) || defined(WIN64)
|
||||
#define TTD_ENDIAN TTD_LITTLE_ENDIAN
|
||||
#if defined(_WIN32) || defined(__OS2__)
|
||||
# define TTD_ENDIAN TTD_LITTLE_ENDIAN
|
||||
#elif defined(OSX)
|
||||
# include <sys/types.h>
|
||||
# if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
|
||||
# define TTD_ENDIAN TTD_LITTLE_ENDIAN
|
||||
# else
|
||||
# define TTD_ENDIAN TTD_BIG_ENDIAN
|
||||
# endif
|
||||
#elif !defined(TESTING)
|
||||
/* Else include endian[target/host].h, which has the endian-type, autodetected by the Makefile */
|
||||
#if defined(STRGEN) || defined(SETTINGSGEN)
|
||||
#include "endian_host.h"
|
||||
#else
|
||||
#include "endian_target.h"
|
||||
#endif
|
||||
#endif /* WIN32 || __OS2__ || WIN64 */
|
||||
# include <sys/param.h>
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define TTD_ENDIAN TTD_LITTLE_ENDIAN
|
||||
# else
|
||||
# define TTD_ENDIAN TTD_BIG_ENDIAN
|
||||
# endif
|
||||
#endif /* _WIN32 || __OS2__ */
|
||||
|
||||
#endif /* ENDIAN_TYPE_HPP */
|
||||
|
@@ -159,7 +159,7 @@ public:
|
||||
|
||||
/**
|
||||
* Erase a row, replacing it with the last one.
|
||||
* @param x Position of the row.
|
||||
* @param y Position of the row.
|
||||
*/
|
||||
void EraseRow(uint y)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
|
||||
/**
|
||||
* Remove columns from the matrix while preserving the order of other columns.
|
||||
* @param x First column to remove.
|
||||
* @param y First column to remove.
|
||||
* @param count Number of consecutive columns to remove.
|
||||
*/
|
||||
void EraseRowPreservingOrder(uint y, uint count = 1)
|
||||
@@ -210,8 +210,8 @@ public:
|
||||
/**
|
||||
* Set the size to a specific width and height, preserving item positions
|
||||
* as far as possible in the process.
|
||||
* @param width Target width.
|
||||
* @param height Target height.
|
||||
* @param new_width Target width.
|
||||
* @param new_height Target height.
|
||||
*/
|
||||
inline void Resize(uint new_width, uint new_height)
|
||||
{
|
||||
@@ -297,7 +297,7 @@ public:
|
||||
/**
|
||||
* Get column "number" (const)
|
||||
*
|
||||
* @param X Position of the column.
|
||||
* @param x Position of the column.
|
||||
* @return Column at "number".
|
||||
*/
|
||||
inline const T *operator[](uint x) const
|
||||
@@ -309,7 +309,7 @@ public:
|
||||
/**
|
||||
* Get column "number" (const)
|
||||
*
|
||||
* @param X Position of the column.
|
||||
* @param x Position of the column.
|
||||
* @return Column at "number".
|
||||
*/
|
||||
inline T *operator[](uint x)
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
/* rdtsc for MSC_VER, uses simple inline assembly, or _rdtsc
|
||||
* from external win64.asm because VS2005 does not support inline assembly */
|
||||
#if defined(_MSC_VER) && !defined(RDTSC_AVAILABLE) && !defined(WINCE)
|
||||
#if defined(_MSC_VER) && !defined(RDTSC_AVAILABLE)
|
||||
#include <intrin.h>
|
||||
uint64 ottd_rdtsc()
|
||||
{
|
||||
@@ -71,10 +71,7 @@ uint64 ottd_rdtsc()
|
||||
/* In all other cases we have no support for rdtsc. No major issue,
|
||||
* you just won't be able to profile your code with TIC()/TOC() */
|
||||
#if !defined(RDTSC_AVAILABLE)
|
||||
/* MSVC (in case of WinCE) can't handle #warning */
|
||||
# if !defined(_MSC_VER)
|
||||
#warning "(non-fatal) No support for rdtsc(), you won't be able to profile with TIC/TOC"
|
||||
# endif
|
||||
uint64 ottd_rdtsc() {return 0;}
|
||||
#endif
|
||||
|
||||
|
@@ -62,6 +62,7 @@ static const CurrencySpec origin_currency_specs[CURRENCY_END] = {
|
||||
{ 1, "", CF_NOEURO, "", "", 2, STR_GAME_OPTIONS_CURRENCY_CUSTOM }, ///< custom currency (add further languages below)
|
||||
{ 3, "", CF_NOEURO, "", NBSP "GEL", 1, STR_GAME_OPTIONS_CURRENCY_GEL }, ///< Georgian Lari
|
||||
{ 4901, "", CF_NOEURO, "", NBSP "Rls", 1, STR_GAME_OPTIONS_CURRENCY_IRR }, ///< Iranian Rial
|
||||
{ 80, "", CF_NOEURO, "", NBSP "rub", 1, STR_GAME_OPTIONS_CURRENCY_RUB }, ///< New Russian Ruble
|
||||
};
|
||||
|
||||
/** Array of currencies used by the system */
|
||||
|
@@ -58,6 +58,7 @@ enum Currencies {
|
||||
CURRENCY_CUSTOM, ///< Custom currency
|
||||
CURRENCY_GEL, ///< Georgian Lari
|
||||
CURRENCY_IRR, ///< Iranian Rial
|
||||
CURRENCY_RUB, ///< New Russian Ruble
|
||||
CURRENCY_END, ///< always the last item
|
||||
};
|
||||
|
||||
|
@@ -17,6 +17,10 @@
|
||||
#include "fileio_func.h"
|
||||
#include "settings_type.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "os/windows/win32.h"
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#if defined(ENABLE_NETWORK)
|
||||
@@ -135,10 +139,10 @@ static void debug_print(const char *dbg, const char *buf)
|
||||
} else {
|
||||
char buffer[512];
|
||||
seprintf(buffer, lastof(buffer), "%sdbg: [%s] %s\n", GetLogPrefix(), dbg, buf);
|
||||
#if defined(WINCE)
|
||||
NKDbgPrintfW(OTTD2FS(buffer));
|
||||
#elif defined(WIN32) || defined(WIN64)
|
||||
_fputts(OTTD2FS(buffer, true), stderr);
|
||||
#if defined(_WIN32)
|
||||
TCHAR system_buf[512];
|
||||
convert_to_fs(buffer, system_buf, lengthof(system_buf), true);
|
||||
_fputts(system_buf, stderr);
|
||||
#else
|
||||
fputs(buffer, stderr);
|
||||
#endif
|
||||
|
@@ -217,22 +217,22 @@ enum Token {
|
||||
TOKEN_LOCAL, ///< Read a local include
|
||||
TOKEN_GLOBAL, ///< Read a global include
|
||||
TOKEN_IDENTIFIER, ///< Identifier within the data.
|
||||
TOKEN_DEFINE, ///< (#)define in code
|
||||
TOKEN_IF, ///< (#)if in code
|
||||
TOKEN_IFDEF, ///< (#)ifdef in code
|
||||
TOKEN_IFNDEF, ///< (#)ifndef in code
|
||||
TOKEN_ELIF, ///< (#)elif in code
|
||||
TOKEN_ELSE, ///< (#)else in code
|
||||
TOKEN_ENDIF, ///< (#)endif in code
|
||||
TOKEN_UNDEF, ///< (#)undef in code
|
||||
TOKEN_OR, ///< '||' within <tt>#if</tt> expression
|
||||
TOKEN_AND, ///< '&&' within <tt>#if</tt> expression
|
||||
TOKEN_DEFINED, ///< 'defined' within <tt>#if</tt> expression
|
||||
TOKEN_OPEN, ///< '(' within <tt>#if</tt> expression
|
||||
TOKEN_CLOSE, ///< ')' within <tt>#if</tt> expression
|
||||
TOKEN_NOT, ///< '!' within <tt>#if</tt> expression
|
||||
TOKEN_ZERO, ///< '0' within <tt>#if</tt> expression
|
||||
TOKEN_INCLUDE, ///< (#)include in code
|
||||
TOKEN_DEFINE, ///< \c \#define in code
|
||||
TOKEN_IF, ///< \c \#if in code
|
||||
TOKEN_IFDEF, ///< \c \#ifdef in code
|
||||
TOKEN_IFNDEF, ///< \c \#ifndef in code
|
||||
TOKEN_ELIF, ///< \c \#elif in code
|
||||
TOKEN_ELSE, ///< \c \#else in code
|
||||
TOKEN_ENDIF, ///< \c \#endif in code
|
||||
TOKEN_UNDEF, ///< \c \#undef in code
|
||||
TOKEN_OR, ///< '||' within \c \#if expression
|
||||
TOKEN_AND, ///< '&&' within \c \#if expression
|
||||
TOKEN_DEFINED, ///< 'defined' within \c \#if expression
|
||||
TOKEN_OPEN, ///< '(' within \c \#if expression
|
||||
TOKEN_CLOSE, ///< ')' within \c \#if expression
|
||||
TOKEN_NOT, ///< '!' within \c \#if expression
|
||||
TOKEN_ZERO, ///< '0' within \c \#if expression
|
||||
TOKEN_INCLUDE, ///< \c \#include in code
|
||||
};
|
||||
|
||||
/** Mapping from a C-style keyword representation to a Token. */
|
||||
@@ -681,8 +681,8 @@ bool ExpressionOr(Lexer *lexer, StringSet *defines, bool verbose)
|
||||
/** Enumerator to tell how long to ignore 'stuff'. */
|
||||
enum Ignore {
|
||||
NOT_IGNORE, ///< No ignoring.
|
||||
IGNORE_UNTIL_ELSE, ///< Ignore till a #else is reached.
|
||||
IGNORE_UNTIL_ENDIF, ///< Ignore till a #endif is reached.
|
||||
IGNORE_UNTIL_ELSE, ///< Ignore till a \c \#else is reached.
|
||||
IGNORE_UNTIL_ENDIF, ///< Ignore till a \c \#endif is reached.
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -908,9 +908,16 @@ struct DepotWindow : Window {
|
||||
*/
|
||||
virtual bool OnVehicleSelect(const Vehicle *v)
|
||||
{
|
||||
if (DoCommandP(this->window_number, v->index, _ctrl_pressed ? 1 : 0, CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN + v->type), CcCloneVehicle)) {
|
||||
ResetObjectToPlace();
|
||||
if (_ctrl_pressed) {
|
||||
/* Share-clone, do not open new viewport, and keep tool active */
|
||||
DoCommandP(this->window_number, v->index, 1, CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN + v->type), NULL);
|
||||
} else {
|
||||
/* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to changs things on new vehicle) */
|
||||
if (DoCommandP(this->window_number, v->index, 0, CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN + v->type), CcCloneVehicle)) {
|
||||
ResetObjectToPlace();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -164,7 +164,7 @@ DisasterVehicle::DisasterVehicle(int x, int y, Direction direction, DisasterSubT
|
||||
this->direction = direction;
|
||||
this->tile = TileVirtXY(x, y);
|
||||
this->subtype = subtype;
|
||||
this->UpdateDeltaXY(INVALID_DIR);
|
||||
this->UpdateDeltaXY();
|
||||
this->owner = OWNER_NONE;
|
||||
this->image_override = 0;
|
||||
this->current_order.Free();
|
||||
@@ -973,7 +973,7 @@ void ReleaseDisastersTargetingVehicle(VehicleID vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
void DisasterVehicle::UpdateDeltaXY(Direction direction)
|
||||
void DisasterVehicle::UpdateDeltaXY()
|
||||
{
|
||||
this->x_offs = -1;
|
||||
this->y_offs = -1;
|
||||
|
@@ -48,7 +48,7 @@ struct DisasterVehicle FINAL : public SpecializedVehicle<DisasterVehicle, VEH_DI
|
||||
virtual ~DisasterVehicle() {}
|
||||
|
||||
void UpdatePosition(int x, int y, int z);
|
||||
void UpdateDeltaXY(Direction direction);
|
||||
void UpdateDeltaXY();
|
||||
void UpdateImage();
|
||||
bool Tick();
|
||||
};
|
||||
|
@@ -56,8 +56,8 @@ void CcPlaySound_SPLAT_WATER(const CommandCost &result, TileIndex tile, uint32 p
|
||||
|
||||
/**
|
||||
* Gets the other end of the aqueduct, if possible.
|
||||
* @param tile_from The begin tile for the aqueduct.
|
||||
* @param [out] tile_to The tile till where to show a selection for the aqueduct.
|
||||
* @param tile_from The begin tile for the aqueduct.
|
||||
* @param[out] tile_to The tile till where to show a selection for the aqueduct.
|
||||
* @return The other end of the aqueduct, or otherwise a tile in line with the aqueduct to cause the right error message.
|
||||
*/
|
||||
static TileIndex GetOtherAqueductEnd(TileIndex tile_from, TileIndex *tile_to = NULL)
|
||||
|
@@ -95,7 +95,7 @@ const ScoreInfo _score_info[] = {
|
||||
{ 0, 0} // SCORE_TOTAL
|
||||
};
|
||||
|
||||
int _score_part[MAX_COMPANIES][SCORE_END];
|
||||
int64 _score_part[MAX_COMPANIES][SCORE_END];
|
||||
Economy _economy;
|
||||
Prices _price;
|
||||
Money _additional_cash_required;
|
||||
@@ -183,7 +183,7 @@ int UpdateCompanyRatingAndValue(Company *c, bool update)
|
||||
_score_part[owner][SCORE_VEHICLES] = num;
|
||||
/* Don't allow negative min_profit to show */
|
||||
if (min_profit > 0) {
|
||||
_score_part[owner][SCORE_MIN_PROFIT] = ClampToI32(min_profit);
|
||||
_score_part[owner][SCORE_MIN_PROFIT] = min_profit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,10 +213,10 @@ int UpdateCompanyRatingAndValue(Company *c, bool update)
|
||||
} while (++cee, --numec);
|
||||
|
||||
if (min_income > 0) {
|
||||
_score_part[owner][SCORE_MIN_INCOME] = ClampToI32(min_income);
|
||||
_score_part[owner][SCORE_MIN_INCOME] = min_income;
|
||||
}
|
||||
|
||||
_score_part[owner][SCORE_MAX_INCOME] = ClampToI32(max_income);
|
||||
_score_part[owner][SCORE_MAX_INCOME] = max_income;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ int UpdateCompanyRatingAndValue(Company *c, bool update)
|
||||
total_delivered += cee->delivered_cargo.GetSum<OverflowSafeInt64>();
|
||||
} while (++cee, --numec);
|
||||
|
||||
_score_part[owner][SCORE_DELIVERED] = ClampToI32(total_delivered);
|
||||
_score_part[owner][SCORE_DELIVERED] = total_delivered;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,13 +242,13 @@ int UpdateCompanyRatingAndValue(Company *c, bool update)
|
||||
/* Generate score for company's money */
|
||||
{
|
||||
if (c->money > 0) {
|
||||
_score_part[owner][SCORE_MONEY] = ClampToI32(c->money);
|
||||
_score_part[owner][SCORE_MONEY] = c->money;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate score for loan */
|
||||
{
|
||||
_score_part[owner][SCORE_LOAN] = ClampToI32(_score_info[SCORE_LOAN].needed - c->current_loan);
|
||||
_score_part[owner][SCORE_LOAN] = _score_info[SCORE_LOAN].needed - c->current_loan;
|
||||
}
|
||||
|
||||
/* Now we calculate the score for each item.. */
|
||||
@@ -696,9 +696,10 @@ static void CompaniesGenStatistics()
|
||||
if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, _cur_month)) return;
|
||||
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
memmove(&c->old_economy[1], &c->old_economy[0], sizeof(c->old_economy) - sizeof(c->old_economy[0]));
|
||||
/* Drop the oldest history off the end */
|
||||
std::copy_backward(c->old_economy, c->old_economy + MAX_HISTORY_QUARTERS - 1, c->old_economy + MAX_HISTORY_QUARTERS);
|
||||
c->old_economy[0] = c->cur_economy;
|
||||
memset(&c->cur_economy, 0, sizeof(c->cur_economy));
|
||||
c->cur_economy = {};
|
||||
|
||||
if (c->num_valid_stat_ent != MAX_HISTORY_QUARTERS) c->num_valid_stat_ent++;
|
||||
|
||||
@@ -1061,6 +1062,7 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n
|
||||
|
||||
uint amount = min(num_pieces, 0xFFFFU - ind->incoming_cargo_waiting[cargo_index]);
|
||||
ind->incoming_cargo_waiting[cargo_index] += amount;
|
||||
ind->last_cargo_accepted_at[cargo_index] = _date;
|
||||
num_pieces -= amount;
|
||||
accepted += amount;
|
||||
}
|
||||
@@ -1137,7 +1139,6 @@ static void TriggerIndustryProduction(Industry *i)
|
||||
uint16 callback = indspec->callback_mask;
|
||||
|
||||
i->was_cargo_delivered = true;
|
||||
i->last_cargo_accepted_at = _date;
|
||||
|
||||
if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
|
||||
if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) {
|
||||
@@ -1146,14 +1147,15 @@ static void TriggerIndustryProduction(Industry *i)
|
||||
SetWindowDirty(WC_INDUSTRY_VIEW, i->index);
|
||||
}
|
||||
} else {
|
||||
for (uint cargo_index = 0; cargo_index < lengthof(i->incoming_cargo_waiting); cargo_index++) {
|
||||
uint cargo_waiting = i->incoming_cargo_waiting[cargo_index];
|
||||
for (uint ci_in = 0; ci_in < lengthof(i->incoming_cargo_waiting); ci_in++) {
|
||||
uint cargo_waiting = i->incoming_cargo_waiting[ci_in];
|
||||
if (cargo_waiting == 0) continue;
|
||||
|
||||
i->produced_cargo_waiting[0] = min(i->produced_cargo_waiting[0] + (cargo_waiting * indspec->input_cargo_multiplier[cargo_index][0] / 256), 0xFFFF);
|
||||
i->produced_cargo_waiting[1] = min(i->produced_cargo_waiting[1] + (cargo_waiting * indspec->input_cargo_multiplier[cargo_index][1] / 256), 0xFFFF);
|
||||
for (uint ci_out = 0; ci_out < lengthof(i->produced_cargo_waiting); ci_out++) {
|
||||
i->produced_cargo_waiting[ci_out] = min(i->produced_cargo_waiting[ci_out] + (cargo_waiting * indspec->input_cargo_multiplier[ci_in][ci_out] / 256), 0xFFFF);
|
||||
}
|
||||
|
||||
i->incoming_cargo_waiting[cargo_index] = 0;
|
||||
i->incoming_cargo_waiting[ci_in] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1241,7 +1243,6 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
|
||||
|
||||
/**
|
||||
* Prepare the vehicle to be unloaded.
|
||||
* @param curr_station the station where the consist is at the moment
|
||||
* @param front_v the vehicle to be unloaded
|
||||
*/
|
||||
void PrepareUnload(Vehicle *front_v)
|
||||
@@ -1366,14 +1367,14 @@ struct IsEmptyAction
|
||||
struct PrepareRefitAction
|
||||
{
|
||||
CargoArray &consist_capleft; ///< Capacities left in the consist.
|
||||
uint32 &refit_mask; ///< Bitmask of possible refit cargoes.
|
||||
CargoTypes &refit_mask; ///< Bitmask of possible refit cargoes.
|
||||
|
||||
/**
|
||||
* Create a refit preparation action.
|
||||
* @param consist_capleft Capacities left in consist, to be updated here.
|
||||
* @param refit_mask Refit mask to be constructed from refit information of vehicles.
|
||||
*/
|
||||
PrepareRefitAction(CargoArray &consist_capleft, uint32 &refit_mask) :
|
||||
PrepareRefitAction(CargoArray &consist_capleft, CargoTypes &refit_mask) :
|
||||
consist_capleft(consist_capleft), refit_mask(refit_mask) {}
|
||||
|
||||
/**
|
||||
@@ -1469,7 +1470,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
|
||||
|
||||
uint32 refit_mask = v->GetEngine()->info.refit_mask;
|
||||
CargoTypes refit_mask = v->GetEngine()->info.refit_mask;
|
||||
|
||||
/* Remove old capacity from consist capacity and collect refit mask. */
|
||||
IterateVehicleParts(v_start, PrepareRefitAction(consist_capleft, refit_mask));
|
||||
@@ -1627,10 +1628,10 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
bool completely_emptied = true;
|
||||
bool anything_unloaded = false;
|
||||
bool anything_loaded = false;
|
||||
uint32 full_load_amount = 0;
|
||||
uint32 cargo_not_full = 0;
|
||||
uint32 cargo_full = 0;
|
||||
uint32 reservation_left = 0;
|
||||
CargoTypes full_load_amount = 0;
|
||||
CargoTypes cargo_not_full = 0;
|
||||
CargoTypes cargo_full = 0;
|
||||
CargoTypes reservation_left = 0;
|
||||
|
||||
front->cur_speed = 0;
|
||||
|
||||
@@ -1838,7 +1839,7 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
/* if the aircraft carries passengers and is NOT full, then
|
||||
* continue loading, no matter how much mail is in */
|
||||
if ((front->type == VEH_AIRCRAFT && IsCargoInClass(front->cargo_type, CC_PASSENGERS) && front->cargo_cap > front->cargo.StoredCount()) ||
|
||||
(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are still non-full cargoes
|
||||
(cargo_not_full != 0 && (cargo_full & ~cargo_not_full) == 0)) { // There are still non-full cargoes
|
||||
finished_loading = false;
|
||||
}
|
||||
} else if (cargo_not_full != 0) {
|
||||
|
@@ -22,7 +22,7 @@ void ResetPriceBaseMultipliers();
|
||||
void SetPriceBaseMultiplier(Price price, int factor);
|
||||
|
||||
extern const ScoreInfo _score_info[];
|
||||
extern int _score_part[MAX_COMPANIES][SCORE_END];
|
||||
extern int64 _score_part[MAX_COMPANIES][SCORE_END];
|
||||
extern Economy _economy;
|
||||
/* Prices and also the fractional part. */
|
||||
extern Prices _price;
|
||||
|
@@ -152,12 +152,12 @@ enum ExpensesType {
|
||||
EXPENSES_NEW_VEHICLES, ///< New vehicles.
|
||||
EXPENSES_TRAIN_RUN, ///< Running costs trains.
|
||||
EXPENSES_ROADVEH_RUN, ///< Running costs road vehicles.
|
||||
EXPENSES_AIRCRAFT_RUN, ///< Running costs aircrafts.
|
||||
EXPENSES_AIRCRAFT_RUN, ///< Running costs aircraft.
|
||||
EXPENSES_SHIP_RUN, ///< Running costs ships.
|
||||
EXPENSES_PROPERTY, ///< Property costs.
|
||||
EXPENSES_TRAIN_INC, ///< Income from trains.
|
||||
EXPENSES_ROADVEH_INC, ///< Income from road vehicles.
|
||||
EXPENSES_AIRCRAFT_INC, ///< Income from aircrafts.
|
||||
EXPENSES_AIRCRAFT_INC, ///< Income from aircraft.
|
||||
EXPENSES_SHIP_INC, ///< Income from ships.
|
||||
EXPENSES_LOAN_INT, ///< Interest payments over the loan.
|
||||
EXPENSES_OTHER, ///< Other expenses.
|
||||
|
@@ -603,7 +603,7 @@ EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
|
||||
v->y_pos = y;
|
||||
v->z_pos = z;
|
||||
v->tile = 0;
|
||||
v->UpdateDeltaXY(INVALID_DIR);
|
||||
v->UpdateDeltaXY();
|
||||
v->vehstatus = VS_UNCLICKABLE;
|
||||
|
||||
_effect_init_procs[type](v);
|
||||
@@ -647,7 +647,7 @@ bool EffectVehicle::Tick()
|
||||
return _effect_tick_procs[this->subtype](this);
|
||||
}
|
||||
|
||||
void EffectVehicle::UpdateDeltaXY(Direction direction)
|
||||
void EffectVehicle::UpdateDeltaXY()
|
||||
{
|
||||
this->x_offs = 0;
|
||||
this->y_offs = 0;
|
||||
|
@@ -32,7 +32,7 @@ struct EffectVehicle FINAL : public SpecializedVehicle<EffectVehicle, VEH_EFFECT
|
||||
/** We want to 'destruct' the right class. */
|
||||
virtual ~EffectVehicle() {}
|
||||
|
||||
void UpdateDeltaXY(Direction direction);
|
||||
void UpdateDeltaXY();
|
||||
bool Tick();
|
||||
TransparencyOption GetTransparencyOption() const;
|
||||
};
|
||||
|
@@ -1,78 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file endian_check.cpp
|
||||
* This pretty simple file checks if the system is LITTLE_ENDIAN or BIG_ENDIAN
|
||||
* it does that by putting a 1 and a 0 in an array, and read it out as one
|
||||
* number. If it is 1, it is LITTLE_ENDIAN, if it is 256, it is BIG_ENDIAN
|
||||
*
|
||||
* After that it outputs the contents of an include files (endian.h)
|
||||
* that says or TTD_LITTLE_ENDIAN, or TTD_BIG_ENDIAN. Makefile takes
|
||||
* care of the real writing to the file.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/** Supported endian types */
|
||||
enum Endian {
|
||||
ENDIAN_LITTLE, ///< little endian
|
||||
ENDIAN_BIG, ///< big endian
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut to printf("#define TTD_ENDIAN TTD_*_ENDIAN")
|
||||
* @param endian endian type to define
|
||||
*/
|
||||
static inline void printf_endian(Endian endian)
|
||||
{
|
||||
printf("#define TTD_ENDIAN %s\n", endian == ENDIAN_LITTLE ? "TTD_LITTLE_ENDIAN" : "TTD_BIG_ENDIAN");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main call of the endian_check program
|
||||
* @param argc argument count
|
||||
* @param argv arguments themselves
|
||||
* @return exit code
|
||||
*/
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
unsigned char endian_test[2] = { 1, 0 };
|
||||
int force_BE = 0, force_LE = 0, force_PREPROCESSOR = 0;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "BE") == 0) force_BE = 1;
|
||||
if (argc > 1 && strcmp(argv[1], "LE") == 0) force_LE = 1;
|
||||
if (argc > 1 && strcmp(argv[1], "PREPROCESSOR") == 0) force_PREPROCESSOR = 1;
|
||||
|
||||
printf("#ifndef ENDIAN_H\n#define ENDIAN_H\n");
|
||||
|
||||
if (force_LE == 1) {
|
||||
printf_endian(ENDIAN_LITTLE);
|
||||
} else if (force_BE == 1) {
|
||||
printf_endian(ENDIAN_BIG);
|
||||
} else if (force_PREPROCESSOR == 1) {
|
||||
/* Support for universal binaries on OSX
|
||||
* Universal binaries supports both PPC and x86
|
||||
* If a compiler for OSX gets this setting, it will always pick the correct endian and no test is needed
|
||||
*/
|
||||
printf("#ifdef __BIG_ENDIAN__\n");
|
||||
printf_endian(ENDIAN_BIG);
|
||||
printf("#else\n");
|
||||
printf_endian(ENDIAN_LITTLE);
|
||||
printf("#endif\n");
|
||||
} else if (*(short*)endian_test == 1 ) {
|
||||
printf_endian(ENDIAN_LITTLE);
|
||||
} else {
|
||||
printf_endian(ENDIAN_BIG);
|
||||
}
|
||||
printf("#endif\n");
|
||||
|
||||
return 0;
|
||||
}
|
@@ -431,9 +431,9 @@ uint Engine::GetDisplayMaxTractiveEffort() const
|
||||
/* Only trains and road vehicles have 'tractive effort'. */
|
||||
switch (this->type) {
|
||||
case VEH_TRAIN:
|
||||
return (10 * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->u.rail.tractive_effort)) / 256;
|
||||
return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->u.rail.tractive_effort)) / 256 / 1000;
|
||||
case VEH_ROAD:
|
||||
return (10 * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->u.road.tractive_effort)) / 256;
|
||||
return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->u.road.tractive_effort)) / 256 / 1000;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
@@ -573,7 +573,7 @@ static bool IsWagon(EngineID index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update #reliability of engine \a e, (if needed) update the engine GUIs.
|
||||
* Update #Engine::reliability and (if needed) update the engine GUIs.
|
||||
* @param e %Engine to update.
|
||||
*/
|
||||
static void CalcEngineReliability(Engine *e)
|
||||
@@ -752,7 +752,7 @@ static CompanyID GetPreviewCompany(Engine *e)
|
||||
CompanyID best_company = INVALID_COMPANY;
|
||||
|
||||
/* For trains the cargomask has no useful meaning, since you can attach other wagons */
|
||||
uint32 cargomask = e->type != VEH_TRAIN ? GetUnionOfArticulatedRefitMasks(e->index, true) : (uint32)-1;
|
||||
CargoTypes cargomask = e->type != VEH_TRAIN ? GetUnionOfArticulatedRefitMasks(e->index, true) : ALL_CARGOTYPES;
|
||||
|
||||
int32 best_hist = -1;
|
||||
const Company *c;
|
||||
@@ -1117,7 +1117,9 @@ bool IsEngineRefittable(EngineID engine)
|
||||
|
||||
/* Is there any cargo except the default cargo? */
|
||||
CargoID default_cargo = e->GetDefaultCargoType();
|
||||
return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo;
|
||||
CargoTypes default_cargo_mask = 0;
|
||||
SetBit(default_cargo_mask, default_cargo);
|
||||
return default_cargo != CT_INVALID && ei->refit_mask != default_cargo_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,7 +26,7 @@ extern const uint8 _engine_offsets[4];
|
||||
|
||||
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company);
|
||||
bool IsEngineRefittable(EngineID engine);
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, uint32 *refits);
|
||||
void GetArticulatedVehicleCargoesAndRefits(EngineID engine, CargoArray *cargoes, CargoTypes *refits);
|
||||
void SetYearEngineAgingStops();
|
||||
void StartupOneEngine(Engine *e, Date aging_date);
|
||||
|
||||
|
@@ -137,7 +137,7 @@ struct EngineInfo {
|
||||
byte load_amount;
|
||||
byte climates; ///< Climates supported by the engine.
|
||||
CargoID cargo_type;
|
||||
uint32 refit_mask;
|
||||
CargoTypes refit_mask;
|
||||
byte refit_cost;
|
||||
byte misc_flags; ///< Miscellaneous flags. @see EngineMiscFlags
|
||||
byte callback_mask; ///< Bitmask of vehicle callbacks that have to be called
|
||||
|
@@ -425,7 +425,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel
|
||||
/**
|
||||
* Schedule a list of errors.
|
||||
* Note: This does not try to display the error now. This is useful if the window system is not yet running.
|
||||
* @param data Error message datas; cleared afterwards
|
||||
* @param datas Error message datas; cleared afterwards
|
||||
*/
|
||||
void ScheduleErrorMessage(ErrorList &datas)
|
||||
{
|
||||
|
@@ -15,7 +15,7 @@
|
||||
#include "fios.h"
|
||||
#include "string_func.h"
|
||||
#include "tar_type.h"
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
# define access _taccess
|
||||
#elif defined(__HAIKU__)
|
||||
@@ -325,15 +325,7 @@ bool FioCheckFileExists(const char *filename, Subdirectory subdir)
|
||||
*/
|
||||
bool FileExists(const char *filename)
|
||||
{
|
||||
#if defined(WINCE)
|
||||
/* There is always one platform that doesn't support basic commands... */
|
||||
HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (hand == INVALID_HANDLE_VALUE) return 1;
|
||||
CloseHandle(hand);
|
||||
return 0;
|
||||
#else
|
||||
return access(OTTD2FS(filename), 0) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +347,7 @@ char *FioGetFullPath(char *buf, const char *last, Searchpath sp, Subdirectory su
|
||||
|
||||
/**
|
||||
* Find a path to the filename in one of the search directories.
|
||||
* @param buf [out] Destination buffer for the path.
|
||||
* @param[out] buf Destination buffer for the path.
|
||||
* @param last End of the destination buffer.
|
||||
* @param subdir Subdirectory to try.
|
||||
* @param filename Filename to look for.
|
||||
@@ -369,7 +361,7 @@ char *FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const ch
|
||||
FOR_ALL_SEARCHPATHS(sp) {
|
||||
FioGetFullPath(buf, last, sp, subdir, filename);
|
||||
if (FileExists(buf)) return buf;
|
||||
#if !defined(WIN32)
|
||||
#if !defined(_WIN32)
|
||||
/* Be, as opening files, aware that sometimes the filename
|
||||
* might be in uppercase when it is in lowercase on the
|
||||
* disk. Of course Windows doesn't care about casing. */
|
||||
@@ -407,7 +399,7 @@ char *FioGetDirectory(char *buf, const char *last, Subdirectory subdir)
|
||||
|
||||
static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
|
||||
{
|
||||
#if defined(WIN32) && defined(UNICODE)
|
||||
#if defined(_WIN32) && defined(UNICODE)
|
||||
/* fopen is implemented as a define with ellipses for
|
||||
* Unicode support (prepend an L). As we are not sending
|
||||
* a string, but a variable, it 'renames' the variable,
|
||||
@@ -424,12 +416,12 @@ static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath s
|
||||
seprintf(buf, lastof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
|
||||
#endif
|
||||
|
||||
f = fopen(buf, mode);
|
||||
#if !defined(WIN32)
|
||||
#if !defined(_WIN32)
|
||||
if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
|
||||
f = fopen(buf, mode);
|
||||
}
|
||||
@@ -446,7 +438,7 @@ static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath s
|
||||
/**
|
||||
* Opens a file from inside a tar archive.
|
||||
* @param entry The entry to open.
|
||||
* @param filesize [out] If not \c NULL, size of the opened file.
|
||||
* @param[out] filesize If not \c NULL, size of the opened file.
|
||||
* @return File handle of the opened file, or \c NULL if the file is not available.
|
||||
* @note The file is read from within the tar file, and may not return \c EOF after reading the whole file.
|
||||
*/
|
||||
@@ -468,7 +460,6 @@ FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
|
||||
* Opens a OpenTTD file somewhere in a personal or global directory.
|
||||
* @param filename Name of the file to open.
|
||||
* @param subdir Subdirectory to open.
|
||||
* @param filename Name of the file to open.
|
||||
* @return File handle of the opened file, or \c NULL if the file is not available.
|
||||
*/
|
||||
FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
|
||||
@@ -540,11 +531,11 @@ FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir,
|
||||
* Create a directory with the given name
|
||||
* @param name the new name of the directory
|
||||
*/
|
||||
static void FioCreateDirectory(const char *name)
|
||||
void FioCreateDirectory(const char *name)
|
||||
{
|
||||
/* Ignore directory creation errors; they'll surface later on, and most
|
||||
* of the time they are 'directory already exists' errors anyhow. */
|
||||
#if defined(WIN32) || defined(WINCE)
|
||||
#if defined(_WIN32)
|
||||
CreateDirectory(OTTD2FS(name), NULL);
|
||||
#elif defined(OS2) && !defined(__INNOTEK_LIBC__)
|
||||
mkdir(OTTD2FS(name));
|
||||
@@ -640,7 +631,7 @@ static void SimplifyFileName(char *name)
|
||||
|
||||
/**
|
||||
* Perform the scanning of a particular subdirectory.
|
||||
* @param subdir The subdirectory to scan.
|
||||
* @param sd The subdirectory to scan.
|
||||
* @return The number of found tar files.
|
||||
*/
|
||||
uint TarScanner::DoScan(Subdirectory sd)
|
||||
@@ -988,14 +979,14 @@ bool ExtractTar(const char *tar_filename, Subdirectory subdir)
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(WINCE)
|
||||
#if defined(_WIN32)
|
||||
/**
|
||||
* Determine the base (personal dir and game data dir) paths
|
||||
* @param exe the path from the current path to the executable
|
||||
* @note defined in the OS related files (os2.cpp, win32.cpp, unix.cpp etc)
|
||||
*/
|
||||
extern void DetermineBasePaths(const char *exe);
|
||||
#else /* defined(WIN32) || defined(WINCE) */
|
||||
#else /* defined(_WIN32) */
|
||||
|
||||
/**
|
||||
* Changes the working directory to the path of the give executable.
|
||||
@@ -1158,7 +1149,7 @@ extern void cocoaSetApplicationBundleDir();
|
||||
_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
|
||||
#endif
|
||||
}
|
||||
#endif /* defined(WIN32) || defined(WINCE) */
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
const char *_personal_dir;
|
||||
|
||||
@@ -1311,7 +1302,7 @@ void SanitizeFilename(char *filename)
|
||||
/**
|
||||
* Load a file into memory.
|
||||
* @param filename Name of the file to load.
|
||||
* @param lenp [out] Length of loaded data.
|
||||
* @param[out] lenp Length of loaded data.
|
||||
* @param maxsize Maximum size to load.
|
||||
* @return Pointer to new memory containing the loaded data, or \c NULL if loading failed.
|
||||
* @note If \a maxsize less than the length of the file, loading fails.
|
||||
|
@@ -55,6 +55,7 @@ char *FioGetFullPath(char *buf, const char *last, Searchpath sp, Subdirectory su
|
||||
char *FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename);
|
||||
char *FioAppendDirectory(char *buf, const char *last, Searchpath sp, Subdirectory subdir);
|
||||
char *FioGetDirectory(char *buf, const char *last, Subdirectory subdir);
|
||||
void FioCreateDirectory(const char *name);
|
||||
|
||||
const char *FiosGetScreenshotDir();
|
||||
|
||||
@@ -117,7 +118,7 @@ public:
|
||||
DECLARE_ENUM_AS_BIT_SET(TarScanner::Mode)
|
||||
|
||||
/* Implementation of opendir/readdir/closedir for Windows */
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
struct DIR;
|
||||
|
||||
struct dirent { // XXX - only d_name implemented
|
||||
@@ -135,7 +136,7 @@ int closedir(DIR *d);
|
||||
/* Use system-supplied opendir/readdir/closedir functions */
|
||||
# include <sys/types.h>
|
||||
# include <dirent.h>
|
||||
#endif /* defined(WIN32) */
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/**
|
||||
* A wrapper around opendir() which will convert the string from
|
||||
@@ -149,4 +150,17 @@ static inline DIR *ttd_opendir(const char *path)
|
||||
return opendir(OTTD2FS(path));
|
||||
}
|
||||
|
||||
|
||||
/** Auto-close a file upon scope exit. */
|
||||
class FileCloser {
|
||||
FILE *f;
|
||||
|
||||
public:
|
||||
FileCloser(FILE *_f) : f(_f) {}
|
||||
~FileCloser()
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* FILEIO_FUNC_H */
|
||||
|
10
src/fios.cpp
10
src/fios.cpp
@@ -20,9 +20,9 @@
|
||||
#include "string_func.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif /* WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -153,9 +153,7 @@ const char *FiosBrowseTo(const FiosItem *item)
|
||||
{
|
||||
switch (item->type) {
|
||||
case FIOS_TYPE_DRIVE:
|
||||
#if defined(WINCE)
|
||||
seprintf(_fios_path, _fios_path_last, PATHSEP "");
|
||||
#elif defined(WIN32) || defined(__OS2__)
|
||||
#if defined(_WIN32) || defined(__OS2__)
|
||||
seprintf(_fios_path, _fios_path_last, "%c:" PATHSEP, item->title[0]);
|
||||
#endif
|
||||
break;
|
||||
@@ -320,7 +318,7 @@ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, cons
|
||||
}
|
||||
|
||||
FiosItem *fios = file_list.Append();
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
struct _stat sb;
|
||||
if (_tstat(OTTD2FS(filename), &sb) == 0) {
|
||||
#else
|
||||
|
@@ -18,6 +18,13 @@
|
||||
#include "network/core/tcp_content.h"
|
||||
|
||||
|
||||
/** Special values for save-load window for the data parameter of #InvalidateWindowData. */
|
||||
enum SaveLoadInvalidateWindowData {
|
||||
SLIWD_RESCAN_FILES, ///< Rescan all files (when changed directory, ...)
|
||||
SLIWD_SELECTION_CHANGES, ///< File selection has changed (user click, ...)
|
||||
SLIWD_FILTER_CHANGES, ///< The filename filter has changed (via the editbox)
|
||||
};
|
||||
|
||||
typedef SmallMap<uint, CompanyProperties *> CompanyPropertiesMap;
|
||||
|
||||
/**
|
||||
|
134
src/fios_gui.cpp
134
src/fios_gui.cpp
@@ -28,6 +28,7 @@
|
||||
#include "date_func.h"
|
||||
#include "core/geometry_func.hpp"
|
||||
#include "gamelog.h"
|
||||
#include "stringfilter_type.h"
|
||||
|
||||
#include "widgets/fios_widget.h"
|
||||
|
||||
@@ -76,9 +77,20 @@ static const NWidgetPart _nested_load_dialog_widgets[] = {
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SL_CAPTION),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
/* Current directory and free space */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
|
||||
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
/* Left side : filter box and available files */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 1),
|
||||
/* Filter box with label */
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, 0, WD_FRAMERECT_BOTTOM, 0),
|
||||
SetPIP(WD_FRAMETEXT_LEFT, WD_FRAMETEXT_RIGHT, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetFill(0, 1), SetDataTip(STR_SAVELOAD_FILTER_TITLE , STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
/* Sort buttons */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
|
||||
@@ -86,6 +98,7 @@ static const NWidgetPart _nested_load_dialog_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
|
||||
EndContainer(),
|
||||
/* Files */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_FILE_BACKGROUND),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_INSET, COLOUR_GREY, WID_SL_DRIVES_DIRECTORIES_LIST), SetFill(1, 1), SetPadding(2, 1, 2, 2),
|
||||
@@ -98,6 +111,8 @@ static const NWidgetPart _nested_load_dialog_widgets[] = {
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
|
||||
/* Right side : game details */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SL_DETAILS), SetResize(1, 1), SetFill(1, 1),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_MISSING_NEWGRFS), SetDataTip(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON, STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
|
||||
@@ -119,8 +134,18 @@ static const NWidgetPart _nested_load_heightmap_dialog_widgets[] = {
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SL_CAPTION),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
/* Current directory and free space */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 1),
|
||||
/* Filter box with label */
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, 0, WD_FRAMERECT_BOTTOM, 0),
|
||||
SetPIP(WD_FRAMETEXT_LEFT, WD_FRAMETEXT_RIGHT, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetFill(0, 1), SetDataTip(STR_SAVELOAD_FILTER_TITLE , STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
/* Sort Buttons */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
|
||||
@@ -128,6 +153,7 @@ static const NWidgetPart _nested_load_heightmap_dialog_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
|
||||
EndContainer(),
|
||||
/* Files */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_FILE_BACKGROUND),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_INSET, COLOUR_GREY, WID_SL_DRIVES_DIRECTORIES_LIST), SetFill(1, 1), SetPadding(2, 1, 2, 2),
|
||||
@@ -152,9 +178,19 @@ static const NWidgetPart _nested_save_dialog_widgets[] = {
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SL_CAPTION),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
/* Current directory and free space */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
/* Left side : filter box and available files */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 1),
|
||||
/* Filter box with label */
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(WD_FRAMERECT_TOP, 0, WD_FRAMERECT_BOTTOM, 0),
|
||||
SetPIP(WD_FRAMETEXT_LEFT, WD_FRAMETEXT_RIGHT, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetFill(0, 1), SetDataTip(STR_SAVELOAD_FILTER_TITLE , STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
/* Sort buttons */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
|
||||
@@ -162,6 +198,7 @@ static const NWidgetPart _nested_save_dialog_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
|
||||
EndContainer(),
|
||||
/* Files */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_FILE_BACKGROUND),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_INSET, COLOUR_GREY, WID_SL_DRIVES_DIRECTORIES_LIST), SetPadding(2, 1, 0, 2),
|
||||
@@ -171,11 +208,14 @@ static const NWidgetPart _nested_save_dialog_widgets[] = {
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_SAVE_OSK_TITLE), SetPadding(3, 2, 2, 2), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_SAVELOAD_OSKTITLE, STR_SAVELOAD_EDITBOX_TOOLTIP),
|
||||
EndContainer(),
|
||||
/* Save/delete buttons */
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_DELETE_SELECTION), SetDataTip(STR_SAVELOAD_DELETE_BUTTON, STR_SAVELOAD_DELETE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SAVE_GAME), SetDataTip(STR_SAVELOAD_SAVE_BUTTON, STR_SAVELOAD_SAVE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
|
||||
/* Right side : game details */
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SL_DETAILS), SetResize(1, 1), SetFill(1, 1),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
@@ -201,7 +241,7 @@ static const TextColour _fios_colours[] = {
|
||||
|
||||
/**
|
||||
* Sort the collected list save games prior to displaying it in the save/load gui.
|
||||
* @param [inout] file_list List of save game files found in the directory.
|
||||
* @param[in,out] file_list List of save game files found in the directory.
|
||||
*/
|
||||
static void SortSaveGameList(FileList &file_list)
|
||||
{
|
||||
@@ -227,6 +267,8 @@ static void SortSaveGameList(FileList &file_list)
|
||||
|
||||
struct SaveLoadWindow : public Window {
|
||||
private:
|
||||
static const uint EDITBOX_MAX_SIZE = 50;
|
||||
|
||||
QueryString filename_editbox; ///< Filename editbox.
|
||||
AbstractFileType abstract_filetype; /// Type of file to select.
|
||||
SaveLoadOperation fop; ///< File operation to perform.
|
||||
@@ -234,6 +276,11 @@ private:
|
||||
FiosItem o_dir;
|
||||
const FiosItem *selected; ///< Selected game in #fios_items, or \c NULL.
|
||||
Scrollbar *vscroll;
|
||||
|
||||
StringFilter string_filter; ///< Filter for available games.
|
||||
QueryString filter_editbox; ///< Filter editbox;
|
||||
SmallVector<bool, 32> fios_items_shown; ///< Map of the filtered out fios items
|
||||
|
||||
public:
|
||||
|
||||
/** Generate a default save filename. */
|
||||
@@ -244,7 +291,7 @@ public:
|
||||
}
|
||||
|
||||
SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop)
|
||||
: Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop)
|
||||
: Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop), filter_editbox(EDITBOX_MAX_SIZE)
|
||||
{
|
||||
assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD);
|
||||
|
||||
@@ -296,6 +343,8 @@ public:
|
||||
this->FinishInitNested(0);
|
||||
|
||||
this->LowerWidget(WID_SL_DRIVES_DIRECTORIES_LIST);
|
||||
this->querystrings[WID_SL_FILTER] = &this->filter_editbox;
|
||||
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
|
||||
|
||||
/* pause is only used in single-player, non-editor mode, non-menu mode. It
|
||||
* will be unpaused in the WE_DESTROY event handler. */
|
||||
@@ -304,7 +353,7 @@ public:
|
||||
}
|
||||
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
|
||||
|
||||
this->OnInvalidateData(0);
|
||||
this->OnInvalidateData(SLIWD_RESCAN_FILES);
|
||||
|
||||
ResetObjectToPlace();
|
||||
|
||||
@@ -327,8 +376,15 @@ public:
|
||||
strecpy(o_dir.name, _personal_dir, lastof(o_dir.name));
|
||||
}
|
||||
|
||||
/* Focus the edit box by default in the save windows */
|
||||
if (this->fop == SLO_SAVE) this->SetFocusedWidget(WID_SL_SAVE_OSK_TITLE);
|
||||
switch (this->fop) {
|
||||
case SLO_SAVE:
|
||||
/* Focus the edit box by default in the save window */
|
||||
this->SetFocusedWidget(WID_SL_SAVE_OSK_TITLE);
|
||||
break;
|
||||
|
||||
default:
|
||||
this->SetFocusedWidget(WID_SL_FILTER);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~SaveLoadWindow()
|
||||
@@ -370,6 +426,10 @@ public:
|
||||
|
||||
uint y = r.top + WD_FRAMERECT_TOP;
|
||||
for (uint pos = this->vscroll->GetPosition(); pos < this->fios_items.Length(); pos++) {
|
||||
if (!this->fios_items_shown[pos]) {
|
||||
/* The current item is filtered out : we do not show it */
|
||||
continue;
|
||||
}
|
||||
const FiosItem *item = this->fios_items.Get(pos);
|
||||
|
||||
if (item == this->selected) {
|
||||
@@ -507,7 +567,6 @@ public:
|
||||
SortSaveGameList(this->fios_items);
|
||||
}
|
||||
|
||||
this->vscroll->SetCount(this->fios_items.Length());
|
||||
this->DrawWidgets();
|
||||
}
|
||||
|
||||
@@ -530,7 +589,7 @@ public:
|
||||
|
||||
case WID_SL_HOME_BUTTON: // OpenTTD 'button', jumps to OpenTTD directory
|
||||
FiosBrowseTo(&o_dir);
|
||||
this->InvalidateData();
|
||||
this->InvalidateData(SLIWD_RESCAN_FILES);
|
||||
break;
|
||||
|
||||
case WID_SL_LOAD_BUTTON:
|
||||
@@ -572,6 +631,12 @@ public:
|
||||
int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WD_FRAMERECT_TOP);
|
||||
if (y == INT_MAX) return;
|
||||
|
||||
/* Get the corresponding non-filtered out item from the list */
|
||||
int i = 0;
|
||||
while (i <= y) {
|
||||
if (!this->fios_items_shown[i]) y++;
|
||||
i++;
|
||||
}
|
||||
const FiosItem *file = this->fios_items.Get(y);
|
||||
|
||||
const char *name = FiosBrowseTo(file);
|
||||
@@ -586,7 +651,7 @@ public:
|
||||
SaveOrLoad(name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false);
|
||||
}
|
||||
|
||||
this->InvalidateData(1);
|
||||
this->InvalidateData(SLIWD_SELECTION_CHANGES);
|
||||
}
|
||||
if (this->fop == SLO_SAVE) {
|
||||
/* Copy clicked name to editbox */
|
||||
@@ -611,7 +676,7 @@ public:
|
||||
}
|
||||
} else {
|
||||
/* Changed directory, need refresh. */
|
||||
this->InvalidateData();
|
||||
this->InvalidateData(SLIWD_RESCAN_FILES);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -660,7 +725,7 @@ public:
|
||||
if (!FiosDelete(this->filename_editbox.text.buf)) {
|
||||
ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, WL_ERROR);
|
||||
} else {
|
||||
this->InvalidateData();
|
||||
this->InvalidateData(SLIWD_RESCAN_FILES);
|
||||
/* Reset file name to current date on successful delete */
|
||||
if (this->abstract_filetype == FT_SAVEGAME) GenerateFileName();
|
||||
}
|
||||
@@ -691,7 +756,7 @@ public:
|
||||
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
|
||||
{
|
||||
switch (data) {
|
||||
case 0:
|
||||
case SLIWD_RESCAN_FILES:
|
||||
/* Rescan files */
|
||||
this->selected = NULL;
|
||||
_load_check_data.Clear();
|
||||
@@ -702,9 +767,13 @@ public:
|
||||
this->vscroll->SetCount(this->fios_items.Length());
|
||||
this->selected = NULL;
|
||||
_load_check_data.Clear();
|
||||
|
||||
/* We reset the files filtered */
|
||||
this->OnInvalidateData(SLIWD_FILTER_CHANGES);
|
||||
|
||||
FALLTHROUGH;
|
||||
|
||||
case 1:
|
||||
case SLIWD_SELECTION_CHANGES:
|
||||
/* Selection changes */
|
||||
if (!gui_scope) break;
|
||||
|
||||
@@ -732,6 +801,41 @@ public:
|
||||
NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
|
||||
case SLIWD_FILTER_CHANGES:
|
||||
/* Filter changes */
|
||||
this->fios_items_shown.Resize(this->fios_items.Length());
|
||||
uint items_shown_count = 0; ///< The number of items shown in the list
|
||||
/* We pass through every fios item */
|
||||
for (uint i = 0; i < this->fios_items.Length(); i++) {
|
||||
if (this->string_filter.IsEmpty()) {
|
||||
/* We don't filter anything out if the filter editbox is empty */
|
||||
this->fios_items_shown[i] = true;
|
||||
items_shown_count++;
|
||||
} else {
|
||||
this->string_filter.ResetState();
|
||||
this->string_filter.AddLine(this->fios_items[i].title);
|
||||
/* We set the vector to show this fios element as filtered depending on the result of the filter */
|
||||
this->fios_items_shown[i] = this->string_filter.GetState();
|
||||
if (this->fios_items_shown[i]) items_shown_count++;
|
||||
|
||||
if (&(this->fios_items[i]) == this->selected && this->fios_items_shown[i] == false) {
|
||||
/* The selected element has been filtered out */
|
||||
this->selected = NULL;
|
||||
this->OnInvalidateData(SLIWD_SELECTION_CHANGES);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->vscroll->SetCount(items_shown_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnEditboxChanged(int wid)
|
||||
{
|
||||
if (wid == WID_SL_FILTER) {
|
||||
this->string_filter.SetFilterTerm(this->filter_editbox.text.buf);
|
||||
this->InvalidateData(SLIWD_FILTER_CHANGES);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -86,6 +86,7 @@ public:
|
||||
virtual GlyphID MapCharToGlyph(WChar key) { assert(IsPrintable(key)); return SPRITE_GLYPH | key; }
|
||||
virtual const void *GetFontTable(uint32 tag, size_t &length) { length = 0; return NULL; }
|
||||
virtual const char *GetFontName() { return "sprite"; }
|
||||
virtual bool IsBuiltInFont() { return true; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -208,6 +209,8 @@ bool SpriteFontCache::GetDrawGlyphShadow()
|
||||
class FreeTypeFontCache : public FontCache {
|
||||
private:
|
||||
FT_Face face; ///< The font face associated with this font.
|
||||
int req_size; ///< Requested font size.
|
||||
int used_size; ///< Used font size.
|
||||
|
||||
typedef SmallMap<uint32, SmallPair<size_t, const void*> > FontTable; ///< Table with font table cache
|
||||
FontTable font_tables; ///< Cached font tables.
|
||||
@@ -236,10 +239,12 @@ private:
|
||||
|
||||
GlyphEntry *GetGlyphPtr(GlyphID key);
|
||||
void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false);
|
||||
void SetFontSize(FontSize fs, FT_Face face, int pixels);
|
||||
|
||||
public:
|
||||
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
|
||||
~FreeTypeFontCache();
|
||||
virtual int GetFontSize() const { return this->used_size; }
|
||||
virtual SpriteID GetUnicodeGlyph(WChar key) { return this->parent->GetUnicodeGlyph(key); }
|
||||
virtual void SetUnicodeGlyph(WChar key, SpriteID sprite) { this->parent->SetUnicodeGlyph(key, sprite); }
|
||||
virtual void InitializeUnicodeGlyphMap() { this->parent->InitializeUnicodeGlyphMap(); }
|
||||
@@ -250,6 +255,7 @@ public:
|
||||
virtual GlyphID MapCharToGlyph(WChar key);
|
||||
virtual const void *GetFontTable(uint32 tag, size_t &length);
|
||||
virtual const char *GetFontName() { return face->family_name; }
|
||||
virtual bool IsBuiltInFont() { return false; }
|
||||
};
|
||||
|
||||
FT_Library _library = NULL;
|
||||
@@ -265,22 +271,29 @@ static const byte SHADOW_COLOUR = 2;
|
||||
* @param face The font that has to be loaded.
|
||||
* @param pixels The number of pixels this font should be high.
|
||||
*/
|
||||
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), glyph_to_sprite(NULL)
|
||||
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), req_size(pixels), glyph_to_sprite(NULL)
|
||||
{
|
||||
assert(face != NULL);
|
||||
|
||||
this->SetFontSize(fs, face, pixels);
|
||||
}
|
||||
|
||||
void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the minimal height recommended by the font. */
|
||||
pixels = _default_font_height[this->fs];
|
||||
int scaled_height = ScaleGUITrad(_default_font_height[this->fs]);
|
||||
pixels = scaled_height;
|
||||
|
||||
TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
|
||||
if (head != NULL) {
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = _default_font_height[this->fs] - _default_font_height[FS_SMALL];
|
||||
pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[this->fs], MAX_FONT_SIZE);
|
||||
int diff = scaled_height - ScaleGUITrad(_default_font_height[FS_SMALL]);
|
||||
pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
}
|
||||
}
|
||||
this->used_size = pixels;
|
||||
|
||||
FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
|
||||
if (err != FT_Err_Ok) {
|
||||
@@ -393,6 +406,7 @@ found_face:
|
||||
FreeTypeFontCache::~FreeTypeFontCache()
|
||||
{
|
||||
FT_Done_Face(this->face);
|
||||
this->face = NULL;
|
||||
this->ClearFontCache();
|
||||
|
||||
for (FontTable::iterator iter = this->font_tables.Begin(); iter != this->font_tables.End(); iter++) {
|
||||
@@ -422,6 +436,9 @@ void FreeTypeFontCache::ClearFontCache()
|
||||
this->glyph_to_sprite = NULL;
|
||||
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
|
||||
/* GUI scaling might have changed, determine font size anew if it was automatically selected. */
|
||||
if (this->face != NULL && this->req_size == 0) this->SetFontSize(this->fs, this->face, this->req_size);
|
||||
}
|
||||
|
||||
FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(GlyphID key)
|
||||
|
@@ -64,6 +64,12 @@ public:
|
||||
*/
|
||||
inline int GetUnitsPerEM() const { return this->units_per_em; }
|
||||
|
||||
/**
|
||||
* Get the nominal font size of the font.
|
||||
* @return The nominal font size.
|
||||
*/
|
||||
virtual int GetFontSize() const { return this->height; }
|
||||
|
||||
/**
|
||||
* Get the SpriteID mapped to the given key
|
||||
* @param key The key to get the sprite for.
|
||||
@@ -143,6 +149,11 @@ public:
|
||||
{
|
||||
return this->parent != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a built-in sprite font?
|
||||
*/
|
||||
virtual bool IsBuiltInFont() = 0;
|
||||
};
|
||||
|
||||
/** Get the SpriteID mapped to the given font size and key */
|
||||
|
@@ -28,7 +28,7 @@ extern FT_Library _library;
|
||||
* Windows support
|
||||
* ======================================================================================== */
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include "core/alloc_func.hpp"
|
||||
#include "core/math_func.hpp"
|
||||
#include <windows.h>
|
||||
|
859
src/framerate_gui.cpp
Normal file
859
src/framerate_gui.cpp
Normal file
@@ -0,0 +1,859 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file framerate_gui.cpp GUI for displaying framerate/game speed information. */
|
||||
|
||||
#include "framerate_type.h"
|
||||
#include <chrono>
|
||||
#include "gfx_func.h"
|
||||
#include "window_gui.h"
|
||||
#include "table/sprites.h"
|
||||
#include "strings_func.h"
|
||||
#include "debug.h"
|
||||
#include "console_func.h"
|
||||
#include "console_type.h"
|
||||
|
||||
#include "widgets/framerate_widget.h"
|
||||
|
||||
|
||||
/**
|
||||
* Private declarations for performance measurement implementation
|
||||
*/
|
||||
namespace {
|
||||
|
||||
/** Number of data points to keep in buffer for each performance measurement */
|
||||
const int NUM_FRAMERATE_POINTS = 512;
|
||||
/** %Units a second is divided into in performance measurements */
|
||||
const TimingMeasurement TIMESTAMP_PRECISION = 1000000;
|
||||
|
||||
struct PerformanceData {
|
||||
/** Duration value indicating the value is not valid should be considered a gap in measurements */
|
||||
static const TimingMeasurement INVALID_DURATION = UINT64_MAX;
|
||||
|
||||
/** Time spent processing each cycle of the performance element, circular buffer */
|
||||
TimingMeasurement durations[NUM_FRAMERATE_POINTS];
|
||||
/** Start time of each cycle of the performance element, circular buffer */
|
||||
TimingMeasurement timestamps[NUM_FRAMERATE_POINTS];
|
||||
/** Expected number of cycles per second when the system is running without slowdowns */
|
||||
double expected_rate;
|
||||
/** Next index to write to in \c durations and \c timestamps */
|
||||
int next_index;
|
||||
/** Last index written to in \c durations and \c timestamps */
|
||||
int prev_index;
|
||||
/** Number of data points recorded, clamped to \c NUM_FRAMERATE_POINTS */
|
||||
int num_valid;
|
||||
|
||||
/** Current accumulated duration */
|
||||
TimingMeasurement acc_duration;
|
||||
/** Start time for current accumulation cycle */
|
||||
TimingMeasurement acc_timestamp;
|
||||
|
||||
/**
|
||||
* Initialize a data element with an expected collection rate
|
||||
* @param expected_rate
|
||||
* Expected number of cycles per second of the performance element. Use 1 if unknown or not relevant.
|
||||
* The rate is used for highlighting slow-running elements in the GUI.
|
||||
*/
|
||||
explicit PerformanceData(double expected_rate) : expected_rate(expected_rate), next_index(0), prev_index(0), num_valid(0) { }
|
||||
|
||||
/** Collect a complete measurement, given start and ending times for a processing block */
|
||||
void Add(TimingMeasurement start_time, TimingMeasurement end_time)
|
||||
{
|
||||
this->durations[this->next_index] = end_time - start_time;
|
||||
this->timestamps[this->next_index] = start_time;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
|
||||
}
|
||||
|
||||
/** Begin an accumulation of multiple measurements into a single value, from a given start time */
|
||||
void BeginAccumulate(TimingMeasurement start_time)
|
||||
{
|
||||
this->timestamps[this->next_index] = this->acc_timestamp;
|
||||
this->durations[this->next_index] = this->acc_duration;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid = min(NUM_FRAMERATE_POINTS, this->num_valid + 1);
|
||||
|
||||
this->acc_duration = 0;
|
||||
this->acc_timestamp = start_time;
|
||||
}
|
||||
|
||||
/** Accumulate a period onto the current measurement */
|
||||
void AddAccumulate(TimingMeasurement duration)
|
||||
{
|
||||
this->acc_duration += duration;
|
||||
}
|
||||
|
||||
/** Indicate a pause/expected discontinuity in processing the element */
|
||||
void AddPause(TimingMeasurement start_time)
|
||||
{
|
||||
if (this->durations[this->prev_index] != INVALID_DURATION) {
|
||||
this->timestamps[this->next_index] = start_time;
|
||||
this->durations[this->next_index] = INVALID_DURATION;
|
||||
this->prev_index = this->next_index;
|
||||
this->next_index += 1;
|
||||
if (this->next_index >= NUM_FRAMERATE_POINTS) this->next_index = 0;
|
||||
this->num_valid += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get average cycle processing time over a number of data points */
|
||||
double GetAverageDurationMilliseconds(int count)
|
||||
{
|
||||
count = min(count, this->num_valid);
|
||||
|
||||
int first_point = this->prev_index - count;
|
||||
if (first_point < 0) first_point += NUM_FRAMERATE_POINTS;
|
||||
|
||||
/* Sum durations, skipping invalid points */
|
||||
double sumtime = 0;
|
||||
for (int i = first_point; i < first_point + count; i++) {
|
||||
auto d = this->durations[i % NUM_FRAMERATE_POINTS];
|
||||
if (d != INVALID_DURATION) {
|
||||
sumtime += d;
|
||||
} else {
|
||||
/* Don't count the invalid durations */
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) return 0; // avoid div by zero
|
||||
return sumtime * 1000 / count / TIMESTAMP_PRECISION;
|
||||
}
|
||||
|
||||
/** Get current rate of a performance element, based on approximately the past one second of data */
|
||||
double GetRate()
|
||||
{
|
||||
/* Start at last recorded point, end at latest when reaching the earliest recorded point */
|
||||
int point = this->prev_index;
|
||||
int last_point = this->next_index - this->num_valid;
|
||||
if (last_point < 0) last_point += NUM_FRAMERATE_POINTS;
|
||||
|
||||
/* Number of data points collected */
|
||||
int count = 0;
|
||||
/* Time of previous data point */
|
||||
TimingMeasurement last = this->timestamps[point];
|
||||
/* Total duration covered by collected points */
|
||||
TimingMeasurement total = 0;
|
||||
|
||||
while (point != last_point) {
|
||||
/* Only record valid data points, but pretend the gaps in measurements aren't there */
|
||||
if (this->durations[point] != INVALID_DURATION) {
|
||||
total += last - this->timestamps[point];
|
||||
count++;
|
||||
}
|
||||
last = this->timestamps[point];
|
||||
if (total >= TIMESTAMP_PRECISION) break; // end after 1 second has been collected
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
}
|
||||
|
||||
if (total == 0 || count == 0) return 0;
|
||||
return (double)count * TIMESTAMP_PRECISION / total;
|
||||
}
|
||||
};
|
||||
|
||||
/** %Game loop rate, cycles per second */
|
||||
static const double GL_RATE = 1000.0 / MILLISECONDS_PER_TICK;
|
||||
|
||||
/**
|
||||
* Storage for all performance element measurements.
|
||||
* Elements are initialized with the expected rate in recorded values per second.
|
||||
* @hideinitializer
|
||||
*/
|
||||
PerformanceData _pf_data[PFE_MAX] = {
|
||||
PerformanceData(GL_RATE), // PFE_GAMELOOP
|
||||
PerformanceData(1), // PFE_ACC_GL_ECONOMY
|
||||
PerformanceData(1), // PFE_ACC_GL_TRAINS
|
||||
PerformanceData(1), // PFE_ACC_GL_ROADVEHS
|
||||
PerformanceData(1), // PFE_ACC_GL_SHIPS
|
||||
PerformanceData(1), // PFE_ACC_GL_AIRCRAFT
|
||||
PerformanceData(1), // PFE_GL_LANDSCAPE
|
||||
PerformanceData(1), // PFE_GL_LINKGRAPH
|
||||
PerformanceData(GL_RATE), // PFE_DRAWING
|
||||
PerformanceData(1), // PFE_ACC_DRAWWORLD
|
||||
PerformanceData(60.0), // PFE_VIDEO
|
||||
PerformanceData(1000.0 * 8192 / 44100), // PFE_SOUND
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a timestamp with \c TIMESTAMP_PRECISION ticks per second precision.
|
||||
* The basis of the timestamp is implementation defined, but the value should be steady,
|
||||
* so differences can be taken to reliably measure intervals.
|
||||
*/
|
||||
static TimingMeasurement GetPerformanceTimer()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return (TimingMeasurement)time_point_cast<microseconds>(high_resolution_clock::now()).time_since_epoch().count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin a cycle of a measured element.
|
||||
* @param elem The element to be measured
|
||||
*/
|
||||
PerformanceMeasurer::PerformanceMeasurer(PerformanceElement elem)
|
||||
{
|
||||
assert(elem < PFE_MAX);
|
||||
|
||||
this->elem = elem;
|
||||
this->start_time = GetPerformanceTimer();
|
||||
}
|
||||
|
||||
/** Finish a cycle of a measured element and store the measurement taken. */
|
||||
PerformanceMeasurer::~PerformanceMeasurer()
|
||||
{
|
||||
_pf_data[this->elem].Add(this->start_time, GetPerformanceTimer());
|
||||
}
|
||||
|
||||
/** Set the rate of expected cycles per second of a performance element. */
|
||||
void PerformanceMeasurer::SetExpectedRate(double rate)
|
||||
{
|
||||
_pf_data[this->elem].expected_rate = rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that a cycle of "pause" where no processing occurs.
|
||||
* @param elem The element not currently being processed
|
||||
*/
|
||||
void PerformanceMeasurer::Paused(PerformanceElement elem)
|
||||
{
|
||||
_pf_data[elem].AddPause(GetPerformanceTimer());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Begin measuring one block of the accumulating value.
|
||||
* @param elem The element to be measured
|
||||
*/
|
||||
PerformanceAccumulator::PerformanceAccumulator(PerformanceElement elem)
|
||||
{
|
||||
assert(elem < PFE_MAX);
|
||||
|
||||
this->elem = elem;
|
||||
this->start_time = GetPerformanceTimer();
|
||||
}
|
||||
|
||||
/** Finish and add one block of the accumulating value. */
|
||||
PerformanceAccumulator::~PerformanceAccumulator()
|
||||
{
|
||||
_pf_data[this->elem].AddAccumulate(GetPerformanceTimer() - this->start_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the previous accumulator value and reset for a new cycle of accumulating measurements.
|
||||
* @note This function must be called once per frame, otherwise measurements are not collected.
|
||||
* @param elem The element to begin a new measurement cycle of
|
||||
*/
|
||||
void PerformanceAccumulator::Reset(PerformanceElement elem)
|
||||
{
|
||||
_pf_data[elem].BeginAccumulate(GetPerformanceTimer());
|
||||
}
|
||||
|
||||
|
||||
void ShowFrametimeGraphWindow(PerformanceElement elem);
|
||||
|
||||
|
||||
/** @hideinitializer */
|
||||
static const NWidgetPart _framerate_window_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_FRW_CAPTION), SetDataTip(STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_GAMELOOP), SetDataTip(STR_FRAMERATE_RATE_GAMELOOP, STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_DRAWING), SetDataTip(STR_FRAMERATE_RATE_BLITTER, STR_FRAMERATE_RATE_BLITTER_TOOLTIP),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_RATE_FACTOR), SetDataTip(STR_FRAMERATE_SPEED_FACTOR, STR_FRAMERATE_SPEED_FACTOR_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6), SetPIP(0, 3, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, 6, 0),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_NAMES),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_CURRENT),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FRW_TIMES_AVERAGE),
|
||||
EndContainer(),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY, WID_FRW_INFO_DATA_POINTS), SetDataTip(STR_FRAMERATE_DATA_POINTS, 0x0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
struct FramerateWindow : Window {
|
||||
bool small;
|
||||
uint32 next_update;
|
||||
|
||||
struct CachedDecimal {
|
||||
StringID strid;
|
||||
uint32 value;
|
||||
|
||||
inline void SetRate(double value, double target)
|
||||
{
|
||||
const double threshold_good = target * 0.95;
|
||||
const double threshold_bad = target * 2 / 3;
|
||||
value = min(9999.99, value);
|
||||
this->value = (uint32)(value * 100);
|
||||
this->strid = (value > threshold_good) ? STR_FRAMERATE_FPS_GOOD : (value < threshold_bad) ? STR_FRAMERATE_FPS_BAD : STR_FRAMERATE_FPS_WARN;
|
||||
}
|
||||
|
||||
inline void SetTime(double value, double target)
|
||||
{
|
||||
const double threshold_good = target / 3;
|
||||
const double threshold_bad = target;
|
||||
value = min(9999.99, value);
|
||||
this->value = (uint32)(value * 100);
|
||||
this->strid = (value < threshold_good) ? STR_FRAMERATE_MS_GOOD : (value > threshold_bad) ? STR_FRAMERATE_MS_BAD : STR_FRAMERATE_MS_WARN;
|
||||
}
|
||||
|
||||
inline void InsertDParams(uint n) const
|
||||
{
|
||||
SetDParam(n, this->value);
|
||||
SetDParam(n + 1, 2);
|
||||
}
|
||||
};
|
||||
|
||||
CachedDecimal rate_gameloop; ///< cached game loop tick rate
|
||||
CachedDecimal rate_drawing; ///< cached drawing frame rate
|
||||
CachedDecimal speed_gameloop; ///< cached game loop speed factor
|
||||
CachedDecimal times_shortterm[PFE_MAX]; ///< cached short term average times
|
||||
CachedDecimal times_longterm[PFE_MAX]; ///< cached long term average times
|
||||
|
||||
static const int VSPACING = 3; ///< space between column heading and values
|
||||
|
||||
FramerateWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
||||
{
|
||||
this->InitNested(number);
|
||||
this->small = this->IsShaded();
|
||||
this->UpdateData();
|
||||
}
|
||||
|
||||
virtual void OnTick()
|
||||
{
|
||||
/* Check if the shaded state has changed, switch caption text if it has */
|
||||
if (this->small != this->IsShaded()) {
|
||||
this->small = this->IsShaded();
|
||||
this->GetWidget<NWidgetLeaf>(WID_FRW_CAPTION)->SetDataTip(this->small ? STR_FRAMERATE_CAPTION_SMALL : STR_FRAMERATE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
|
||||
this->next_update = 0;
|
||||
}
|
||||
|
||||
if (_realtime_tick >= this->next_update) {
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
this->next_update = _realtime_tick + 100;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateData()
|
||||
{
|
||||
double gl_rate = _pf_data[PFE_GAMELOOP].GetRate();
|
||||
this->rate_gameloop.SetRate(gl_rate, _pf_data[PFE_GAMELOOP].expected_rate);
|
||||
this->speed_gameloop.SetRate(gl_rate / _pf_data[PFE_GAMELOOP].expected_rate, 1.0);
|
||||
if (this->small) return; // in small mode, this is everything needed
|
||||
|
||||
this->rate_drawing.SetRate(_pf_data[PFE_DRAWING].GetRate(), _pf_data[PFE_DRAWING].expected_rate);
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
this->times_shortterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(8), MILLISECONDS_PER_TICK);
|
||||
this->times_longterm[e].SetTime(_pf_data[e].GetAverageDurationMilliseconds(NUM_FRAMERATE_POINTS), MILLISECONDS_PER_TICK);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_CAPTION:
|
||||
/* When the window is shaded, the caption shows game loop rate and speed factor */
|
||||
if (!this->small) break;
|
||||
SetDParam(0, this->rate_gameloop.strid);
|
||||
this->rate_gameloop.InsertDParams(1);
|
||||
this->speed_gameloop.InsertDParams(3);
|
||||
break;
|
||||
|
||||
case WID_FRW_RATE_GAMELOOP:
|
||||
SetDParam(0, this->rate_gameloop.strid);
|
||||
this->rate_gameloop.InsertDParams(1);
|
||||
break;
|
||||
case WID_FRW_RATE_DRAWING:
|
||||
SetDParam(0, this->rate_drawing.strid);
|
||||
this->rate_drawing.InsertDParams(1);
|
||||
break;
|
||||
case WID_FRW_RATE_FACTOR:
|
||||
this->speed_gameloop.InsertDParams(0);
|
||||
break;
|
||||
case WID_FRW_INFO_DATA_POINTS:
|
||||
SetDParam(0, NUM_FRAMERATE_POINTS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_RATE_GAMELOOP:
|
||||
SetDParam(0, STR_FRAMERATE_FPS_GOOD);
|
||||
SetDParam(1, 999999);
|
||||
SetDParam(2, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_RATE_GAMELOOP);
|
||||
break;
|
||||
case WID_FRW_RATE_DRAWING:
|
||||
SetDParam(0, STR_FRAMERATE_FPS_GOOD);
|
||||
SetDParam(1, 999999);
|
||||
SetDParam(2, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_RATE_BLITTER);
|
||||
break;
|
||||
case WID_FRW_RATE_FACTOR:
|
||||
SetDParam(0, 999999);
|
||||
SetDParam(1, 2);
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_SPEED_FACTOR);
|
||||
break;
|
||||
|
||||
case WID_FRW_TIMES_NAMES: {
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
size->width = 0;
|
||||
size->height = FONT_HEIGHT_NORMAL * (linecount + 1) + VSPACING;
|
||||
for (int line = 0; line < linecount; line++) {
|
||||
Dimension line_size = GetStringBoundingBox(STR_FRAMERATE_GAMELOOP + line);
|
||||
size->width = max(size->width, line_size.width);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
case WID_FRW_TIMES_AVERAGE: {
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
*size = GetStringBoundingBox(STR_FRAMERATE_CURRENT + (widget - WID_FRW_TIMES_CURRENT));
|
||||
SetDParam(0, 999999);
|
||||
SetDParam(1, 2);
|
||||
Dimension item_size = GetStringBoundingBox(STR_FRAMERATE_MS_GOOD);
|
||||
size->width = max(size->width, item_size.width);
|
||||
size->height += FONT_HEIGHT_NORMAL * linecount + VSPACING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Render a column of formatted average durations */
|
||||
void DrawElementTimesColumn(const Rect &r, StringID heading_str, const CachedDecimal *values) const
|
||||
{
|
||||
int y = r.top;
|
||||
DrawString(r.left, r.right, y, heading_str, TC_FROMSTRING, SA_CENTER, true);
|
||||
y += FONT_HEIGHT_NORMAL + VSPACING;
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
values[e].InsertDParams(0);
|
||||
DrawString(r.left, r.right, y, values[e].strid, TC_FROMSTRING, SA_RIGHT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_TIMES_NAMES: {
|
||||
/* Render a column of titles for performance element names */
|
||||
int linecount = PFE_MAX - PFE_FIRST;
|
||||
int y = r.top + FONT_HEIGHT_NORMAL + VSPACING; // first line contains headings in the value columns
|
||||
for (int i = 0; i < linecount; i++) {
|
||||
DrawString(r.left, r.right, y, STR_FRAMERATE_GAMELOOP + i, TC_FROMSTRING, SA_LEFT);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
/* Render short-term average values */
|
||||
DrawElementTimesColumn(r, STR_FRAMERATE_CURRENT, this->times_shortterm);
|
||||
break;
|
||||
case WID_FRW_TIMES_AVERAGE:
|
||||
/* Render averages of all recorded values */
|
||||
DrawElementTimesColumn(r, STR_FRAMERATE_AVERAGE, this->times_longterm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FRW_TIMES_NAMES:
|
||||
case WID_FRW_TIMES_CURRENT:
|
||||
case WID_FRW_TIMES_AVERAGE: {
|
||||
/* Open time graph windows when clicking detail measurement lines */
|
||||
int line = this->GetRowFromWidget(pt.y, widget, VSPACING, FONT_HEIGHT_NORMAL);
|
||||
if (line > 0) {
|
||||
line -= 1;
|
||||
ShowFrametimeGraphWindow((PerformanceElement)line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static WindowDesc _framerate_display_desc(
|
||||
WDP_AUTO, "framerate_display", 60, 40,
|
||||
WC_FRAMERATE_DISPLAY, WC_NONE,
|
||||
0,
|
||||
_framerate_window_widgets, lengthof(_framerate_window_widgets)
|
||||
);
|
||||
|
||||
|
||||
/** @hideinitializer */
|
||||
static const NWidgetPart _frametime_graph_window_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_FGW_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY),
|
||||
NWidget(NWID_VERTICAL), SetPadding(6),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_FGW_GRAPH),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
struct FrametimeGraphWindow : Window {
|
||||
int vertical_scale; ///< number of TIMESTAMP_PRECISION units vertically
|
||||
int horizontal_scale; ///< number of half-second units horizontally
|
||||
uint32 next_scale_update; ///< realtime tick for next scale update
|
||||
|
||||
PerformanceElement element; ///< what element this window renders graph for
|
||||
Dimension graph_size; ///< size of the main graph area (excluding axis labels)
|
||||
|
||||
FrametimeGraphWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
||||
{
|
||||
this->element = (PerformanceElement)number;
|
||||
this->horizontal_scale = 4;
|
||||
this->vertical_scale = TIMESTAMP_PRECISION / 10;
|
||||
this->next_scale_update = 0;
|
||||
|
||||
this->InitNested(number);
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_FGW_CAPTION:
|
||||
SetDParam(0, STR_FRAMETIME_CAPTION_GAMELOOP + this->element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
if (widget == WID_FGW_GRAPH) {
|
||||
SetDParam(0, 100);
|
||||
Dimension size_ms_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_MILLISECONDS);
|
||||
SetDParam(0, 100);
|
||||
Dimension size_s_label = GetStringBoundingBox(STR_FRAMERATE_GRAPH_SECONDS);
|
||||
|
||||
/* Size graph in height to fit at least 10 vertical labels with space between, or at least 100 pixels */
|
||||
graph_size.height = max<uint>(100, 10 * (size_ms_label.height + 1));
|
||||
/* Always 2:1 graph area */
|
||||
graph_size.width = 2 * graph_size.height;
|
||||
*size = graph_size;
|
||||
|
||||
size->width += size_ms_label.width + 2;
|
||||
size->height += size_s_label.height + 2;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectHorizontalScale(TimingMeasurement range)
|
||||
{
|
||||
/* Determine horizontal scale based on period covered by 60 points
|
||||
* (slightly less than 2 seconds at full game speed) */
|
||||
struct ScaleDef { TimingMeasurement range; int scale; };
|
||||
static const ScaleDef hscales[] = {
|
||||
{ 120, 60 },
|
||||
{ 10, 20 },
|
||||
{ 5, 10 },
|
||||
{ 3, 4 },
|
||||
{ 1, 2 },
|
||||
};
|
||||
for (const ScaleDef *sc = hscales; sc < hscales + lengthof(hscales); sc++) {
|
||||
if (range < sc->range) this->horizontal_scale = sc->scale;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectVerticalScale(TimingMeasurement range)
|
||||
{
|
||||
/* Determine vertical scale based on peak value (within the horizontal scale + a bit) */
|
||||
static const TimingMeasurement vscales[] = {
|
||||
TIMESTAMP_PRECISION * 100,
|
||||
TIMESTAMP_PRECISION * 10,
|
||||
TIMESTAMP_PRECISION * 5,
|
||||
TIMESTAMP_PRECISION,
|
||||
TIMESTAMP_PRECISION / 2,
|
||||
TIMESTAMP_PRECISION / 5,
|
||||
TIMESTAMP_PRECISION / 10,
|
||||
TIMESTAMP_PRECISION / 50,
|
||||
TIMESTAMP_PRECISION / 200,
|
||||
};
|
||||
for (const TimingMeasurement *sc = vscales; sc < vscales + lengthof(vscales); sc++) {
|
||||
if (range < *sc) this->vertical_scale = (int)*sc;
|
||||
}
|
||||
}
|
||||
|
||||
/** Recalculate the graph scaling factors based on current recorded data */
|
||||
void UpdateScale()
|
||||
{
|
||||
const TimingMeasurement *durations = _pf_data[this->element].durations;
|
||||
const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
|
||||
int num_valid = _pf_data[this->element].num_valid;
|
||||
int point = _pf_data[this->element].prev_index;
|
||||
|
||||
TimingMeasurement lastts = timestamps[point];
|
||||
TimingMeasurement time_sum = 0;
|
||||
TimingMeasurement peak_value = 0;
|
||||
int count = 0;
|
||||
|
||||
/* Sensible default for when too few measurements are available */
|
||||
this->horizontal_scale = 4;
|
||||
|
||||
for (int i = 1; i < num_valid; i++) {
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
|
||||
TimingMeasurement value = durations[point];
|
||||
if (value == PerformanceData::INVALID_DURATION) {
|
||||
/* Skip gaps in data by pretending time is continuous across them */
|
||||
lastts = timestamps[point];
|
||||
continue;
|
||||
}
|
||||
if (value > peak_value) peak_value = value;
|
||||
count++;
|
||||
|
||||
/* Accumulate period of time covered by data */
|
||||
time_sum += lastts - timestamps[point];
|
||||
lastts = timestamps[point];
|
||||
|
||||
/* Enough data to select a range and get decent data density */
|
||||
if (count == 60) this->SelectHorizontalScale(time_sum / TIMESTAMP_PRECISION);
|
||||
|
||||
/* End when enough points have been collected and the horizontal scale has been exceeded */
|
||||
if (count >= 60 && time_sum >= (this->horizontal_scale + 2) * TIMESTAMP_PRECISION / 2) break;
|
||||
}
|
||||
|
||||
this->SelectVerticalScale(peak_value);
|
||||
}
|
||||
|
||||
virtual void OnTick()
|
||||
{
|
||||
this->SetDirty();
|
||||
|
||||
if (this->next_scale_update < _realtime_tick) {
|
||||
this->next_scale_update = _realtime_tick + 500;
|
||||
this->UpdateScale();
|
||||
}
|
||||
}
|
||||
|
||||
/** Scale and interpolate a value from a source range into a destination range */
|
||||
template<typename T>
|
||||
static inline T Scinterlate(T dst_min, T dst_max, T src_min, T src_max, T value)
|
||||
{
|
||||
T dst_diff = dst_max - dst_min;
|
||||
T src_diff = src_max - src_min;
|
||||
return (value - src_min) * dst_diff / src_diff + dst_min;
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
if (widget == WID_FGW_GRAPH) {
|
||||
const TimingMeasurement *durations = _pf_data[this->element].durations;
|
||||
const TimingMeasurement *timestamps = _pf_data[this->element].timestamps;
|
||||
int point = _pf_data[this->element].prev_index;
|
||||
|
||||
const int x_zero = r.right - (int)this->graph_size.width;
|
||||
const int x_max = r.right;
|
||||
const int y_zero = r.top + (int)this->graph_size.height;
|
||||
const int y_max = r.top;
|
||||
const int c_grid = PC_DARK_GREY;
|
||||
const int c_lines = PC_BLACK;
|
||||
const int c_peak = PC_DARK_RED;
|
||||
|
||||
const TimingMeasurement draw_horz_scale = (TimingMeasurement)this->horizontal_scale * TIMESTAMP_PRECISION / 2;
|
||||
const TimingMeasurement draw_vert_scale = (TimingMeasurement)this->vertical_scale;
|
||||
|
||||
/* Number of \c horizontal_scale units in each horizontal division */
|
||||
const uint horz_div_scl = (this->horizontal_scale <= 20) ? 1 : 10;
|
||||
/* Number of divisions of the horizontal axis */
|
||||
const uint horz_divisions = this->horizontal_scale / horz_div_scl;
|
||||
/* Number of divisions of the vertical axis */
|
||||
const uint vert_divisions = 10;
|
||||
|
||||
/* Draw division lines and labels for the vertical axis */
|
||||
for (uint division = 0; division < vert_divisions; division++) {
|
||||
int y = Scinterlate(y_zero, y_max, 0, (int)vert_divisions, (int)division);
|
||||
GfxDrawLine(x_zero, y, x_max, y, c_grid);
|
||||
if (division % 2 == 0) {
|
||||
if ((TimingMeasurement)this->vertical_scale > TIMESTAMP_PRECISION) {
|
||||
SetDParam(0, this->vertical_scale * division / 10 / TIMESTAMP_PRECISION);
|
||||
DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
} else {
|
||||
SetDParam(0, this->vertical_scale * division / 10 * 1000 / TIMESTAMP_PRECISION);
|
||||
DrawString(r.left, x_zero - 2, y - FONT_HEIGHT_SMALL, STR_FRAMERATE_GRAPH_MILLISECONDS, TC_GREY, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Draw divison lines and labels for the horizontal axis */
|
||||
for (uint division = horz_divisions; division > 0; division--) {
|
||||
int x = Scinterlate(x_zero, x_max, 0, (int)horz_divisions, (int)horz_divisions - (int)division);
|
||||
GfxDrawLine(x, y_max, x, y_zero, c_grid);
|
||||
if (division % 2 == 0) {
|
||||
SetDParam(0, division * horz_div_scl / 2);
|
||||
DrawString(x, x_max, y_zero + 2, STR_FRAMERATE_GRAPH_SECONDS, TC_GREY, SA_LEFT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Position of last rendered data point */
|
||||
Point lastpoint = {
|
||||
x_max,
|
||||
(int)Scinterlate<int64>(y_zero, y_max, 0, this->vertical_scale, durations[point])
|
||||
};
|
||||
/* Timestamp of last rendered data point */
|
||||
TimingMeasurement lastts = timestamps[point];
|
||||
|
||||
TimingMeasurement peak_value = 0;
|
||||
Point peak_point = { 0, 0 };
|
||||
TimingMeasurement value_sum = 0;
|
||||
TimingMeasurement time_sum = 0;
|
||||
int points_drawn = 0;
|
||||
|
||||
for (int i = 1; i < NUM_FRAMERATE_POINTS; i++) {
|
||||
point--;
|
||||
if (point < 0) point = NUM_FRAMERATE_POINTS - 1;
|
||||
|
||||
TimingMeasurement value = durations[point];
|
||||
if (value == PerformanceData::INVALID_DURATION) {
|
||||
/* Skip gaps in measurements, pretend the data points on each side are continuous */
|
||||
lastts = timestamps[point];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Use total time period covered for value along horizontal axis */
|
||||
time_sum += lastts - timestamps[point];
|
||||
lastts = timestamps[point];
|
||||
/* Stop if past the width of the graph */
|
||||
if (time_sum > draw_horz_scale) break;
|
||||
|
||||
/* Draw line from previous point to new point */
|
||||
Point newpoint = {
|
||||
(int)Scinterlate<int64>(x_zero, x_max, 0, (int64)draw_horz_scale, (int64)draw_horz_scale - (int64)time_sum),
|
||||
(int)Scinterlate<int64>(y_zero, y_max, 0, (int64)draw_vert_scale, (int64)value)
|
||||
};
|
||||
assert(newpoint.x <= lastpoint.x);
|
||||
GfxDrawLine(lastpoint.x, lastpoint.y, newpoint.x, newpoint.y, c_lines);
|
||||
lastpoint = newpoint;
|
||||
|
||||
/* Record peak and average value across graphed data */
|
||||
value_sum += value;
|
||||
points_drawn++;
|
||||
if (value > peak_value) {
|
||||
peak_value = value;
|
||||
peak_point = newpoint;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the peak value is significantly larger than the average, mark and label it */
|
||||
if (points_drawn > 0 && peak_value > TIMESTAMP_PRECISION / 100 && 2 * peak_value > 3 * value_sum / points_drawn) {
|
||||
TextColour tc_peak = (TextColour)(TC_IS_PALETTE_COLOUR | c_peak);
|
||||
GfxFillRect(peak_point.x - 1, peak_point.y - 1, peak_point.x + 1, peak_point.y + 1, c_peak);
|
||||
SetDParam(0, peak_value * 1000 / TIMESTAMP_PRECISION);
|
||||
int label_y = max(y_max, peak_point.y - FONT_HEIGHT_SMALL);
|
||||
if (peak_point.x - x_zero > (int)this->graph_size.width / 2) {
|
||||
DrawString(x_zero, peak_point.x - 2, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_RIGHT | SA_FORCE, false, FS_SMALL);
|
||||
} else {
|
||||
DrawString(peak_point.x + 2, x_max, label_y, STR_FRAMERATE_GRAPH_MILLISECONDS, tc_peak, SA_LEFT | SA_FORCE, false, FS_SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static WindowDesc _frametime_graph_window_desc(
|
||||
WDP_AUTO, "frametime_graph", 140, 90,
|
||||
WC_FRAMETIME_GRAPH, WC_NONE,
|
||||
0,
|
||||
_frametime_graph_window_widgets, lengthof(_frametime_graph_window_widgets)
|
||||
);
|
||||
|
||||
|
||||
|
||||
/** Open the general framerate window */
|
||||
void ShowFramerateWindow()
|
||||
{
|
||||
AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
|
||||
}
|
||||
|
||||
/** Open a graph window for a performance element */
|
||||
void ShowFrametimeGraphWindow(PerformanceElement elem)
|
||||
{
|
||||
if (elem < PFE_FIRST || elem >= PFE_MAX) return; // maybe warn?
|
||||
AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem, true);
|
||||
}
|
||||
|
||||
/** Print performance statistics to game console */
|
||||
void ConPrintFramerate()
|
||||
{
|
||||
const int count1 = NUM_FRAMERATE_POINTS / 8;
|
||||
const int count2 = NUM_FRAMERATE_POINTS / 4;
|
||||
const int count3 = NUM_FRAMERATE_POINTS / 1;
|
||||
|
||||
IConsolePrintF(TC_SILVER, "Based on num. data points: %d %d %d", count1, count2, count3);
|
||||
|
||||
static const char *MEASUREMENT_NAMES[PFE_MAX] = {
|
||||
"Game loop",
|
||||
" GL station ticks",
|
||||
" GL train ticks",
|
||||
" GL road vehicle ticks",
|
||||
" GL ship ticks",
|
||||
" GL aircraft ticks",
|
||||
" GL landscape ticks",
|
||||
" GL link graph delays",
|
||||
"Drawing",
|
||||
" Viewport drawing",
|
||||
"Video output",
|
||||
"Sound mixing",
|
||||
};
|
||||
|
||||
static const PerformanceElement rate_elements[] = { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO };
|
||||
|
||||
bool printed_anything = false;
|
||||
|
||||
for (const PerformanceElement *e = rate_elements; e < rate_elements + lengthof(rate_elements); e++) {
|
||||
auto &pf = _pf_data[*e];
|
||||
if (pf.num_valid == 0) continue;
|
||||
IConsolePrintF(TC_GREEN, "%s rate: %.2ffps (expected: %.2ffps)",
|
||||
MEASUREMENT_NAMES[*e],
|
||||
pf.GetRate(),
|
||||
pf.expected_rate);
|
||||
printed_anything = true;
|
||||
}
|
||||
|
||||
for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
|
||||
auto &pf = _pf_data[e];
|
||||
if (pf.num_valid == 0) continue;
|
||||
IConsolePrintF(TC_LIGHT_BLUE, "%s times: %.2fms %.2fms %.2fms",
|
||||
MEASUREMENT_NAMES[e],
|
||||
pf.GetAverageDurationMilliseconds(count1),
|
||||
pf.GetAverageDurationMilliseconds(count2),
|
||||
pf.GetAverageDurationMilliseconds(count3));
|
||||
printed_anything = true;
|
||||
}
|
||||
|
||||
if (!printed_anything) {
|
||||
IConsoleWarning("No performance measurements have been taken yet");
|
||||
}
|
||||
}
|
109
src/framerate_type.h
Normal file
109
src/framerate_type.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file framerate_type.h
|
||||
* Types for recording game performance data.
|
||||
*
|
||||
* @par Adding new measurements
|
||||
* Adding a new measurement requires multiple steps, which are outlined here.
|
||||
* The first thing to do is add a new member of the #PerformanceElement enum.
|
||||
* It must be added before \c PFE_MAX and should be added in a logical place.
|
||||
* For example, an element of the game loop would be added next to the other game loop elements, and a rendering element next to the other rendering elements.
|
||||
*
|
||||
* @par
|
||||
* Second is adding a member to the \link anonymous_namespace{framerate_gui.cpp}::_pf_data _pf_data \endlink array, in the same position as the new #PerformanceElement member.
|
||||
*
|
||||
* @par
|
||||
* Third is adding strings for the new element. There is an array in #ConPrintFramerate with strings used for the console command.
|
||||
* Additionally, there are two sets of strings in \c english.txt for two GUI uses, also in the #PerformanceElement order.
|
||||
* Search for \c STR_FRAMERATE_GAMELOOP and \c STR_FRAMETIME_CAPTION_GAMELOOP in \c english.txt to find those.
|
||||
*
|
||||
* @par
|
||||
* Last is actually adding the measurements. There are two ways to measure, either one-shot (a single function/block handling all processing),
|
||||
* or as an accumulated element (multiple functions/blocks that need to be summed across each frame/tick).
|
||||
* Use either the PerformanceMeasurer or the PerformanceAccumulator class respectively for the two cases.
|
||||
* Either class is used by instantiating an object of it at the beginning of the block to be measured, so it auto-destructs at the end of the block.
|
||||
* For PerformanceAccumulator, make sure to also call PerformanceAccumulator::Reset once at the beginning of a new frame. Usually the StateGameLoop function is appropriate for this.
|
||||
*
|
||||
* @see framerate_gui.cpp for implementation
|
||||
*/
|
||||
|
||||
#ifndef FRAMERATE_TYPE_H
|
||||
#define FRAMERATE_TYPE_H
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "core/enum_type.hpp"
|
||||
|
||||
/**
|
||||
* Elements of game performance that can be measured.
|
||||
*
|
||||
* @note When adding new elements here, make sure to also update all other locations depending on the length and order of this enum.
|
||||
* See <em>Adding new measurements</em> above.
|
||||
*/
|
||||
enum PerformanceElement {
|
||||
PFE_FIRST = 0,
|
||||
PFE_GAMELOOP = 0, ///< Speed of gameloop processing.
|
||||
PFE_GL_ECONOMY, ///< Time spent processing cargo movement
|
||||
PFE_GL_TRAINS, ///< Time spent processing trains
|
||||
PFE_GL_ROADVEHS, ///< Time spend processing road vehicles
|
||||
PFE_GL_SHIPS, ///< Time spent processing ships
|
||||
PFE_GL_AIRCRAFT, ///< Time spent processing aircraft
|
||||
PFE_GL_LANDSCAPE, ///< Time spent processing other world features
|
||||
PFE_GL_LINKGRAPH, ///< Time spent waiting for link graph background jobs
|
||||
PFE_DRAWING, ///< Speed of drawing world and GUI.
|
||||
PFE_DRAWWORLD, ///< Time spent drawing world viewports in GUI
|
||||
PFE_VIDEO, ///< Speed of painting drawn video buffer.
|
||||
PFE_SOUND, ///< Speed of mixing audio samples
|
||||
PFE_MAX, ///< End of enum, must be last.
|
||||
};
|
||||
DECLARE_POSTFIX_INCREMENT(PerformanceElement)
|
||||
|
||||
/** Type used to hold a performance timing measurement */
|
||||
typedef uint64 TimingMeasurement;
|
||||
|
||||
/**
|
||||
* RAII class for measuring simple elements of performance.
|
||||
* Construct an object with the appropriate element parameter when processing begins,
|
||||
* time is automatically taken when the object goes out of scope again.
|
||||
*
|
||||
* Call Paused at the start of a frame if the processing of this element is paused.
|
||||
*/
|
||||
class PerformanceMeasurer {
|
||||
PerformanceElement elem;
|
||||
TimingMeasurement start_time;
|
||||
public:
|
||||
PerformanceMeasurer(PerformanceElement elem);
|
||||
~PerformanceMeasurer();
|
||||
void SetExpectedRate(double rate);
|
||||
static void Paused(PerformanceElement elem);
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII class for measuring multi-step elements of performance.
|
||||
* At the beginning of a frame, call Reset on the element, then construct an object in the scope where
|
||||
* each processing cycle happens. The measurements are summed between resets.
|
||||
*
|
||||
* Usually StateGameLoop is an appropriate function to place Reset calls in, but for elements with
|
||||
* more isolated scopes it can also be appropriate to Reset somewhere else.
|
||||
* An example is the CallVehicleTicks function where all the vehicle type elements are reset.
|
||||
*
|
||||
* The PerformanceMeasurer::Paused function can also be used with elements otherwise measured with this class.
|
||||
*/
|
||||
class PerformanceAccumulator {
|
||||
PerformanceElement elem;
|
||||
TimingMeasurement start_time;
|
||||
public:
|
||||
PerformanceAccumulator(PerformanceElement elem);
|
||||
~PerformanceAccumulator();
|
||||
static void Reset(PerformanceElement elem);
|
||||
};
|
||||
|
||||
void ShowFramerateWindow();
|
||||
|
||||
#endif /* FRAMERATE_TYPE_H */
|
@@ -29,9 +29,9 @@ static bool CheckAPIVersion(const char *api_version)
|
||||
strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
#undef GetClassName
|
||||
#endif /* WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
template <> const char *GetClassName<GameInfo, ST_GS>() { return "GSInfo"; }
|
||||
|
||||
/* static */ void GameInfo::RegisterAPI(Squirrel *engine)
|
||||
|
@@ -142,7 +142,7 @@ struct StringListReader : StringReader {
|
||||
/**
|
||||
* Create the reader.
|
||||
* @param data The data to fill during reading.
|
||||
* @param file The file we are reading.
|
||||
* @param strings The language strings we are reading.
|
||||
* @param master Are we reading the master file?
|
||||
* @param translation Are we reading a translation?
|
||||
*/
|
||||
|
@@ -771,9 +771,9 @@ void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
|
||||
* Get some basic information from the given gamelog.
|
||||
* @param gamelog_action Pointer to the gamelog to extract information from.
|
||||
* @param gamelog_actions Number of actions in the given gamelog.
|
||||
* @param [out] last_ottd_rev OpenTTD NewGRF version from the binary that saved the savegame last.
|
||||
* @param [out] ever_modified Max value of 'modified' from all binaries that ever saved this savegame.
|
||||
* @param [out] removed_newgrfs Set to true if any NewGRFs have been removed.
|
||||
* @param[out] last_ottd_rev OpenTTD NewGRF version from the binary that saved the savegame last.
|
||||
* @param[out] ever_modified Max value of 'modified' from all binaries that ever saved this savegame.
|
||||
* @param[out] removed_newgrfs Set to true if any NewGRFs have been removed.
|
||||
*/
|
||||
void GamelogInfo(LoggedAction *gamelog_action, uint gamelog_actions, uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs)
|
||||
{
|
||||
|
@@ -38,7 +38,7 @@ void GamelogReset();
|
||||
* @param s The string to print.
|
||||
*/
|
||||
typedef void GamelogPrintProc(const char *s);
|
||||
void GamelogPrint(GamelogPrintProc *proc); // needed for WIN32 / WINCE crash.log
|
||||
void GamelogPrint(GamelogPrintProc *proc); // needed for WIN32 crash.log
|
||||
|
||||
void GamelogPrintDebug(int level);
|
||||
void GamelogPrintConsole();
|
||||
|
@@ -22,7 +22,7 @@ enum LandscapeGenerator {
|
||||
LG_TERRAGENESIS = 1, ///< TerraGenesis Perlin landscape generator
|
||||
};
|
||||
|
||||
static const uint GENERATE_NEW_SEED = UINT_MAX; ///< Create a new random seed
|
||||
static const uint32 GENERATE_NEW_SEED = UINT32_MAX; ///< Create a new random seed
|
||||
|
||||
/** Modes for GenerateWorld */
|
||||
enum GenWorldMode {
|
||||
@@ -97,7 +97,7 @@ void SetGeneratingWorldProgress(GenWorldProgress cls, uint total);
|
||||
void IncreaseGeneratingWorldProgress(GenWorldProgress cls);
|
||||
void PrepareGenerateWorldProgress();
|
||||
void ShowGenerateWorldProgress();
|
||||
void StartNewGameWithoutGUI(uint seed);
|
||||
void StartNewGameWithoutGUI(uint32 seed);
|
||||
void ShowCreateScenario();
|
||||
void StartScenarioEditor();
|
||||
|
||||
|
@@ -288,7 +288,7 @@ static DropDownList *BuildMapsizeDropDown()
|
||||
|
||||
for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) {
|
||||
DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false);
|
||||
item->SetParam(0, 1 << i);
|
||||
item->SetParam(0, 1LL << i);
|
||||
*list->Append() = item;
|
||||
}
|
||||
|
||||
@@ -336,8 +336,8 @@ struct GenerateLandscapeWindow : public Window {
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_GL_START_DATE_TEXT: SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); break;
|
||||
case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_x); break;
|
||||
case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_y); break;
|
||||
case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break;
|
||||
case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break;
|
||||
case WID_GL_MAX_HEIGHTLEVEL_TEXT: SetDParam(0, _settings_newgame.construction.max_heightlevel); break;
|
||||
case WID_GL_SNOW_LEVEL_TEXT: SetDParam(0, _settings_newgame.game_creation.snow_line_height); break;
|
||||
|
||||
@@ -870,7 +870,7 @@ void StartScenarioEditor()
|
||||
* Start a normal game without the GUI.
|
||||
* @param seed The seed of the new game.
|
||||
*/
|
||||
void StartNewGameWithoutGUI(uint seed)
|
||||
void StartNewGameWithoutGUI(uint32 seed)
|
||||
{
|
||||
/* GenerateWorld takes care of the possible GENERATE_NEW_SEED value in 'seed' */
|
||||
_settings_newgame.game_creation.generation_seed = seed;
|
||||
@@ -896,11 +896,11 @@ struct CreateScenarioWindow : public Window
|
||||
break;
|
||||
|
||||
case WID_CS_MAPSIZE_X_PULLDOWN:
|
||||
SetDParam(0, 1 << _settings_newgame.game_creation.map_x);
|
||||
SetDParam(0, 1LL << _settings_newgame.game_creation.map_x);
|
||||
break;
|
||||
|
||||
case WID_CS_MAPSIZE_Y_PULLDOWN:
|
||||
SetDParam(0, 1 << _settings_newgame.game_creation.map_y);
|
||||
SetDParam(0, 1LL << _settings_newgame.game_creation.map_y);
|
||||
break;
|
||||
|
||||
case WID_CS_FLAT_LAND_HEIGHT_TEXT:
|
||||
|
26
src/gfx.cpp
26
src/gfx.cpp
@@ -703,8 +703,8 @@ Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bounding box of a string. Uses parameters set by #DParam if needed.
|
||||
* Has the same restrictions as #GetStringBoundingBox(const char *str).
|
||||
* Get bounding box of a string. Uses parameters set by #SetDParam if needed.
|
||||
* Has the same restrictions as #GetStringBoundingBox(const char *str, FontSize start_fontsize).
|
||||
* @param strid String to examine.
|
||||
* @return Width and height of the bounding box for the string in pixels.
|
||||
*/
|
||||
@@ -761,7 +761,7 @@ void DrawCharCentered(WChar c, int x, int y, TextColour colour)
|
||||
/**
|
||||
* Get the size of a sprite.
|
||||
* @param sprid Sprite to examine.
|
||||
* @param [out] offset Optionally returns the sprite position offset.
|
||||
* @param[out] offset Optionally returns the sprite position offset.
|
||||
* @return Sprite size in pixels.
|
||||
* @note The size assumes (0, 0) as top-left coordinate and ignores any part of the sprite drawn at the left or above that position.
|
||||
*/
|
||||
@@ -1111,16 +1111,17 @@ void DoPaletteAnimations()
|
||||
/**
|
||||
* Determine a contrasty text colour for a coloured background.
|
||||
* @param background Background colour.
|
||||
* @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128.
|
||||
* @return TC_BLACK or TC_WHITE depending on what gives a better contrast.
|
||||
*/
|
||||
TextColour GetContrastColour(uint8 background)
|
||||
TextColour GetContrastColour(uint8 background, uint8 threshold)
|
||||
{
|
||||
Colour c = _cur_palette.palette[background];
|
||||
/* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast.
|
||||
* The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */
|
||||
uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
|
||||
/* Compare with threshold brightness 128 (50%) */
|
||||
return sq1000_brightness < 128 * 128 * 1000 ? TC_WHITE : TC_BLACK;
|
||||
/* Compare with threshold brightness which defaults to 128 (50%) */
|
||||
return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1169,8 +1170,8 @@ byte GetDigitWidth(FontSize size)
|
||||
|
||||
/**
|
||||
* Determine the broadest digits for guessing the maximum width of a n-digit number.
|
||||
* @param [out] front Broadest digit, which is not 0. (Use this digit as first digit for numbers with more than one digit.)
|
||||
* @param [out] next Broadest digit, including 0. (Use this digit for all digits, except the first one; or for numbers with only one digit.)
|
||||
* @param[out] front Broadest digit, which is not 0. (Use this digit as first digit for numbers with more than one digit.)
|
||||
* @param[out] next Broadest digit, including 0. (Use this digit for all digits, except the first one; or for numbers with only one digit.)
|
||||
* @param size Font of the digit
|
||||
*/
|
||||
void GetBroadestDigit(uint *front, uint *next, FontSize size)
|
||||
@@ -1214,11 +1215,6 @@ void UndrawMouseCursor()
|
||||
|
||||
void DrawMouseCursor()
|
||||
{
|
||||
#if defined(WINCE)
|
||||
/* Don't ever draw the mouse for WinCE, as we work with a stylus */
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Don't draw the mouse cursor if the screen is not ready */
|
||||
if (_screen.dst_ptr == NULL) return;
|
||||
|
||||
@@ -1639,8 +1635,8 @@ void SetAnimatedMouseCursor(const AnimCursor *table)
|
||||
* Update cursor position on mouse movement.
|
||||
* @param x New X position.
|
||||
* @param y New Y position.
|
||||
* @param queued True, if the OS queues mouse warps after pending mouse movement events.
|
||||
* False, if the warp applies instantaneous.
|
||||
* @param queued_warp True, if the OS queues mouse warps after pending mouse movement events.
|
||||
* False, if the warp applies instantaneous.
|
||||
* @return true, if the OS cursor position should be warped back to this->pos.
|
||||
*/
|
||||
bool CursorVars::UpdateCursorPosition(int x, int y, bool queued_warp)
|
||||
|
@@ -187,7 +187,7 @@ int GetCharacterHeight(FontSize size);
|
||||
|
||||
extern DrawPixelInfo *_cur_dpi;
|
||||
|
||||
TextColour GetContrastColour(uint8 background);
|
||||
TextColour GetContrastColour(uint8 background, uint8 threshold = 128);
|
||||
|
||||
/**
|
||||
* All 16 colour gradients
|
||||
|
@@ -21,6 +21,14 @@
|
||||
#include <unicode/ustring.h>
|
||||
#endif /* WITH_ICU_LAYOUT */
|
||||
|
||||
#ifdef WITH_UNISCRIBE
|
||||
#include "os/windows/string_uniscribe.h"
|
||||
#endif /* WITH_UNISCRIBE */
|
||||
|
||||
#ifdef WITH_COCOA
|
||||
#include "os/macosx/string_osx.h"
|
||||
#endif
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
@@ -113,32 +121,18 @@ le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &poin
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
|
||||
{
|
||||
/* Transform from UTF-32 to internal ICU format of UTF-16. */
|
||||
int32 length = 0;
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for doing layouts with ICU.
|
||||
*/
|
||||
class ICUParagraphLayout : public AutoDeleteSmallVector<ParagraphLayouter::Line *, 4>, public ParagraphLayouter {
|
||||
ParagraphLayout *p; ///< The actual ICU paragraph layout.
|
||||
icu::ParagraphLayout *p; ///< The actual ICU paragraph layout.
|
||||
public:
|
||||
/** Helper for GetLayouter, to get the right type. */
|
||||
typedef UChar CharType;
|
||||
/** Helper for GetLayouter, to get whether the layouter supports RTL. */
|
||||
static const bool SUPPORTS_RTL = true;
|
||||
|
||||
/** Visual run contains data about the bit of text with the same font. */
|
||||
class ICUVisualRun : public ParagraphLayouter::VisualRun {
|
||||
const ParagraphLayout::VisualRun *vr; ///< The actual ICU vr.
|
||||
const icu::ParagraphLayout::VisualRun *vr; ///< The actual ICU vr.
|
||||
|
||||
public:
|
||||
ICUVisualRun(const ParagraphLayout::VisualRun *vr) : vr(vr) { }
|
||||
ICUVisualRun(const icu::ParagraphLayout::VisualRun *vr) : vr(vr) { }
|
||||
|
||||
const Font *GetFont() const { return (const Font*)vr->getFont(); }
|
||||
int GetGlyphCount() const { return vr->getGlyphCount(); }
|
||||
@@ -150,10 +144,10 @@ public:
|
||||
|
||||
/** A single line worth of VisualRuns. */
|
||||
class ICULine : public AutoDeleteSmallVector<ICUVisualRun *, 4>, public ParagraphLayouter::Line {
|
||||
ParagraphLayout::Line *l; ///< The actual ICU line.
|
||||
icu::ParagraphLayout::Line *l; ///< The actual ICU line.
|
||||
|
||||
public:
|
||||
ICULine(ParagraphLayout::Line *l) : l(l)
|
||||
ICULine(icu::ParagraphLayout::Line *l) : l(l)
|
||||
{
|
||||
for (int i = 0; i < l->countRuns(); i++) {
|
||||
*this->Append() = new ICUVisualRun(l->getVisualRun(i));
|
||||
@@ -173,46 +167,65 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
ICUParagraphLayout(ParagraphLayout *p) : p(p) { }
|
||||
ICUParagraphLayout(icu::ParagraphLayout *p) : p(p) { }
|
||||
~ICUParagraphLayout() { delete p; }
|
||||
void Reflow() { p->reflow(); }
|
||||
|
||||
ParagraphLayouter::Line *NextLine(int max_width)
|
||||
{
|
||||
ParagraphLayout::Line *l = p->nextLine(max_width);
|
||||
icu::ParagraphLayout::Line *l = p->nextLine(max_width);
|
||||
return l == NULL ? NULL : new ICULine(l);
|
||||
}
|
||||
};
|
||||
|
||||
static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
int32 length = buff_end - buff;
|
||||
/**
|
||||
* Helper class to construct a new #ICUParagraphLayout.
|
||||
*/
|
||||
class ICUParagraphLayoutFactory {
|
||||
public:
|
||||
/** Helper for GetLayouter, to get the right type. */
|
||||
typedef UChar CharType;
|
||||
/** Helper for GetLayouter, to get whether the layouter supports RTL. */
|
||||
static const bool SUPPORTS_RTL = true;
|
||||
|
||||
if (length == 0) {
|
||||
/* ICU's ParagraphLayout cannot handle empty strings, so fake one. */
|
||||
buff[0] = ' ';
|
||||
length = 1;
|
||||
fontMapping.End()[-1].first++;
|
||||
static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
int32 length = buff_end - buff;
|
||||
|
||||
if (length == 0) {
|
||||
/* ICU's ParagraphLayout cannot handle empty strings, so fake one. */
|
||||
buff[0] = ' ';
|
||||
length = 1;
|
||||
fontMapping.End()[-1].first++;
|
||||
}
|
||||
|
||||
/* Fill ICU's FontRuns with the right data. */
|
||||
icu::FontRuns runs(fontMapping.Length());
|
||||
for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
|
||||
runs.add(iter->second, iter->first);
|
||||
}
|
||||
|
||||
LEErrorCode status = LE_NO_ERROR;
|
||||
/* ParagraphLayout does not copy "buff", so it must stay valid.
|
||||
* "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */
|
||||
icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
|
||||
if (status != LE_NO_ERROR) {
|
||||
delete p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new ICUParagraphLayout(p);
|
||||
}
|
||||
|
||||
/* Fill ICU's FontRuns with the right data. */
|
||||
FontRuns runs(fontMapping.Length());
|
||||
for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
|
||||
runs.add(iter->second, iter->first);
|
||||
static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
|
||||
{
|
||||
/* Transform from UTF-32 to internal ICU format of UTF-16. */
|
||||
int32 length = 0;
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
|
||||
return length;
|
||||
}
|
||||
|
||||
LEErrorCode status = LE_NO_ERROR;
|
||||
/* ParagraphLayout does not copy "buff", so it must stay valid.
|
||||
* "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */
|
||||
ParagraphLayout *p = new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
|
||||
if (status != LE_NO_ERROR) {
|
||||
delete p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new ICUParagraphLayout(p);
|
||||
}
|
||||
|
||||
};
|
||||
#endif /* WITH_ICU_LAYOUT */
|
||||
|
||||
/*** Paragraph layout ***/
|
||||
@@ -236,11 +249,6 @@ static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontM
|
||||
*/
|
||||
class FallbackParagraphLayout : public ParagraphLayouter {
|
||||
public:
|
||||
/** Helper for GetLayouter, to get the right type. */
|
||||
typedef WChar CharType;
|
||||
/** Helper for GetLayouter, to get whether the layouter supports RTL. */
|
||||
static const bool SUPPORTS_RTL = false;
|
||||
|
||||
/** Visual run contains data about the bit of text with the same font. */
|
||||
class FallbackVisualRun : public ParagraphLayouter::VisualRun {
|
||||
Font *font; ///< The font used to layout these.
|
||||
@@ -280,6 +288,42 @@ public:
|
||||
const ParagraphLayouter::Line *NextLine(int max_width);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class to construct a new #FallbackParagraphLayout.
|
||||
*/
|
||||
class FallbackParagraphLayoutFactory {
|
||||
public:
|
||||
/** Helper for GetLayouter, to get the right type. */
|
||||
typedef WChar CharType;
|
||||
/** Helper for GetLayouter, to get whether the layouter supports RTL. */
|
||||
static const bool SUPPORTS_RTL = false;
|
||||
|
||||
/**
|
||||
* Get the actual ParagraphLayout for the given buffer.
|
||||
* @param buff The begin of the buffer.
|
||||
* @param buff_end The location after the last element in the buffer.
|
||||
* @param fontMapping THe mapping of the fonts.
|
||||
* @return The ParagraphLayout instance.
|
||||
*/
|
||||
static ParagraphLayouter *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a wide character to the internal buffer.
|
||||
* @param buff The buffer to append to.
|
||||
* @param buffer_last The end of the buffer.
|
||||
* @param c The character to add.
|
||||
* @return The number of buffer spaces that were used.
|
||||
*/
|
||||
static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
|
||||
{
|
||||
*buff = c;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the visual run.
|
||||
* @param font The font to use for this run.
|
||||
@@ -458,11 +502,6 @@ const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width)
|
||||
return l;
|
||||
}
|
||||
|
||||
const WChar *begin = this->buffer;
|
||||
const WChar *last_space = NULL;
|
||||
const WChar *last_char = begin;
|
||||
int width = 0;
|
||||
|
||||
int offset = this->buffer - this->buffer_begin;
|
||||
FontMap::iterator iter = this->runs.Begin();
|
||||
while (iter->first <= offset) {
|
||||
@@ -473,6 +512,10 @@ const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width)
|
||||
const FontCache *fc = iter->second->fc;
|
||||
const WChar *next_run = this->buffer_begin + iter->first;
|
||||
|
||||
const WChar *begin = this->buffer;
|
||||
const WChar *last_space = NULL;
|
||||
const WChar *last_char;
|
||||
int width = 0;
|
||||
for (;;) {
|
||||
WChar c = *this->buffer;
|
||||
last_char = this->buffer;
|
||||
@@ -536,31 +579,6 @@ const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width)
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appand a wide character to the internal buffer.
|
||||
* @param buff The buffer to append to.
|
||||
* @param buffer_last The end of the buffer.
|
||||
* @param c The character to add.
|
||||
* @return The number of buffer spaces that were used.
|
||||
*/
|
||||
static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
|
||||
{
|
||||
*buff = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual ParagraphLayout for the given buffer.
|
||||
* @param buff The begin of the buffer.
|
||||
* @param buff_end The location after the last element in the buffer.
|
||||
* @param fontMapping THe mapping of the fonts.
|
||||
* @return The ParagraphLayout instance.
|
||||
*/
|
||||
static FallbackParagraphLayout *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
|
||||
{
|
||||
return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting a ParagraphLayouter of the given type.
|
||||
*
|
||||
@@ -582,6 +600,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
|
||||
Font *f = Layouter::GetFont(state.fontsize, state.cur_colour);
|
||||
|
||||
line.buffer = buff_begin;
|
||||
fontMapping.Clear();
|
||||
|
||||
/*
|
||||
* Go through the whole string while adding Font instances to the font map
|
||||
@@ -605,7 +624,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
|
||||
* will not be handled in the fallback non ICU case because they are
|
||||
* mostly needed for RTL languages which need more ICU support. */
|
||||
if (!T::SUPPORTS_RTL && IsTextDirectionChar(c)) continue;
|
||||
buff += AppendToBuffer(buff, buffer_last, c);
|
||||
buff += T::AppendToBuffer(buff, buffer_last, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -621,7 +640,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
|
||||
if (!fontMapping.Contains(buff - buff_begin)) {
|
||||
fontMapping.Insert(buff - buff_begin, f);
|
||||
}
|
||||
line.layout = GetParagraphLayout(buff_begin, buff, fontMapping);
|
||||
line.layout = T::GetParagraphLayout(buff_begin, buff, fontMapping);
|
||||
line.state_after = state;
|
||||
}
|
||||
|
||||
@@ -654,11 +673,13 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi
|
||||
line.layout->Reflow();
|
||||
} else {
|
||||
/* Line is new, layout it */
|
||||
#ifdef WITH_ICU_LAYOUT
|
||||
FontState old_state = state;
|
||||
#if defined(WITH_ICU_LAYOUT) || defined(WITH_UNISCRIBE) || defined(WITH_COCOA)
|
||||
const char *old_str = str;
|
||||
#endif
|
||||
|
||||
GetLayouter<ICUParagraphLayout>(line, str, state);
|
||||
#ifdef WITH_ICU_LAYOUT
|
||||
GetLayouter<ICUParagraphLayoutFactory>(line, str, state);
|
||||
if (line.layout == NULL) {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
@@ -668,11 +689,32 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi
|
||||
|
||||
state = old_state;
|
||||
str = old_str;
|
||||
GetLayouter<FallbackParagraphLayout>(line, str, state);
|
||||
}
|
||||
#else
|
||||
GetLayouter<FallbackParagraphLayout>(line, str, state);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_UNISCRIBE
|
||||
if (line.layout == NULL) {
|
||||
GetLayouter<UniscribeParagraphLayoutFactory>(line, str, state);
|
||||
if (line.layout == NULL) {
|
||||
state = old_state;
|
||||
str = old_str;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_COCOA
|
||||
if (line.layout == NULL) {
|
||||
GetLayouter<CoreTextParagraphLayoutFactory>(line, str, state);
|
||||
if (line.layout == NULL) {
|
||||
state = old_state;
|
||||
str = old_str;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (line.layout == NULL) {
|
||||
GetLayouter<FallbackParagraphLayoutFactory>(line, str, state);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy all lines into a local cache so we can reuse them later on more easily. */
|
||||
@@ -809,6 +851,13 @@ void Layouter::ResetFontCache(FontSize size)
|
||||
|
||||
/* We must reset the linecache since it references the just freed fonts */
|
||||
ResetLineCache();
|
||||
|
||||
#if defined(WITH_UNISCRIBE)
|
||||
UniscribeResetScriptCache(size);
|
||||
#endif
|
||||
#if defined(WITH_COCOA)
|
||||
MacOSResetScriptCache(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#ifdef WITH_ICU_LAYOUT
|
||||
#include "layout/ParagraphLayout.h"
|
||||
#define ICU_FONTINSTANCE : public LEFontInstance
|
||||
#define ICU_FONTINSTANCE : public icu::LEFontInstance
|
||||
#else /* WITH_ICU_LAYOUT */
|
||||
#define ICU_FONTINSTANCE
|
||||
#endif /* WITH_ICU_LAYOUT */
|
||||
|
@@ -189,7 +189,7 @@ union Colour {
|
||||
|
||||
/**
|
||||
* Create a new colour.
|
||||
* @param The colour in the correct packed format.
|
||||
* @param data The colour in the correct packed format.
|
||||
*/
|
||||
Colour(uint data = 0) : data(data)
|
||||
{
|
||||
|
@@ -80,7 +80,7 @@ static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
|
||||
/**
|
||||
* Load an old fashioned GRF file to replace already loaded sprites.
|
||||
* @param filename The name of the file to open.
|
||||
* @param index_tlb The offsets of each of the sprites.
|
||||
* @param index_tbl The offsets of each of the sprites.
|
||||
* @param file_index The Fio offset to load the file in.
|
||||
* @return The number of loaded sprites.
|
||||
*/
|
||||
@@ -284,6 +284,9 @@ static bool SwitchNewGRFBlitter()
|
||||
#endif
|
||||
{ "8bpp-optimized", 2, 8, 8, 8, 8 },
|
||||
{ "32bpp-optimized", 0, 8, 32, 8, 32 },
|
||||
#ifdef WITH_SSE
|
||||
{ "32bpp-sse2-anim", 1, 8, 32, 8, 32 },
|
||||
#endif
|
||||
{ "32bpp-anim", 1, 8, 32, 8, 32 },
|
||||
};
|
||||
|
||||
|
32
src/goal.cpp
32
src/goal.cpp
@@ -23,6 +23,8 @@
|
||||
#include "string_func.h"
|
||||
#include "gui.h"
|
||||
#include "network/network.h"
|
||||
#include "network/network_base.h"
|
||||
#include "network/network_func.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
@@ -234,7 +236,9 @@ CommandCost CmdSetGoalCompleted(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
||||
* @param flags type of operation
|
||||
* @param p1 various bitstuffed elements
|
||||
* - p1 = (bit 0 - 15) - Unique ID to use for this question.
|
||||
* - p1 = (bit 16 - 23) - Company for which this question is.
|
||||
* - p1 = (bit 16 - 23) - Company or client for which this question is.
|
||||
* - p1 = (bit 24 - 25) - Question type.
|
||||
* - p1 = (bit 31) - Question target: 0 - company, 1 - client.
|
||||
* @param p2 Buttons of the question.
|
||||
* @param text Text of the question.
|
||||
* @return the cost of this operation or an error
|
||||
@@ -243,17 +247,37 @@ CommandCost CmdGoalQuestion(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
{
|
||||
uint16 uniqueid = (GoalType)GB(p1, 0, 16);
|
||||
CompanyID company = (CompanyID)GB(p1, 16, 8);
|
||||
byte type = GB(p1, 24, 8);
|
||||
#ifdef ENABLE_NETWORK
|
||||
ClientIndex client = (ClientIndex)GB(p1, 16, 8);
|
||||
#endif
|
||||
byte type = GB(p1, 24, 2);
|
||||
bool is_client = HasBit(p1, 31);
|
||||
|
||||
if (_current_company != OWNER_DEITY) return CMD_ERROR;
|
||||
if (StrEmpty(text)) return CMD_ERROR;
|
||||
if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
|
||||
if (is_client) {
|
||||
#ifdef ENABLE_NETWORK
|
||||
if (!NetworkClientInfo::IsValidID(client)) return CMD_ERROR;
|
||||
#else
|
||||
return CMD_ERROR;
|
||||
#endif
|
||||
} else {
|
||||
if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR;
|
||||
}
|
||||
if (CountBits(p2) < 1 || CountBits(p2) > 3) return CMD_ERROR;
|
||||
if (p2 >= (1 << GOAL_QUESTION_BUTTON_COUNT)) return CMD_ERROR;
|
||||
if (type >= GOAL_QUESTION_TYPE_COUNT) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
if ((company != INVALID_COMPANY && company == _local_company) || (company == INVALID_COMPANY && Company::IsValidID(_local_company))) ShowGoalQuestion(uniqueid, type, p2, text);
|
||||
if (is_client) {
|
||||
#ifdef ENABLE_NETWORK
|
||||
if (NetworkClientInfo::Get(client)->client_id != _network_own_client_id) return CommandCost();
|
||||
#endif
|
||||
} else {
|
||||
if (company == INVALID_COMPANY && !Company::IsValidID(_local_company)) return CommandCost();
|
||||
if (company != INVALID_COMPANY && company != _local_company) return CommandCost();
|
||||
}
|
||||
ShowGoalQuestion(uniqueid, type, p2, text);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
|
@@ -101,7 +101,7 @@ struct GoalListWindow : public Window {
|
||||
|
||||
/**
|
||||
* Handle clicking at a goal.
|
||||
* @param s @Goal clicked at.
|
||||
* @param s #Goal clicked at.
|
||||
*/
|
||||
void HandleClick(const Goal *s)
|
||||
{
|
||||
@@ -193,7 +193,7 @@ struct GoalListWindow : public Window {
|
||||
/**
|
||||
* Draws either the global goals or the company goal section.
|
||||
* This is a helper method for #DrawWidget.
|
||||
* @param pos [inout] Vertical line number to draw.
|
||||
* @param[in,out] pos Vertical line number to draw.
|
||||
* @param cap Number of lines to draw in the window.
|
||||
* @param x Left edge of the text line to draw.
|
||||
* @param y Vertical position of the top edge of the window.
|
||||
@@ -250,8 +250,8 @@ struct GoalListWindow : public Window {
|
||||
/**
|
||||
* Draws a given column of the goal list.
|
||||
* @param column Which column to draw.
|
||||
* @wid Pointer to the goal list widget.
|
||||
* @progress_col_width Width of the progress column.
|
||||
* @param wid Pointer to the goal list widget.
|
||||
* @param progress_col_width Width of the progress column.
|
||||
* @return max width of drawn text
|
||||
*/
|
||||
void DrawListColumn(GoalColumn column, NWidgetBase *wid, uint progress_col_width) const
|
||||
|
@@ -33,8 +33,8 @@
|
||||
#include "safeguards.h"
|
||||
|
||||
/* Bitmasks of company and cargo indices that shouldn't be drawn. */
|
||||
static uint _legend_excluded_companies;
|
||||
static uint _legend_excluded_cargo;
|
||||
static CompanyMask _legend_excluded_companies;
|
||||
static CargoTypes _legend_excluded_cargo;
|
||||
|
||||
/* Apparently these don't play well with enums. */
|
||||
static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
|
||||
@@ -166,14 +166,14 @@ struct ValuesInterval {
|
||||
|
||||
struct BaseGraphWindow : Window {
|
||||
protected:
|
||||
static const int GRAPH_MAX_DATASETS = 32;
|
||||
static const int GRAPH_MAX_DATASETS = 64;
|
||||
static const int GRAPH_AXIS_LINE_COLOUR = PC_BLACK;
|
||||
static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph.
|
||||
|
||||
static const int MIN_GRAPH_NUM_LINES_Y = 9; ///< Minimal number of horizontal lines to draw.
|
||||
static const int MIN_GRID_PIXEL_SIZE = 20; ///< Minimum distance between graph lines.
|
||||
|
||||
uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed.
|
||||
uint64 excluded_data; ///< bitmask of the datasets that shouldn't be displayed.
|
||||
byte num_dataset;
|
||||
byte num_on_x_axis;
|
||||
byte num_vert_lines;
|
||||
@@ -561,7 +561,7 @@ public:
|
||||
*/
|
||||
void UpdateStatistics(bool initialize)
|
||||
{
|
||||
uint excluded_companies = _legend_excluded_companies;
|
||||
CompanyMask excluded_companies = _legend_excluded_companies;
|
||||
|
||||
/* Exclude the companies which aren't valid */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
@@ -1402,9 +1402,9 @@ struct PerformanceRatingDetailWindow : Window {
|
||||
int colour_notdone = _colour_gradient[COLOUR_RED][4];
|
||||
|
||||
/* Draw all the score parts */
|
||||
int val = _score_part[company][score_type];
|
||||
int needed = _score_info[score_type].needed;
|
||||
int score = _score_info[score_type].score;
|
||||
int64 val = _score_part[company][score_type];
|
||||
int64 needed = _score_info[score_type].needed;
|
||||
int score = _score_info[score_type].score;
|
||||
|
||||
/* SCORE_TOTAL has his own rules ;) */
|
||||
if (score_type == SCORE_TOTAL) {
|
||||
@@ -1422,7 +1422,7 @@ struct PerformanceRatingDetailWindow : Window {
|
||||
DrawString(this->score_info_left, this->score_info_right, text_top, STR_BLACK_COMMA, TC_FROMSTRING, SA_RIGHT);
|
||||
|
||||
/* Calculate the %-bar */
|
||||
uint x = Clamp(val, 0, needed) * this->bar_width / needed;
|
||||
uint x = Clamp<int64>(val, 0, needed) * this->bar_width / needed;
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
if (rtl) {
|
||||
x = this->bar_right - x;
|
||||
@@ -1435,7 +1435,7 @@ struct PerformanceRatingDetailWindow : Window {
|
||||
if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height, rtl ? colour_done : colour_notdone);
|
||||
|
||||
/* Draw it */
|
||||
SetDParam(0, Clamp(val, 0, needed) * 100 / needed);
|
||||
SetDParam(0, Clamp<int64>(val, 0, needed) * 100 / needed);
|
||||
DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING, SA_HOR_CENTER);
|
||||
|
||||
/* SCORE_LOAN is inversed */
|
||||
|
@@ -58,8 +58,8 @@ void GroundVehicle<T, Type>::PowerChanged()
|
||||
|
||||
this->gcache.cached_air_drag = air_drag + 3 * air_drag * number_of_parts / 20;
|
||||
|
||||
max_te *= 10000; // Tractive effort in (tonnes * 1000 * 10 =) N.
|
||||
max_te /= 256; // Tractive effort is a [0-255] coefficient.
|
||||
max_te *= GROUND_ACCELERATION; // Tractive effort in (tonnes * 1000 * 9.8 =) N.
|
||||
max_te /= 256; // Tractive effort is a [0-255] coefficient.
|
||||
if (this->gcache.cached_power != total_power || this->gcache.cached_max_te != max_te) {
|
||||
/* Stop the vehicle if it has no power. */
|
||||
if (total_power == 0) this->vehstatus |= VS_STOPPED;
|
||||
|
@@ -98,7 +98,7 @@ void GroupStatistics::Clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all caches after loading a game, changing NewGRF etc..
|
||||
* Update all caches after loading a game, changing NewGRF, etc.
|
||||
*/
|
||||
/* static */ void GroupStatistics::UpdateAfterLoad()
|
||||
{
|
||||
|
@@ -977,11 +977,11 @@ static inline VehicleGroupWindow *FindVehicleGroupWindow(VehicleType vt, Owner o
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a 'Rename group' window for newly created group
|
||||
* @param success did command succeed?
|
||||
* @param tile unused
|
||||
* @param p1 vehicle type
|
||||
* @param p2 unused
|
||||
* Opens a 'Rename group' window for newly created group.
|
||||
* @param result Did command succeed?
|
||||
* @param tile Unused.
|
||||
* @param p1 Vehicle type.
|
||||
* @param p2 Unused.
|
||||
* @see CmdCreateGroup
|
||||
*/
|
||||
void CcCreateGroup(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
||||
@@ -995,7 +995,7 @@ void CcCreateGroup(const CommandCost &result, TileIndex tile, uint32 p1, uint32
|
||||
|
||||
/**
|
||||
* Open rename window after adding a vehicle to a new group via drag and drop.
|
||||
* @param success Did command succeed?
|
||||
* @param result Did command succeed?
|
||||
* @param tile Unused.
|
||||
* @param p1 Unused.
|
||||
* @param p2 Bit 0-19: Vehicle ID.
|
||||
|
@@ -447,9 +447,9 @@ void FixSlopes()
|
||||
* Reads the heightmap with the correct file reader.
|
||||
* @param dft Type of image file.
|
||||
* @param filename Name of the file to load.
|
||||
* @param [out] x Length of the image.
|
||||
* @param [out] y Height of the image.
|
||||
* @param [inout] map If not \c NULL, destination to store the loaded block of image data.
|
||||
* @param[out] x Length of the image.
|
||||
* @param[out] y Height of the image.
|
||||
* @param[in,out] map If not \c NULL, destination to store the loaded block of image data.
|
||||
* @return Whether loading was successful.
|
||||
*/
|
||||
static bool ReadHeightMap(DetailedFileType dft, const char *filename, uint *x, uint *y, byte **map)
|
||||
|
@@ -41,6 +41,7 @@ static const KeycodeNames _keycode_to_name[] = {
|
||||
{"GLOBAL", WKC_GLOBAL_HOTKEY},
|
||||
{"ESC", WKC_ESC},
|
||||
{"DEL", WKC_DELETE},
|
||||
{"BACKSPACE", WKC_BACKSPACE},
|
||||
{"RETURN", WKC_RETURN},
|
||||
{"BACKQUOTE", WKC_BACKQUOTE},
|
||||
{"F1", WKC_F1},
|
||||
|
@@ -120,7 +120,7 @@ struct HouseSpec {
|
||||
AnimationInfo animation; ///< information about the animation.
|
||||
byte processing_time; ///< Periodic refresh multiplier
|
||||
byte minimum_life; ///< The minimum number of years this house will survive before the town rebuilds it
|
||||
uint32 watched_cargoes; ///< Cargo types watched for acceptance.
|
||||
CargoTypes watched_cargoes; ///< Cargo types watched for acceptance.
|
||||
|
||||
Money GetRemovalCost() const;
|
||||
|
||||
|
@@ -12,9 +12,11 @@
|
||||
#ifndef INDUSTRY_H
|
||||
#define INDUSTRY_H
|
||||
|
||||
#include <algorithm>
|
||||
#include "newgrf_storage.h"
|
||||
#include "subsidy_type.h"
|
||||
#include "industry_map.h"
|
||||
#include "industrytype.h"
|
||||
#include "tilearea_type.h"
|
||||
|
||||
|
||||
@@ -37,20 +39,20 @@ enum ProductionLevels {
|
||||
* Defines the internal data of a functional industry.
|
||||
*/
|
||||
struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
||||
TileArea location; ///< Location of the industry
|
||||
Town *town; ///< Nearest town
|
||||
CargoID produced_cargo[2]; ///< 2 production cargo slots
|
||||
uint16 produced_cargo_waiting[2]; ///< amount of cargo produced per cargo
|
||||
uint16 incoming_cargo_waiting[3]; ///< incoming cargo waiting to be processed
|
||||
byte production_rate[2]; ///< production rate for each cargo
|
||||
byte prod_level; ///< general production level
|
||||
CargoID accepts_cargo[3]; ///< 3 input cargo slots
|
||||
uint16 this_month_production[2]; ///< stats of this month's production per cargo
|
||||
uint16 this_month_transported[2]; ///< stats of this month's transport per cargo
|
||||
byte last_month_pct_transported[2]; ///< percentage transported per cargo in the last full month
|
||||
uint16 last_month_production[2]; ///< total units produced per cargo in the last full month
|
||||
uint16 last_month_transported[2]; ///< total units transported per cargo in the last full month
|
||||
uint16 counter; ///< used for animation and/or production (if available cargo)
|
||||
TileArea location; ///< Location of the industry
|
||||
Town *town; ///< Nearest town
|
||||
CargoID produced_cargo[INDUSTRY_NUM_OUTPUTS]; ///< 16 production cargo slots
|
||||
uint16 produced_cargo_waiting[INDUSTRY_NUM_OUTPUTS]; ///< amount of cargo produced per cargo
|
||||
uint16 incoming_cargo_waiting[INDUSTRY_NUM_INPUTS]; ///< incoming cargo waiting to be processed
|
||||
byte production_rate[INDUSTRY_NUM_OUTPUTS]; ///< production rate for each cargo
|
||||
byte prod_level; ///< general production level
|
||||
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]; ///< 16 input cargo slots
|
||||
uint16 this_month_production[INDUSTRY_NUM_OUTPUTS]; ///< stats of this month's production per cargo
|
||||
uint16 this_month_transported[INDUSTRY_NUM_OUTPUTS]; ///< stats of this month's transport per cargo
|
||||
byte last_month_pct_transported[INDUSTRY_NUM_OUTPUTS]; ///< percentage transported per cargo in the last full month
|
||||
uint16 last_month_production[INDUSTRY_NUM_OUTPUTS]; ///< total units produced per cargo in the last full month
|
||||
uint16 last_month_transported[INDUSTRY_NUM_OUTPUTS]; ///< total units transported per cargo in the last full month
|
||||
uint16 counter; ///< used for animation and/or production (if available cargo)
|
||||
|
||||
IndustryType type; ///< type of industry.
|
||||
OwnerByte owner; ///< owner of the industry. Which SHOULD always be (imho) OWNER_NONE
|
||||
@@ -63,7 +65,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
||||
OwnerByte founder; ///< Founder of the industry
|
||||
Date construction_date; ///< Date of the construction of the industry
|
||||
uint8 construction_type; ///< Way the industry was constructed (@see IndustryConstructionType)
|
||||
Date last_cargo_accepted_at; ///< Last day cargo was accepted by this industry
|
||||
Date last_cargo_accepted_at[INDUSTRY_NUM_INPUTS]; ///< Last day each cargo type was accepted by this industry
|
||||
byte selected_layout; ///< Which tile layout was used when creating the industry
|
||||
|
||||
uint16 random; ///< Random value used for randomisation of all kinds of things
|
||||
@@ -85,6 +87,22 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
||||
return IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == this->index;
|
||||
}
|
||||
|
||||
inline int GetCargoProducedIndex(CargoID cargo) const
|
||||
{
|
||||
if (cargo == CT_INVALID) return -1;
|
||||
const CargoID *pos = std::find(this->produced_cargo, endof(this->produced_cargo), cargo);
|
||||
if (pos == endof(this->produced_cargo)) return -1;
|
||||
return pos - this->produced_cargo;
|
||||
}
|
||||
|
||||
inline int GetCargoAcceptedIndex(CargoID cargo) const
|
||||
{
|
||||
if (cargo == CT_INVALID) return -1;
|
||||
const CargoID *pos = std::find(this->accepts_cargo, endof(this->accepts_cargo), cargo);
|
||||
if (pos == endof(this->accepts_cargo)) return -1;
|
||||
return pos - this->accepts_cargo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the industry of the given tile
|
||||
* @param tile the tile to get the industry from
|
||||
|
@@ -390,39 +390,53 @@ static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh)
|
||||
return FlatteningFoundation(tileh);
|
||||
}
|
||||
|
||||
static void AddAcceptedCargo_Industry(TileIndex tile, CargoArray &acceptance, uint32 *always_accepted)
|
||||
static void AddAcceptedCargo_Industry(TileIndex tile, CargoArray &acceptance, CargoTypes *always_accepted)
|
||||
{
|
||||
IndustryGfx gfx = GetIndustryGfx(tile);
|
||||
const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
|
||||
const Industry *ind = Industry::GetByTile(tile);
|
||||
|
||||
/* When we have to use a callback, we put our data in the next two variables */
|
||||
CargoID raw_accepts_cargo[lengthof(itspec->accepts_cargo)];
|
||||
uint8 raw_cargo_acceptance[lengthof(itspec->acceptance)];
|
||||
/* Starting point for acceptance */
|
||||
CargoID accepts_cargo[lengthof(itspec->accepts_cargo)];
|
||||
int8 cargo_acceptance[lengthof(itspec->acceptance)];
|
||||
MemCpyT(accepts_cargo, itspec->accepts_cargo, lengthof(accepts_cargo));
|
||||
MemCpyT(cargo_acceptance, itspec->acceptance, lengthof(cargo_acceptance));
|
||||
|
||||
/* And then these will always point to a same sized array with the required data */
|
||||
const CargoID *accepts_cargo = itspec->accepts_cargo;
|
||||
const uint8 *cargo_acceptance = itspec->acceptance;
|
||||
if (itspec->special_flags & INDTILE_SPECIAL_ACCEPTS_ALL_CARGO) {
|
||||
/* Copy all accepted cargoes from industry itself */
|
||||
for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
|
||||
CargoID *pos = std::find(accepts_cargo, endof(accepts_cargo), ind->accepts_cargo[i]);
|
||||
if (pos == endof(accepts_cargo)) {
|
||||
/* Not found, insert */
|
||||
pos = std::find(accepts_cargo, endof(accepts_cargo), CT_INVALID);
|
||||
if (pos == endof(accepts_cargo)) continue; // nowhere to place, give up on this one
|
||||
*pos = ind->accepts_cargo[i];
|
||||
}
|
||||
cargo_acceptance[pos - accepts_cargo] += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasBit(itspec->callback_mask, CBM_INDT_ACCEPT_CARGO)) {
|
||||
/* Try callback for accepts list, if success override all existing accepts */
|
||||
uint16 res = GetIndustryTileCallback(CBID_INDTILE_ACCEPT_CARGO, 0, 0, gfx, Industry::GetByTile(tile), tile);
|
||||
if (res != CALLBACK_FAILED) {
|
||||
accepts_cargo = raw_accepts_cargo;
|
||||
for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_accepts_cargo[i] = GetCargoTranslation(GB(res, i * 5, 5), itspec->grf_prop.grffile);
|
||||
MemSetT(accepts_cargo, CT_INVALID, lengthof(accepts_cargo));
|
||||
for (uint i = 0; i < 3; i++) accepts_cargo[i] = GetCargoTranslation(GB(res, i * 5, 5), itspec->grf_prop.grffile);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasBit(itspec->callback_mask, CBM_INDT_CARGO_ACCEPTANCE)) {
|
||||
/* Try callback for acceptance list, if success override all existing acceptance */
|
||||
uint16 res = GetIndustryTileCallback(CBID_INDTILE_CARGO_ACCEPTANCE, 0, 0, gfx, Industry::GetByTile(tile), tile);
|
||||
if (res != CALLBACK_FAILED) {
|
||||
cargo_acceptance = raw_cargo_acceptance;
|
||||
for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_cargo_acceptance[i] = GB(res, i * 4, 4);
|
||||
MemSetT(cargo_acceptance, 0, lengthof(cargo_acceptance));
|
||||
for (uint i = 0; i < 3; i++) cargo_acceptance[i] = GB(res, i * 4, 4);
|
||||
}
|
||||
}
|
||||
|
||||
const Industry *ind = Industry::GetByTile(tile);
|
||||
for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) {
|
||||
CargoID a = accepts_cargo[i];
|
||||
if (a == CT_INVALID || cargo_acceptance[i] == 0) continue; // work only with valid cargoes
|
||||
if (a == CT_INVALID || cargo_acceptance[i] <= 0) continue; // work only with valid cargoes
|
||||
|
||||
/* Add accepted cargo */
|
||||
acceptance[a] += cargo_acceptance[i];
|
||||
@@ -1118,8 +1132,9 @@ static void ProduceIndustryGoods(Industry *i)
|
||||
if (HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1);
|
||||
|
||||
IndustryBehaviour indbehav = indsp->behaviour;
|
||||
i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + i->production_rate[0]);
|
||||
i->produced_cargo_waiting[1] = min(0xffff, i->produced_cargo_waiting[1] + i->production_rate[1]);
|
||||
for (size_t j = 0; j < lengthof(i->produced_cargo_waiting); j++) {
|
||||
i->produced_cargo_waiting[j] = min(0xffff, i->produced_cargo_waiting[j] + i->production_rate[j]);
|
||||
}
|
||||
|
||||
if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) {
|
||||
uint16 cb_res = CALLBACK_FAILED;
|
||||
@@ -1323,7 +1338,7 @@ static CheckNewIndustryProc * const _check_new_industry_procs[CHECK_END] = {
|
||||
* Find a town for the industry, while checking for multiple industries in the same town.
|
||||
* @param tile Position of the industry to build.
|
||||
* @param type Industry type.
|
||||
* @param [out] town Pointer to return town for the new industry, \c NULL is written if no good town can be found.
|
||||
* @param[out] t Pointer to return town for the new industry, \c NULL is written if no good town can be found.
|
||||
* @return Succeeded or failed command.
|
||||
*
|
||||
* @pre \c *t != NULL
|
||||
@@ -1365,14 +1380,14 @@ bool IsSlopeRefused(Slope current, Slope refused)
|
||||
|
||||
/**
|
||||
* Are the tiles of the industry free?
|
||||
* @param tile Position to check.
|
||||
* @param it Industry tiles table.
|
||||
* @param itspec_index The index of the itsepc to build/fund
|
||||
* @param type Type of the industry.
|
||||
* @param initial_random_bits The random bits the industry is going to have after construction.
|
||||
* @param founder Industry founder
|
||||
* @param creation_type The circumstances the industry is created under.
|
||||
* @param [out] custom_shape_check Perform custom check for the site.
|
||||
* @param tile Position to check.
|
||||
* @param it Industry tiles table.
|
||||
* @param itspec_index The index of the itsepc to build/fund
|
||||
* @param type Type of the industry.
|
||||
* @param initial_random_bits The random bits the industry is going to have after construction.
|
||||
* @param founder Industry founder
|
||||
* @param creation_type The circumstances the industry is created under.
|
||||
* @param[out] custom_shape_check Perform custom check for the site.
|
||||
* @return Failed or succeeded command.
|
||||
*/
|
||||
static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, uint itspec_index, int type, uint16 initial_random_bits, Owner founder, IndustryAvailabilityCallType creation_type, bool *custom_shape_check = NULL)
|
||||
@@ -1648,18 +1663,23 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
||||
i->type = type;
|
||||
Industry::IncIndustryTypeCount(type);
|
||||
|
||||
i->produced_cargo[0] = indspec->produced_cargo[0];
|
||||
i->produced_cargo[1] = indspec->produced_cargo[1];
|
||||
i->accepts_cargo[0] = indspec->accepts_cargo[0];
|
||||
i->accepts_cargo[1] = indspec->accepts_cargo[1];
|
||||
i->accepts_cargo[2] = indspec->accepts_cargo[2];
|
||||
i->production_rate[0] = indspec->production_rate[0];
|
||||
i->production_rate[1] = indspec->production_rate[1];
|
||||
MemCpyT(i->produced_cargo, indspec->produced_cargo, lengthof(i->produced_cargo));
|
||||
MemCpyT(i->production_rate, indspec->production_rate, lengthof(i->production_rate));
|
||||
MemCpyT(i->accepts_cargo, indspec->accepts_cargo, lengthof(i->accepts_cargo));
|
||||
|
||||
MemSetT(i->produced_cargo_waiting, 0, lengthof(i->produced_cargo_waiting));
|
||||
MemSetT(i->this_month_production, 0, lengthof(i->this_month_production));
|
||||
MemSetT(i->this_month_transported, 0, lengthof(i->this_month_transported));
|
||||
MemSetT(i->last_month_pct_transported, 0, lengthof(i->last_month_pct_transported));
|
||||
MemSetT(i->last_month_transported, 0, lengthof(i->last_month_transported));
|
||||
MemSetT(i->incoming_cargo_waiting, 0, lengthof(i->incoming_cargo_waiting));
|
||||
MemSetT(i->last_cargo_accepted_at, 0, lengthof(i->last_cargo_accepted_at));
|
||||
|
||||
/* don't use smooth economy for industries using production related callbacks */
|
||||
if (indspec->UsesSmoothEconomy()) {
|
||||
i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8, 255);
|
||||
i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8, 255);
|
||||
for (size_t ci = 0; ci < lengthof(i->production_rate); ci++) {
|
||||
i->production_rate[ci] = min((RandomRange(256) + 128) * i->production_rate[ci] >> 8, 255);
|
||||
}
|
||||
}
|
||||
|
||||
i->town = t;
|
||||
@@ -1669,19 +1689,6 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
||||
i->random_colour = GB(r, 0, 4);
|
||||
i->counter = GB(r, 4, 12);
|
||||
i->random = initial_random_bits;
|
||||
i->produced_cargo_waiting[0] = 0;
|
||||
i->produced_cargo_waiting[1] = 0;
|
||||
i->incoming_cargo_waiting[0] = 0;
|
||||
i->incoming_cargo_waiting[1] = 0;
|
||||
i->incoming_cargo_waiting[2] = 0;
|
||||
i->this_month_production[0] = 0;
|
||||
i->this_month_production[1] = 0;
|
||||
i->this_month_transported[0] = 0;
|
||||
i->this_month_transported[1] = 0;
|
||||
i->last_month_pct_transported[0] = 0;
|
||||
i->last_month_pct_transported[1] = 0;
|
||||
i->last_month_transported[0] = 0;
|
||||
i->last_month_transported[1] = 0;
|
||||
i->was_cargo_delivered = false;
|
||||
i->last_prod_year = _cur_year;
|
||||
i->founder = founder;
|
||||
@@ -1712,10 +1719,9 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
||||
}
|
||||
|
||||
if (_generating_world) {
|
||||
i->last_month_production[0] = i->production_rate[0] * 8;
|
||||
i->last_month_production[1] = i->production_rate[1] * 8;
|
||||
} else {
|
||||
i->last_month_production[0] = i->last_month_production[1] = 0;
|
||||
for (size_t ci = 0; ci < lengthof(i->last_month_production); ci++) {
|
||||
i->last_month_production[ci] = i->production_rate[ci] * 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasBit(indspec->callback_mask, CBM_IND_DECIDE_COLOUR)) {
|
||||
@@ -1727,28 +1733,56 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
||||
}
|
||||
|
||||
if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) {
|
||||
/* Clear all input cargo types */
|
||||
for (uint j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID;
|
||||
for (uint j = 0; j < lengthof(i->accepts_cargo); j++) {
|
||||
/* Query actual types */
|
||||
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->accepts_cargo) : 3;
|
||||
for (uint j = 0; j < maxcargoes; j++) {
|
||||
uint16 res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
|
||||
if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
|
||||
if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) {
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
i->accepts_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
||||
CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
||||
if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) {
|
||||
/* Cargo not in spec, error in NewGRF */
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
if (std::find(i->accepts_cargo, i->accepts_cargo + j, cargo) != i->accepts_cargo + j) {
|
||||
/* Duplicate cargo */
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
i->accepts_cargo[j] = cargo;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) {
|
||||
/* Clear all output cargo types */
|
||||
for (uint j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID;
|
||||
for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
|
||||
/* Query actual types */
|
||||
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->produced_cargo) : 2;
|
||||
for (uint j = 0; j < maxcargoes; j++) {
|
||||
uint16 res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
|
||||
if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
|
||||
if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) {
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
i->produced_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
||||
CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
||||
if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) {
|
||||
/* Cargo not in spec, error in NewGRF */
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
if (std::find(i->produced_cargo, i->produced_cargo + j, cargo) != i->produced_cargo + j) {
|
||||
/* Duplicate cargo */
|
||||
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
|
||||
break;
|
||||
}
|
||||
i->produced_cargo[j] = cargo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1793,11 +1827,11 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
||||
* @param flags of operations to conduct
|
||||
* @param indspec pointer to industry specifications
|
||||
* @param itspec_index the index of the itsepc to build/fund
|
||||
* @param seed random seed (possibly) used by industries
|
||||
* @param initial_random_bits The random bits the industry is going to have after construction.
|
||||
* @param random_var8f random seed (possibly) used by industries
|
||||
* @param random_initial_bits The random bits the industry is going to have after construction.
|
||||
* @param founder Founder of the industry
|
||||
* @param creation_type The circumstances the industry is created under.
|
||||
* @param [out] ip Pointer to store newly created industry.
|
||||
* @param[out] ip Pointer to store newly created industry.
|
||||
* @return Succeeded or failed command.
|
||||
*
|
||||
* @post \c *ip contains the newly created industry if all checks are successful and the \a flags request actual creation, else it contains \c NULL afterwards.
|
||||
@@ -1961,7 +1995,7 @@ static Industry *CreateNewIndustry(TileIndex tile, IndustryType type, IndustryAv
|
||||
/**
|
||||
* Compute the appearance probability for an industry during map creation.
|
||||
* @param it Industry type to compute.
|
||||
* @param [out] force_at_least_one Returns whether at least one instance should be forced on map creation.
|
||||
* @param[out] force_at_least_one Returns whether at least one instance should be forced on map creation.
|
||||
* @return Relative probability for the industry to appear.
|
||||
*/
|
||||
static uint32 GetScaledIndustryGenerationProbability(IndustryType it, bool *force_at_least_one)
|
||||
@@ -1986,7 +2020,7 @@ static uint32 GetScaledIndustryGenerationProbability(IndustryType it, bool *forc
|
||||
/**
|
||||
* Compute the probability for constructing a new industry during game play.
|
||||
* @param it Industry type to compute.
|
||||
* @param [out] min_number Minimal number of industries that should exist at the map.
|
||||
* @param[out] min_number Minimal number of industries that should exist at the map.
|
||||
* @return Relative probability for the industry to appear.
|
||||
*/
|
||||
static uint16 GetIndustryGamePlayProbability(IndustryType it, byte *min_number)
|
||||
@@ -2194,8 +2228,9 @@ void Industry::RecomputeProductionMultipliers()
|
||||
assert(!indspec->UsesSmoothEconomy());
|
||||
|
||||
/* Rates are rounded up, so e.g. oilrig always produces some passengers */
|
||||
this->production_rate[0] = min(CeilDiv(indspec->production_rate[0] * this->prod_level, PRODLEVEL_DEFAULT), 0xFF);
|
||||
this->production_rate[1] = min(CeilDiv(indspec->production_rate[1] * this->prod_level, PRODLEVEL_DEFAULT), 0xFF);
|
||||
for (size_t i = 0; i < lengthof(this->production_rate); i++) {
|
||||
this->production_rate[i] = min(CeilDiv(indspec->production_rate[i] * this->prod_level, PRODLEVEL_DEFAULT), 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -74,12 +74,7 @@ static void ShowIndustryCargoesWindow(IndustryType id);
|
||||
|
||||
/**
|
||||
* Gets the string to display after the cargo name (using callback 37)
|
||||
* @param cargo the cargo for which the suffix is requested
|
||||
* - 00 - first accepted cargo type
|
||||
* - 01 - second accepted cargo type
|
||||
* - 02 - third accepted cargo type
|
||||
* - 03 - first produced cargo type
|
||||
* - 04 - second produced cargo type
|
||||
* @param cargo the cargo for which the suffix is requested, meaning depends on presence of flag 18 in prop 1A
|
||||
* @param cst the cargo suffix type (for which window is it requested). @see CargoSuffixType
|
||||
* @param ind the industry (NULL if in fund window)
|
||||
* @param ind_type the industry type
|
||||
@@ -134,9 +129,14 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind,
|
||||
}
|
||||
}
|
||||
|
||||
enum CargoSuffixInOut {
|
||||
CARGOSUFFIX_OUT = 0,
|
||||
CARGOSUFFIX_IN = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets all strings to display after the cargoes of industries (using callback 37)
|
||||
* @param cb_offset The offset for the cargo used in cb37, 0 for accepted cargoes, 3 for produced cargoes
|
||||
* @param use_input get suffixes for output cargoes or input cargoes?
|
||||
* @param cst the cargo suffix type (for which window is it requested). @see CargoSuffixType
|
||||
* @param ind the industry (NULL if in fund window)
|
||||
* @param ind_type the industry type
|
||||
@@ -145,14 +145,40 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind,
|
||||
* @param suffixes is filled with the suffixes
|
||||
*/
|
||||
template <typename TC, typename TS>
|
||||
static inline void GetAllCargoSuffixes(uint cb_offset, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
|
||||
static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
|
||||
{
|
||||
assert_compile(lengthof(cargoes) <= lengthof(suffixes));
|
||||
for (uint j = 0; j < lengthof(cargoes); j++) {
|
||||
if (cargoes[j] != CT_INVALID) {
|
||||
GetCargoSuffix(cb_offset + j, cst, ind, ind_type, indspec, suffixes[j]);
|
||||
} else {
|
||||
|
||||
if (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) {
|
||||
/* Reworked behaviour with new many-in-many-out scheme */
|
||||
for (uint j = 0; j < lengthof(suffixes); j++) {
|
||||
if (cargoes[j] != CT_INVALID) {
|
||||
byte local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
|
||||
uint cargotype = local_id << 16 | use_input;
|
||||
GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
|
||||
} else {
|
||||
suffixes[j].text[0] = '\0';
|
||||
suffixes[j].display = CSD_CARGO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Compatible behaviour with old 3-in-2-out scheme */
|
||||
for (uint j = 0; j < lengthof(suffixes); j++) {
|
||||
suffixes[j].text[0] = '\0';
|
||||
suffixes[j].display = CSD_CARGO;
|
||||
}
|
||||
switch (use_input) {
|
||||
case CARGOSUFFIX_OUT:
|
||||
if (cargoes[0] != CT_INVALID) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
|
||||
if (cargoes[1] != CT_INVALID) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
|
||||
break;
|
||||
case CARGOSUFFIX_IN:
|
||||
if (cargoes[0] != CT_INVALID) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
|
||||
if (cargoes[1] != CT_INVALID) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
|
||||
if (cargoes[2] != CT_INVALID) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,8 +384,8 @@ public:
|
||||
|
||||
const IndustrySpec *indsp = GetIndustrySpec(this->index[i]);
|
||||
|
||||
CargoSuffix cargo_suffix[3];
|
||||
GetAllCargoSuffixes(0, CST_FUND, NULL, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
|
||||
CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, NULL, this->index[i], indsp, indsp->accepts_cargo, cargo_suffix);
|
||||
StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
|
||||
byte p = 0;
|
||||
SetDParam(0, STR_JUST_NOTHING);
|
||||
@@ -373,7 +399,7 @@ public:
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
|
||||
/* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
|
||||
GetAllCargoSuffixes(3, CST_FUND, NULL, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, NULL, this->index[i], indsp, indsp->produced_cargo, cargo_suffix);
|
||||
str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
|
||||
p = 0;
|
||||
SetDParam(0, STR_JUST_NOTHING);
|
||||
@@ -477,8 +503,8 @@ public:
|
||||
}
|
||||
|
||||
/* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */
|
||||
CargoSuffix cargo_suffix[3];
|
||||
GetAllCargoSuffixes(0, CST_FUND, NULL, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
|
||||
CargoSuffix cargo_suffix[lengthof(indsp->accepts_cargo)];
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, NULL, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix);
|
||||
StringID str = STR_INDUSTRY_VIEW_REQUIRES_CARGO;
|
||||
byte p = 0;
|
||||
SetDParam(0, STR_JUST_NOTHING);
|
||||
@@ -493,7 +519,7 @@ public:
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
/* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */
|
||||
GetAllCargoSuffixes(3, CST_FUND, NULL, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, NULL, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix);
|
||||
str = STR_INDUSTRY_VIEW_PRODUCES_CARGO;
|
||||
p = 0;
|
||||
SetDParam(0, STR_JUST_NOTHING);
|
||||
@@ -508,7 +534,6 @@ public:
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
/* Get the additional purchase info text, if it has not already been queried. */
|
||||
str = STR_NULL;
|
||||
if (HasBit(indsp->callback_mask, CBM_IND_FUND_MORE_TEXT)) {
|
||||
uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
|
||||
if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
|
||||
@@ -684,8 +709,15 @@ static void UpdateIndustryProduction(Industry *i);
|
||||
static inline bool IsProductionAlterable(const Industry *i)
|
||||
{
|
||||
const IndustrySpec *is = GetIndustrySpec(i->type);
|
||||
bool has_prod = false;
|
||||
for (size_t j = 0; j < lengthof(is->production_rate); j++) {
|
||||
if (is->production_rate[j] != 0) {
|
||||
has_prod = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
|
||||
(is->production_rate[0] != 0 || is->production_rate[1] != 0 || is->IsRawIndustry()) &&
|
||||
(has_prod || is->IsRawIndustry()) &&
|
||||
!_networking);
|
||||
}
|
||||
|
||||
@@ -764,8 +796,8 @@ public:
|
||||
y += 2 * FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
|
||||
CargoSuffix cargo_suffix[3];
|
||||
GetAllCargoSuffixes(0, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
|
||||
CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
|
||||
bool stockpiling = HasBit(ind->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_mask, CBM_IND_PRODUCTION_256_TICKS);
|
||||
|
||||
uint left_side = left + WD_FRAMERECT_LEFT * 4; // Indent accepted cargoes.
|
||||
@@ -804,7 +836,7 @@ public:
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
|
||||
GetAllCargoSuffixes(3, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
|
||||
first = true;
|
||||
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
|
||||
if (i->produced_cargo[j] == CT_INVALID) continue;
|
||||
@@ -1256,7 +1288,7 @@ protected:
|
||||
SetDParam(p++, i->index);
|
||||
|
||||
static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
|
||||
GetAllCargoSuffixes(3, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
|
||||
GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
|
||||
|
||||
/* Industry productions */
|
||||
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
|
||||
@@ -1515,7 +1547,7 @@ enum CargoesFieldType {
|
||||
CFT_HEADER, ///< Header text.
|
||||
};
|
||||
|
||||
static const uint MAX_CARGOES = 3; ///< Maximum number of cargoes carried in a #CFT_CARGO field in #CargoesField.
|
||||
static const uint MAX_CARGOES = 16; ///< Maximum number of cargoes carried in a #CFT_CARGO field in #CargoesField.
|
||||
|
||||
/** Data about a single field in the #IndustryCargoesWindow panel. */
|
||||
struct CargoesField {
|
||||
@@ -1523,7 +1555,6 @@ struct CargoesField {
|
||||
static const int HOR_CARGO_BORDER_SPACE;
|
||||
static const int CARGO_STUB_WIDTH;
|
||||
static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE;
|
||||
static const int CARGO_FIELD_WIDTH;
|
||||
static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE;
|
||||
static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT;
|
||||
|
||||
@@ -1531,7 +1562,9 @@ struct CargoesField {
|
||||
static const int CARGO_LINE_COLOUR;
|
||||
|
||||
static int small_height, normal_height;
|
||||
static int cargo_field_width;
|
||||
static int industry_width;
|
||||
static uint max_cargoes;
|
||||
|
||||
CargoesFieldType type; ///< Type of field.
|
||||
union {
|
||||
@@ -1580,7 +1613,7 @@ struct CargoesField {
|
||||
/**
|
||||
* Connect a cargo from an industry to the #CFT_CARGO column.
|
||||
* @param cargo Cargo to connect.
|
||||
* @param produced Cargo is produced (if \c false, cargo is assumed to be accepted).
|
||||
* @param producer Cargo is produced (if \c false, cargo is assumed to be accepted).
|
||||
* @return Horizontal connection index, or \c -1 if not accepted at all.
|
||||
*/
|
||||
int ConnectCargo(CargoID cargo, bool producer)
|
||||
@@ -1684,20 +1717,19 @@ struct CargoesField {
|
||||
int GetCargoBase(int xpos) const
|
||||
{
|
||||
assert(this->type == CFT_CARGO);
|
||||
int n = this->u.cargo.num_cargoes;
|
||||
|
||||
switch (this->u.cargo.num_cargoes) {
|
||||
case 0: return xpos + CARGO_FIELD_WIDTH / 2;
|
||||
case 1: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH / 2;
|
||||
case 2: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE / 2;
|
||||
case 3: return xpos + CARGO_FIELD_WIDTH / 2 - HOR_CARGO_WIDTH - HOR_CARGO_SPACE - HOR_CARGO_WIDTH / 2;
|
||||
default: NOT_REACHED();
|
||||
if (n % 2 == 0) {
|
||||
return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2);
|
||||
} else {
|
||||
return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the field.
|
||||
* @param xpos Position of the left edge.
|
||||
* @param vpos Position of the top edge.
|
||||
* @param ypos Position of the top edge.
|
||||
*/
|
||||
void Draw(int xpos, int ypos) const
|
||||
{
|
||||
@@ -1749,7 +1781,7 @@ struct CargoesField {
|
||||
other_left = this->u.industry.other_accepted;
|
||||
}
|
||||
ypos1 += VERT_CARGO_EDGE;
|
||||
for (uint i = 0; i < MAX_CARGOES; i++) {
|
||||
for (uint i = 0; i < CargoesField::max_cargoes; i++) {
|
||||
if (other_right[i] != INVALID_CARGO) {
|
||||
const CargoSpec *csp = CargoSpec::Get(other_right[i]);
|
||||
int xp = xpos + industry_width + CARGO_STUB_WIDTH;
|
||||
@@ -1814,7 +1846,7 @@ struct CargoesField {
|
||||
DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp);
|
||||
dx = 1;
|
||||
}
|
||||
DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CARGO_FIELD_WIDTH - 1, ypos, csp);
|
||||
DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
|
||||
}
|
||||
ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE;
|
||||
}
|
||||
@@ -1936,9 +1968,11 @@ private:
|
||||
assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo));
|
||||
assert_compile(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo));
|
||||
|
||||
int CargoesField::small_height; ///< Height of the header row.
|
||||
int CargoesField::normal_height; ///< Height of the non-header rows.
|
||||
int CargoesField::industry_width; ///< Width of an industry field.
|
||||
int CargoesField::small_height; ///< Height of the header row.
|
||||
int CargoesField::normal_height; ///< Height of the non-header rows.
|
||||
int CargoesField::industry_width; ///< Width of an industry field.
|
||||
int CargoesField::cargo_field_width; ///< Width of a cargo field.
|
||||
uint CargoesField::max_cargoes; ///< Largest number of cargoes actually on any industry.
|
||||
const int CargoesField::VERT_INTER_INDUSTRY_SPACE = 6; ///< Amount of space between two industries in a column.
|
||||
|
||||
const int CargoesField::HOR_CARGO_BORDER_SPACE = 15; ///< Amount of space between the left/right edge of a #CFT_CARGO field, and the left/right most vertical cargo.
|
||||
@@ -1952,9 +1986,6 @@ const int CargoesField::BLOB_DISTANCE = 5; ///< Distance of the industry legend
|
||||
const int CargoesField::BLOB_WIDTH = 12; ///< Width of the industry legend colour, including border.
|
||||
const int CargoesField::BLOB_HEIGHT = 9; ///< Height of the industry legend colour, including border
|
||||
|
||||
/** Width of a #CFT_CARGO field. */
|
||||
const int CargoesField::CARGO_FIELD_WIDTH = HOR_CARGO_BORDER_SPACE * 2 + HOR_CARGO_WIDTH * MAX_CARGOES + HOR_CARGO_SPACE * (MAX_CARGOES - 1);
|
||||
|
||||
const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box.
|
||||
const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo.
|
||||
|
||||
@@ -1979,13 +2010,14 @@ struct CargoesRow {
|
||||
int other_count = 0;
|
||||
|
||||
const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
|
||||
for (uint i = 0; i < lengthof(indsp->produced_cargo); i++) {
|
||||
assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
|
||||
for (uint i = 0; i < CargoesField::max_cargoes; i++) {
|
||||
int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
|
||||
if (col < 0) others[other_count++] = indsp->produced_cargo[i];
|
||||
}
|
||||
|
||||
/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
|
||||
for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
|
||||
for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
|
||||
if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
|
||||
}
|
||||
} else {
|
||||
@@ -2036,13 +2068,14 @@ struct CargoesRow {
|
||||
int other_count = 0;
|
||||
|
||||
const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
|
||||
for (uint i = 0; i < lengthof(indsp->accepts_cargo); i++) {
|
||||
assert(CargoesField::max_cargoes <= lengthof(indsp->accepts_cargo));
|
||||
for (uint i = 0; i < CargoesField::max_cargoes; i++) {
|
||||
int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
|
||||
if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
|
||||
}
|
||||
|
||||
/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
|
||||
for (uint i = 0; i < MAX_CARGOES && other_count > 0; i++) {
|
||||
for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
|
||||
if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
|
||||
}
|
||||
} else {
|
||||
@@ -2125,10 +2158,13 @@ struct IndustryCargoesWindow : public Window {
|
||||
/* Decide about the size of the box holding the text of an industry type. */
|
||||
this->ind_textsize.width = 0;
|
||||
this->ind_textsize.height = 0;
|
||||
CargoesField::max_cargoes = 0;
|
||||
for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
|
||||
const IndustrySpec *indsp = GetIndustrySpec(it);
|
||||
if (!indsp->enabled) continue;
|
||||
this->ind_textsize = maxdim(this->ind_textsize, GetStringBoundingBox(indsp->name));
|
||||
CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->accepts_cargo, endof(indsp->accepts_cargo), IsCargoIDValid));
|
||||
CargoesField::max_cargoes = max<uint>(CargoesField::max_cargoes, std::count_if(indsp->produced_cargo, endof(indsp->produced_cargo), IsCargoIDValid));
|
||||
}
|
||||
d.width = max(d.width, this->ind_textsize.width);
|
||||
d.height = this->ind_textsize.height;
|
||||
@@ -2147,18 +2183,21 @@ struct IndustryCargoesWindow : public Window {
|
||||
|
||||
d.width += 2 * HOR_TEXT_PADDING;
|
||||
/* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */
|
||||
uint min_ind_height = CargoesField::VERT_CARGO_EDGE * 2 + MAX_CARGOES * FONT_HEIGHT_NORMAL + (MAX_CARGOES - 1) * CargoesField::VERT_CARGO_SPACE;
|
||||
uint min_ind_height = CargoesField::VERT_CARGO_EDGE * 2 + CargoesField::max_cargoes * FONT_HEIGHT_NORMAL + (CargoesField::max_cargoes - 1) * CargoesField::VERT_CARGO_SPACE;
|
||||
d.height = max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height);
|
||||
|
||||
CargoesField::industry_width = d.width;
|
||||
CargoesField::normal_height = d.height + CargoesField::VERT_INTER_INDUSTRY_SPACE;
|
||||
|
||||
/* Width of a #CFT_CARGO field. */
|
||||
CargoesField::cargo_field_width = CargoesField::HOR_CARGO_BORDER_SPACE * 2 + CargoesField::HOR_CARGO_WIDTH * CargoesField::max_cargoes + CargoesField::HOR_CARGO_SPACE * (CargoesField::max_cargoes - 1);
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_IC_PANEL:
|
||||
size->width = WD_FRAMETEXT_LEFT + CargoesField::industry_width * 3 + CargoesField::CARGO_FIELD_WIDTH * 2 + WD_FRAMETEXT_RIGHT;
|
||||
size->width = WD_FRAMETEXT_LEFT + CargoesField::industry_width * 3 + CargoesField::cargo_field_width * 2 + WD_FRAMETEXT_RIGHT;
|
||||
break;
|
||||
|
||||
case WID_IC_IND_DROPDOWN:
|
||||
@@ -2519,7 +2558,7 @@ struct IndustryCargoesWindow : public Window {
|
||||
_cur_dpi = &tmp_dpi;
|
||||
|
||||
int left_pos = WD_FRAMERECT_LEFT;
|
||||
if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2;
|
||||
if (this->ind_cargo >= NUM_INDUSTRYTYPES) left_pos += (CargoesField::industry_width + CargoesField::cargo_field_width) / 2;
|
||||
int last_column = (this->ind_cargo < NUM_INDUSTRYTYPES) ? 4 : 2;
|
||||
|
||||
const NWidgetBase *nwp = this->GetWidget<NWidgetBase>(WID_IC_PANEL);
|
||||
@@ -2538,7 +2577,7 @@ struct IndustryCargoesWindow : public Window {
|
||||
}
|
||||
while (col >= 0 && col <= last_column) {
|
||||
this->fields[i].columns[col].Draw(xpos, vpos);
|
||||
xpos += (col & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width;
|
||||
xpos += (col & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width;
|
||||
col += dir;
|
||||
}
|
||||
}
|
||||
@@ -2570,11 +2609,11 @@ struct IndustryCargoesWindow : public Window {
|
||||
vpos = pt.y - vpos - row * CargoesField::normal_height; // Position in the row + 1 field
|
||||
row++; // rebase row to match index of this->fields.
|
||||
|
||||
int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::CARGO_FIELD_WIDTH) / 2);
|
||||
int xpos = 2 * WD_FRAMERECT_LEFT + ((this->ind_cargo < NUM_INDUSTRYTYPES) ? 0 : (CargoesField::industry_width + CargoesField::cargo_field_width) / 2);
|
||||
if (pt.x < xpos) return false;
|
||||
int column;
|
||||
for (column = 0; column <= 5; column++) {
|
||||
int width = (column & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width;
|
||||
int width = (column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width;
|
||||
if (pt.x < xpos + width) break;
|
||||
xpos += width;
|
||||
}
|
||||
@@ -2587,7 +2626,7 @@ struct IndustryCargoesWindow : public Window {
|
||||
xy->y = vpos;
|
||||
if (_current_text_dir == TD_RTL) {
|
||||
fieldxy->x = num_columns - column;
|
||||
xy->x = ((column & 1) ? CargoesField::CARGO_FIELD_WIDTH : CargoesField::industry_width) - xpos;
|
||||
xy->x = ((column & 1) ? CargoesField::cargo_field_width : CargoesField::industry_width) - xpos;
|
||||
} else {
|
||||
fieldxy->x = column;
|
||||
xy->x = xpos;
|
||||
|
@@ -37,6 +37,10 @@ static const IndustryGfx INVALID_INDUSTRYTILE = NUM_INDUSTRYTILES; ///< one a
|
||||
|
||||
static const int INDUSTRY_COMPLETED = 3; ///< final stage of industry construction.
|
||||
|
||||
static const int INDUSTRY_NUM_INPUTS = 16; ///< Number of cargo types an industry can accept
|
||||
static const int INDUSTRY_NUM_OUTPUTS = 16; ///< Number of cargo types an industry can produce
|
||||
|
||||
|
||||
void CheckIndustries();
|
||||
|
||||
#endif /* INDUSTRY_TYPE_H */
|
||||
|
@@ -80,6 +80,7 @@ enum IndustryBehaviour {
|
||||
INDUSTRYBEH_PRODCALLBACK_RANDOM = 1 << 15, ///< Production callback needs random bits in var 10
|
||||
INDUSTRYBEH_NOBUILT_MAPCREATION = 1 << 16, ///< Do not force one instance of this type to appear on map generation
|
||||
INDUSTRYBEH_CANCLOSE_LASTINSTANCE = 1 << 17, ///< Allow closing down the last instance of this type
|
||||
INDUSTRYBEH_CARGOTYPES_UNLIMITED = 1 << 18, ///< Allow produced/accepted cargoes callbacks to supply more than 2 and 3 types
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(IndustryBehaviour)
|
||||
|
||||
@@ -87,6 +88,7 @@ DECLARE_ENUM_AS_BIT_SET(IndustryBehaviour)
|
||||
enum IndustryTileSpecialFlags {
|
||||
INDTILE_SPECIAL_NONE = 0,
|
||||
INDTILE_SPECIAL_NEXTFRAME_RANDOMBITS = 1 << 0, ///< Callback 0x26 needs random bits
|
||||
INDTILE_SPECIAL_ACCEPTS_ALL_CARGO = 1 << 1, ///< Tile always accepts all cargoes the associated industry accepts
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(IndustryTileSpecialFlags)
|
||||
|
||||
@@ -99,41 +101,41 @@ struct IndustryTileTable {
|
||||
* Defines the data structure for constructing industry.
|
||||
*/
|
||||
struct IndustrySpec {
|
||||
const IndustryTileTable * const *table;///< List of the tiles composing the industry
|
||||
byte num_table; ///< Number of elements in the table
|
||||
uint8 cost_multiplier; ///< Base construction cost multiplier.
|
||||
uint32 removal_cost_multiplier; ///< Base removal cost multiplier.
|
||||
uint32 prospecting_chance; ///< Chance prospecting succeeds
|
||||
IndustryType conflicting[3]; ///< Industries this industry cannot be close to
|
||||
byte check_proc; ///< Index to a procedure to check for conflicting circumstances
|
||||
CargoID produced_cargo[2];
|
||||
byte production_rate[2];
|
||||
const IndustryTileTable * const *table; ///< List of the tiles composing the industry
|
||||
byte num_table; ///< Number of elements in the table
|
||||
uint8 cost_multiplier; ///< Base construction cost multiplier.
|
||||
uint32 removal_cost_multiplier; ///< Base removal cost multiplier.
|
||||
uint32 prospecting_chance; ///< Chance prospecting succeeds
|
||||
IndustryType conflicting[3]; ///< Industries this industry cannot be close to
|
||||
byte check_proc; ///< Index to a procedure to check for conflicting circumstances
|
||||
CargoID produced_cargo[INDUSTRY_NUM_OUTPUTS];
|
||||
byte production_rate[INDUSTRY_NUM_OUTPUTS];
|
||||
/**
|
||||
* minimum amount of cargo transported to the stations.
|
||||
* If the waiting cargo is less than this number, no cargo is moved to it.
|
||||
*/
|
||||
byte minimal_cargo;
|
||||
CargoID accepts_cargo[3]; ///< 3 accepted cargoes.
|
||||
uint16 input_cargo_multiplier[3][2]; ///< Input cargo multipliers (multiply amount of incoming cargo for the produced cargoes)
|
||||
IndustryLifeType life_type; ///< This is also known as Industry production flag, in newgrf specs
|
||||
byte climate_availability; ///< Bitmask, giving landscape enums as bit position
|
||||
IndustryBehaviour behaviour; ///< How this industry will behave, and how others entities can use it
|
||||
byte map_colour; ///< colour used for the small map
|
||||
StringID name; ///< Displayed name of the industry
|
||||
StringID new_industry_text; ///< Message appearing when the industry is built
|
||||
StringID closure_text; ///< Message appearing when the industry closes
|
||||
StringID production_up_text; ///< Message appearing when the industry's production is increasing
|
||||
StringID production_down_text; ///< Message appearing when the industry's production is decreasing
|
||||
StringID station_name; ///< Default name for nearby station
|
||||
byte appear_ingame[NUM_LANDSCAPE]; ///< Probability of appearance in game
|
||||
byte appear_creation[NUM_LANDSCAPE]; ///< Probability of appearance during map creation
|
||||
uint8 number_of_sounds; ///< Number of sounds available in the sounds array
|
||||
const uint8 *random_sounds; ///< array of random sounds.
|
||||
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]; ///< 16 accepted cargoes.
|
||||
uint16 input_cargo_multiplier[INDUSTRY_NUM_INPUTS][INDUSTRY_NUM_OUTPUTS]; ///< Input cargo multipliers (multiply amount of incoming cargo for the produced cargoes)
|
||||
IndustryLifeType life_type; ///< This is also known as Industry production flag, in newgrf specs
|
||||
byte climate_availability; ///< Bitmask, giving landscape enums as bit position
|
||||
IndustryBehaviour behaviour; ///< How this industry will behave, and how others entities can use it
|
||||
byte map_colour; ///< colour used for the small map
|
||||
StringID name; ///< Displayed name of the industry
|
||||
StringID new_industry_text; ///< Message appearing when the industry is built
|
||||
StringID closure_text; ///< Message appearing when the industry closes
|
||||
StringID production_up_text; ///< Message appearing when the industry's production is increasing
|
||||
StringID production_down_text; ///< Message appearing when the industry's production is decreasing
|
||||
StringID station_name; ///< Default name for nearby station
|
||||
byte appear_ingame[NUM_LANDSCAPE]; ///< Probability of appearance in game
|
||||
byte appear_creation[NUM_LANDSCAPE]; ///< Probability of appearance during map creation
|
||||
uint8 number_of_sounds; ///< Number of sounds available in the sounds array
|
||||
const uint8 *random_sounds; ///< array of random sounds.
|
||||
/* Newgrf data */
|
||||
uint16 callback_mask; ///< Bitmask of industry callbacks that have to be called
|
||||
uint8 cleanup_flag; ///< flags indicating which data should be freed upon cleaning up
|
||||
bool enabled; ///< entity still available (by default true).newgrf can disable it, though
|
||||
GRFFileProps grf_prop; ///< properties related to the grf file
|
||||
uint16 callback_mask; ///< Bitmask of industry callbacks that have to be called
|
||||
uint8 cleanup_flag; ///< flags indicating which data should be freed upon cleaning up
|
||||
bool enabled; ///< entity still available (by default true).newgrf can disable it, though
|
||||
GRFFileProps grf_prop; ///< properties related to the grf file
|
||||
|
||||
bool IsRawIndustry() const;
|
||||
bool IsProcessingIndustry() const;
|
||||
@@ -144,10 +146,11 @@ struct IndustrySpec {
|
||||
|
||||
/**
|
||||
* Defines the data structure of each individual tile of an industry.
|
||||
* @note A tile can at most accept 3 types of cargo, even if an industry as a whole can accept more types.
|
||||
*/
|
||||
struct IndustryTileSpec {
|
||||
CargoID accepts_cargo[3]; ///< Cargo accepted by this tile
|
||||
uint8 acceptance[3]; ///< Level of acceptance per cargo type
|
||||
CargoID accepts_cargo[INDUSTRY_NUM_INPUTS]; ///< Cargo accepted by this tile
|
||||
int8 acceptance[INDUSTRY_NUM_INPUTS]; ///< Level of acceptance per cargo type (signed, may be negative!)
|
||||
Slope slopes_refused; ///< slope pattern on which this tile cannot be built
|
||||
byte anim_production; ///< Animation frame to start when goods are produced
|
||||
byte anim_next; ///< Next frame in an animation
|
||||
|
@@ -19,7 +19,7 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
# include "core/mem_func.hpp"
|
||||
@@ -87,7 +87,7 @@ bool IniFile::SaveToDisk(const char *filename)
|
||||
fclose(f);
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#if defined(_WIN32)
|
||||
/* _tcsncpy = strcpy is TCHAR is char, but isn't when TCHAR is wchar. */
|
||||
#undef strncpy
|
||||
/* Allocate space for one more \0 character. */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user