Merge branch 'save_ext' into more_cond_orders-sx
# Conflicts: # src/lang/english.txt # src/order_type.h
This commit is contained in:
4
src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
vendored
4
src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
vendored
@@ -119,10 +119,10 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
|
||||
const SQChar *sErr = 0;
|
||||
if(sq_gettop(v)>=1) {
|
||||
if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) {
|
||||
pf(v,"\nAN ERROR HAS OCCURED [%s]\n",sErr);
|
||||
pf(v,"\nAN ERROR HAS OCCURRED [%s]\n",sErr);
|
||||
}
|
||||
else{
|
||||
pf(v,"\nAN ERROR HAS OCCURED [unknown]\n");
|
||||
pf(v,"\nAN ERROR HAS OCCURRED [unknown]\n");
|
||||
}
|
||||
sqstd_printcallstack(v);
|
||||
}
|
||||
|
||||
3
src/3rdparty/squirrel/squirrel/sqapi.cpp
vendored
3
src/3rdparty/squirrel/squirrel/sqapi.cpp
vendored
@@ -798,7 +798,8 @@ SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx)
|
||||
switch(type) {
|
||||
case OT_TABLE:
|
||||
if(type(mt) == OT_TABLE) {
|
||||
if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, "delagate cycle"); v->Pop();}
|
||||
if(!_table(self)->SetDelegate(_table(mt))) return sq_throwerror(v, "delagate cycle");
|
||||
v->Pop();}
|
||||
else if(type(mt)==OT_NULL) {
|
||||
_table(self)->SetDelegate(NULL); v->Pop(); }
|
||||
else return sq_aux_invalidtype(v,type);
|
||||
|
||||
4
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
4
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
@@ -105,7 +105,7 @@ void SQVM::Raise_IdxError(const SQObject &o)
|
||||
void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2)
|
||||
{
|
||||
SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2);
|
||||
Raise_Error("comparsion between '%.50s' and '%.50s'", _stringval(oval1), _stringval(oval2));
|
||||
Raise_Error("comparison between '%.50s' and '%.50s'", _stringval(oval1), _stringval(oval2));
|
||||
}
|
||||
|
||||
|
||||
@@ -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--;
|
||||
}
|
||||
|
||||
17
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
17
src/3rdparty/squirrel/squirrel/sqvm.cpp
vendored
@@ -215,7 +215,7 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result)
|
||||
_RET_SUCCEED(_integer(res))
|
||||
}
|
||||
}
|
||||
//continues through (no break needed)
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
_RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 );
|
||||
}
|
||||
@@ -287,6 +287,7 @@ void SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res)
|
||||
//else keeps going to the default
|
||||
}
|
||||
}
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
seprintf(buf, lastof(buf),"(%s : 0x%p)",GetTypeName(o),(void*)_rawval(o));
|
||||
}
|
||||
@@ -377,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 );
|
||||
@@ -539,7 +539,7 @@ bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr
|
||||
_generator(o1)->Resume(this, arg_2+1);
|
||||
_FINISH(0);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
Raise_Error("cannot iterate %s", GetTypeName(o1));
|
||||
}
|
||||
@@ -769,7 +769,7 @@ exception_restore:
|
||||
ct_stackbase = _stackbase;
|
||||
goto common_call;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
case _OP_CALL: {
|
||||
ct_tailcall = false;
|
||||
ct_target = arg0;
|
||||
@@ -1101,7 +1101,7 @@ exception_trap:
|
||||
_lasterror = currerror;
|
||||
return false;
|
||||
}
|
||||
assert(0);
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor)
|
||||
@@ -1158,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;
|
||||
@@ -1330,7 +1329,7 @@ bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//keeps going
|
||||
FALLTHROUGH;
|
||||
case OT_USERDATA:
|
||||
if(_delegable(self)->_delegate) {
|
||||
SQObjectPtr t;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "../widgets/dropdown_type.h"
|
||||
#include "../widgets/dropdown_func.h"
|
||||
#include "../hotkeys.h"
|
||||
#include "../core/geometry_func.hpp"
|
||||
|
||||
#include "ai.hpp"
|
||||
#include "ai_gui.hpp"
|
||||
@@ -305,7 +306,6 @@ struct AISettingsWindow : public Window {
|
||||
timeout(0)
|
||||
{
|
||||
this->ai_config = GetConfig(slot);
|
||||
this->RebuildVisibleSettings();
|
||||
|
||||
this->CreateNestedTree();
|
||||
this->vscroll = this->GetScrollbar(WID_AIS_SCROLLBAR);
|
||||
@@ -313,7 +313,7 @@ struct AISettingsWindow : public Window {
|
||||
|
||||
this->SetWidgetDisabledState(WID_AIS_RESET, _game_mode != GM_MENU && Company::IsValidID(this->slot));
|
||||
|
||||
this->vscroll->SetCount((int)this->visible_settings.size());
|
||||
this->RebuildVisibleSettings();
|
||||
}
|
||||
|
||||
virtual void SetStringParameters(int widget) const
|
||||
@@ -341,6 +341,8 @@ struct AISettingsWindow : public Window {
|
||||
visible_settings.push_back(&(*it));
|
||||
}
|
||||
}
|
||||
|
||||
this->vscroll->SetCount((int)this->visible_settings.size());
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
|
||||
@@ -530,21 +532,23 @@ struct AISettingsWindow : public Window {
|
||||
virtual void OnQueryTextFinished(char *str)
|
||||
{
|
||||
if (StrEmpty(str)) return;
|
||||
ScriptConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
|
||||
VisibleSettingsList::const_iterator it = this->visible_settings.begin();
|
||||
for (int i = 0; i < this->clicked_row; i++) it++;
|
||||
if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (it->flags & SCRIPTCONFIG_INGAME) == 0) return;
|
||||
const ScriptConfigItem config_item = **it;
|
||||
if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
|
||||
int32 value = atoi(str);
|
||||
this->ai_config->SetSetting((*it).name, value);
|
||||
this->ai_config->SetSetting(config_item.name, value);
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
virtual void OnDropdownSelect(int widget, int index)
|
||||
{
|
||||
assert(this->clicked_dropdown);
|
||||
ScriptConfigItemList::const_iterator it = this->ai_config->GetConfigList()->begin();
|
||||
VisibleSettingsList::const_iterator it = this->visible_settings.begin();
|
||||
for (int i = 0; i < this->clicked_row; i++) it++;
|
||||
if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (it->flags & SCRIPTCONFIG_INGAME) == 0) return;
|
||||
this->ai_config->SetSetting((*it).name, index);
|
||||
const ScriptConfigItem config_item = **it;
|
||||
if (_game_mode == GM_NORMAL && ((this->slot == OWNER_DEITY) || Company::IsValidID(this->slot)) && (config_item.flags & SCRIPTCONFIG_INGAME) == 0) return;
|
||||
this->ai_config->SetSetting(config_item.name, index);
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
@@ -766,6 +770,22 @@ struct AIConfigWindow : public Window {
|
||||
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
|
||||
size->height = 8 * this->line_height;
|
||||
break;
|
||||
|
||||
case WID_AIC_CHANGE: {
|
||||
SetDParam(0, STR_AI_CONFIG_CHANGE_GAMESCRIPT);
|
||||
Dimension dim = GetStringBoundingBox(STR_AI_CONFIG_CHANGE);
|
||||
|
||||
SetDParam(0, STR_AI_CONFIG_CHANGE_NONE);
|
||||
dim = maxdim(dim, GetStringBoundingBox(STR_AI_CONFIG_CHANGE));
|
||||
|
||||
SetDParam(0, STR_AI_CONFIG_CHANGE_AI);
|
||||
dim = maxdim(dim, GetStringBoundingBox(STR_AI_CONFIG_CHANGE));
|
||||
|
||||
dim.width += padding.width;
|
||||
dim.height += padding.height;
|
||||
*size = maxdim(*size, dim);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,13 @@ static bool CheckAPIVersion(const char *api_version)
|
||||
{
|
||||
return strcmp(api_version, "0.7") == 0 || strcmp(api_version, "1.0") == 0 || strcmp(api_version, "1.1") == 0 ||
|
||||
strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 ||
|
||||
strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0;
|
||||
strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0 || strcmp(api_version, "1.7") == 0 ||
|
||||
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)
|
||||
|
||||
@@ -114,7 +114,7 @@ AIInfo *AIScannerInfo::FindInfo(const char *nameParam, int versionParam, bool fo
|
||||
*e = '\0';
|
||||
e++;
|
||||
versionParam = atoi(e);
|
||||
/* FALL THROUGH, like we were calling this function with a version. */
|
||||
/* Continue, like we were calling this function with a version. */
|
||||
}
|
||||
|
||||
if (force_exact_match) {
|
||||
|
||||
@@ -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,10 +92,10 @@ 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(); }
|
||||
SpriteID GetImage(Direction direction, EngineImageType image_type) const;
|
||||
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const;
|
||||
int GetDisplaySpeed() const { return this->cur_speed; }
|
||||
int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed; }
|
||||
int GetSpeedOldUnits() const { return this->vcache.cached_max_speed * 10 / 128; }
|
||||
@@ -137,11 +139,11 @@ 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)
|
||||
|
||||
SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type);
|
||||
void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result);
|
||||
|
||||
Station *GetTargetAirportIfValid(const Aircraft *v);
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -152,64 +153,69 @@ static StationID FindNearestHangar(const Aircraft *v)
|
||||
return index;
|
||||
}
|
||||
|
||||
SpriteID Aircraft::GetImage(Direction direction, EngineImageType image_type) const
|
||||
void Aircraft::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
|
||||
{
|
||||
uint8 spritenum = this->spritenum;
|
||||
|
||||
if (is_custom_sprite(spritenum)) {
|
||||
SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type);
|
||||
if (sprite != 0) return sprite;
|
||||
GetCustomVehicleSprite(this, direction, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
|
||||
spritenum = this->GetEngine()->original_image_index;
|
||||
}
|
||||
|
||||
assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
|
||||
return direction + _aircraft_sprite[spritenum];
|
||||
result->Set(direction + _aircraft_sprite[spritenum]);
|
||||
}
|
||||
|
||||
SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type)
|
||||
void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result)
|
||||
{
|
||||
assert(v->subtype == AIR_HELICOPTER);
|
||||
|
||||
const Aircraft *w = v->Next()->Next();
|
||||
if (is_custom_sprite(v->spritenum)) {
|
||||
SpriteID sprite = GetCustomRotorSprite(v, false, image_type);
|
||||
if (sprite != 0) return sprite;
|
||||
GetCustomRotorSprite(v, false, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
}
|
||||
|
||||
/* Return standard rotor sprites if there are no custom sprites for this helicopter */
|
||||
return SPR_ROTOR_STOPPED + w->state;
|
||||
result->Set(SPR_ROTOR_STOPPED + w->state);
|
||||
}
|
||||
|
||||
static SpriteID GetAircraftIcon(EngineID engine, EngineImageType image_type)
|
||||
static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine);
|
||||
uint8 spritenum = e->u.air.image_index;
|
||||
|
||||
if (is_custom_sprite(spritenum)) {
|
||||
SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
|
||||
if (sprite != 0) return sprite;
|
||||
GetCustomVehicleIcon(engine, DIR_W, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
|
||||
spritenum = e->original_image_index;
|
||||
}
|
||||
|
||||
assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
|
||||
return DIR_W + _aircraft_sprite[spritenum];
|
||||
result->Set(DIR_W + _aircraft_sprite[spritenum]);
|
||||
}
|
||||
|
||||
void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
|
||||
{
|
||||
SpriteID sprite = GetAircraftIcon(engine, image_type);
|
||||
const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
|
||||
VehicleSpriteSeq seq;
|
||||
GetAircraftIcon(engine, image_type, &seq);
|
||||
|
||||
Rect rect;
|
||||
seq.GetBounds(&rect);
|
||||
preferred_x = Clamp(preferred_x,
|
||||
left - UnScaleGUI(real_sprite->x_offs),
|
||||
right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs));
|
||||
DrawSprite(sprite, pal, preferred_x, y);
|
||||
left - UnScaleGUI(rect.left),
|
||||
right - UnScaleGUI(rect.right));
|
||||
|
||||
seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
|
||||
|
||||
if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
|
||||
SpriteID rotor_sprite = GetCustomRotorIcon(engine, image_type);
|
||||
if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
|
||||
DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - ScaleGUITrad(5));
|
||||
VehicleSpriteSeq rotor_seq;
|
||||
GetCustomRotorIcon(engine, image_type, &rotor_seq);
|
||||
if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
|
||||
rotor_seq.Draw(preferred_x, y - ScaleGUITrad(5), PAL_NONE, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,12 +230,16 @@ void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID en
|
||||
*/
|
||||
void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
|
||||
{
|
||||
const Sprite *spr = GetSprite(GetAircraftIcon(engine, image_type), ST_NORMAL);
|
||||
VehicleSpriteSeq seq;
|
||||
GetAircraftIcon(engine, image_type, &seq);
|
||||
|
||||
width = UnScaleGUI(spr->width);
|
||||
height = UnScaleGUI(spr->height);
|
||||
xoffs = UnScaleGUI(spr->x_offs);
|
||||
yoffs = UnScaleGUI(spr->y_offs);
|
||||
Rect rect;
|
||||
seq.GetBounds(&rect);
|
||||
|
||||
width = UnScaleGUI(rect.right - rect.left + 1);
|
||||
height = UnScaleGUI(rect.bottom - rect.top + 1);
|
||||
xoffs = UnScaleGUI(rect.left);
|
||||
yoffs = UnScaleGUI(rect.top);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,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)
|
||||
@@ -294,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;
|
||||
@@ -317,7 +327,8 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
|
||||
v->date_of_last_service = _date;
|
||||
v->build_year = u->build_year = _cur_year;
|
||||
|
||||
v->cur_image = u->cur_image = SPR_IMG_QUERY;
|
||||
v->sprite_seq.Set(SPR_IMG_QUERY);
|
||||
u->sprite_seq.Set(SPR_IMG_QUERY);
|
||||
|
||||
v->random_bits = VehicleRandomBits();
|
||||
u->random_bits = VehicleRandomBits();
|
||||
@@ -349,11 +360,11 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
|
||||
w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
|
||||
w->spritenum = 0xFF;
|
||||
w->subtype = AIR_ROTOR;
|
||||
w->cur_image = SPR_ROTOR_STOPPED;
|
||||
w->sprite_seq.Set(SPR_ROTOR_STOPPED);
|
||||
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();
|
||||
@@ -468,21 +479,21 @@ static void HelicopterTickHandler(Aircraft *v)
|
||||
int tick = ++u->tick_counter;
|
||||
int spd = u->cur_speed >> 4;
|
||||
|
||||
SpriteID img;
|
||||
VehicleSpriteSeq seq;
|
||||
if (spd == 0) {
|
||||
u->state = HRS_ROTOR_STOPPED;
|
||||
img = GetRotorImage(v, EIT_ON_MAP);
|
||||
if (u->cur_image == img) return;
|
||||
GetRotorImage(v, EIT_ON_MAP, &seq);
|
||||
if (u->sprite_seq == seq) return;
|
||||
} else if (tick >= spd) {
|
||||
u->tick_counter = 0;
|
||||
u->state++;
|
||||
if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
|
||||
img = GetRotorImage(v, EIT_ON_MAP);
|
||||
GetRotorImage(v, EIT_ON_MAP, &seq);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
u->cur_image = img;
|
||||
u->sprite_seq = seq;
|
||||
|
||||
u->UpdatePositionAndViewport();
|
||||
}
|
||||
@@ -502,7 +513,9 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z)
|
||||
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, false);
|
||||
if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
|
||||
if (v->subtype == AIR_HELICOPTER) {
|
||||
GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_seq);
|
||||
}
|
||||
|
||||
Aircraft *u = v->Next();
|
||||
|
||||
@@ -513,7 +526,7 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z)
|
||||
|
||||
safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
|
||||
u->z_pos = GetSlopePixelZ(safe_x, safe_y);
|
||||
u->cur_image = v->cur_image;
|
||||
u->sprite_seq.CopyWithoutPalette(v->sprite_seq); // the shadow is never coloured
|
||||
|
||||
u->UpdatePositionAndViewport();
|
||||
|
||||
@@ -622,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;
|
||||
@@ -641,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;
|
||||
@@ -681,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)
|
||||
{
|
||||
@@ -715,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.
|
||||
@@ -737,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);
|
||||
|
||||
@@ -902,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
|
||||
@@ -922,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);
|
||||
@@ -1237,7 +1258,9 @@ void Aircraft::MarkDirty()
|
||||
{
|
||||
this->colourmap = PAL_NONE;
|
||||
this->UpdateViewport(true, false);
|
||||
if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
|
||||
if (this->subtype == AIR_HELICOPTER) {
|
||||
GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_seq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1347,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);
|
||||
@@ -1539,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)
|
||||
@@ -1552,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);
|
||||
@@ -1585,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! */
|
||||
@@ -1618,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)
|
||||
@@ -1874,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)
|
||||
@@ -2024,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++;
|
||||
|
||||
@@ -83,11 +83,14 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s
|
||||
{
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
|
||||
SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type);
|
||||
const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
|
||||
VehicleSpriteSeq seq;
|
||||
v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
|
||||
|
||||
int width = UnScaleGUI(real_sprite->width);
|
||||
int x_offs = UnScaleGUI(real_sprite->x_offs);
|
||||
Rect rect;
|
||||
seq.GetBounds(&rect);
|
||||
|
||||
int width = UnScaleGUI(rect.right - rect.left + 1);
|
||||
int x_offs = UnScaleGUI(rect.left);
|
||||
int x = rtl ? right - width - x_offs : left - x_offs;
|
||||
bool helicopter = v->subtype == AIR_HELICOPTER;
|
||||
|
||||
@@ -95,17 +98,18 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s
|
||||
int heli_offs = 0;
|
||||
|
||||
PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
|
||||
DrawSprite(sprite, pal, x, y + y_offs);
|
||||
seq.Draw(x, y + y_offs, pal, (v->vehstatus & VS_CRASHED) != 0);
|
||||
if (helicopter) {
|
||||
const Aircraft *a = Aircraft::From(v);
|
||||
SpriteID rotor_sprite = GetCustomRotorSprite(a, true, image_type);
|
||||
if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
|
||||
VehicleSpriteSeq rotor_seq;
|
||||
GetCustomRotorSprite(a, true, image_type, &rotor_seq);
|
||||
if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
|
||||
heli_offs = ScaleGUITrad(5);
|
||||
DrawSprite(rotor_sprite, PAL_NONE, x, y + y_offs - heli_offs);
|
||||
rotor_seq.Draw(x, y + y_offs - heli_offs, PAL_NONE, false);
|
||||
}
|
||||
if (v->index == selection) {
|
||||
x += x_offs;
|
||||
y += UnScaleGUI(real_sprite->y_offs) + y_offs - heli_offs;
|
||||
DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(real_sprite->height) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY);
|
||||
y += UnScaleGUI(rect.top) + y_offs - heli_offs;
|
||||
DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(rect.bottom - rect.top + 1) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* @param delta_z Height of the airport above the land.
|
||||
*/
|
||||
#define AIRPORT_GENERIC(name, terminals, num_helipads, flags, delta_z) \
|
||||
static AirportFTAClass _airportfta_ ## name(_airport_moving_data_ ## name, terminals, \
|
||||
static const AirportFTAClass _airportfta_ ## name(_airport_moving_data_ ## name, terminals, \
|
||||
num_helipads, _airport_entries_ ## name, flags, _airport_fta_ ## name, delta_z);
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,17 +11,15 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "core/alloc_func.hpp"
|
||||
#include "core/smallvec_type.hpp"
|
||||
#include "tile_cmd.h"
|
||||
#include "viewport_func.h"
|
||||
#include "framerate_type.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/** The table/list with animated tiles. */
|
||||
TileIndex *_animated_tile_list = NULL;
|
||||
/** The number of animated tiles in the current state. */
|
||||
uint _animated_tile_count = 0;
|
||||
/** The number of slots for animated tiles allocated currently. */
|
||||
uint _animated_tile_allocated = 0;
|
||||
SmallVector<TileIndex, 256> _animated_tiles;
|
||||
|
||||
/**
|
||||
* Removes the given tile from the animated tile table.
|
||||
@@ -29,17 +27,11 @@ uint _animated_tile_allocated = 0;
|
||||
*/
|
||||
void DeleteAnimatedTile(TileIndex tile)
|
||||
{
|
||||
for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
|
||||
if (tile == *ti) {
|
||||
/* Remove the hole
|
||||
* The order of the remaining elements must stay the same, otherwise the animation loop
|
||||
* may miss a tile; that's why we must use memmove instead of just moving the last element.
|
||||
*/
|
||||
memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti));
|
||||
_animated_tile_count--;
|
||||
MarkTileDirtyByTile(tile);
|
||||
return;
|
||||
}
|
||||
TileIndex *to_remove = _animated_tiles.Find(tile);
|
||||
if (to_remove != _animated_tiles.End()) {
|
||||
/* The order of the remaining elements must stay the same, otherwise the animation loop may miss a tile. */
|
||||
_animated_tiles.ErasePreservingOrder(to_remove);
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,19 +43,7 @@ void DeleteAnimatedTile(TileIndex tile)
|
||||
void AddAnimatedTile(TileIndex tile)
|
||||
{
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
||||
for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
|
||||
if (tile == *ti) return;
|
||||
}
|
||||
|
||||
/* Table not large enough, so make it larger */
|
||||
if (_animated_tile_count == _animated_tile_allocated) {
|
||||
_animated_tile_allocated *= 2;
|
||||
_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
|
||||
}
|
||||
|
||||
_animated_tile_list[_animated_tile_count] = tile;
|
||||
_animated_tile_count++;
|
||||
_animated_tiles.Include(tile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,8 +51,10 @@ void AddAnimatedTile(TileIndex tile)
|
||||
*/
|
||||
void AnimateAnimatedTiles()
|
||||
{
|
||||
const TileIndex *ti = _animated_tile_list;
|
||||
while (ti < _animated_tile_list + _animated_tile_count) {
|
||||
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
|
||||
|
||||
const TileIndex *ti = _animated_tiles.Begin();
|
||||
while (ti < _animated_tiles.End()) {
|
||||
const TileIndex curr = *ti;
|
||||
AnimateTile(curr);
|
||||
/* During the AnimateTile call, DeleteAnimatedTile could have been called,
|
||||
@@ -93,7 +75,5 @@ void AnimateAnimatedTiles()
|
||||
*/
|
||||
void InitializeAnimatedTiles()
|
||||
{
|
||||
_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, 256);
|
||||
_animated_tile_count = 0;
|
||||
_animated_tile_allocated = 256;
|
||||
_animated_tiles.Clear();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -433,6 +433,7 @@ void AddArticulatedParts(Vehicle *first)
|
||||
v->x_pos = first->x_pos;
|
||||
v->y_pos = first->y_pos;
|
||||
v->z_pos = first->z_pos;
|
||||
v->date_of_last_service = first->date_of_last_service;
|
||||
v->build_year = first->build_year;
|
||||
v->vehstatus = first->vehstatus & ~VS_STOPPED;
|
||||
|
||||
@@ -440,7 +441,7 @@ void AddArticulatedParts(Vehicle *first)
|
||||
v->max_age = 0;
|
||||
v->engine_type = engine_type;
|
||||
v->value = 0;
|
||||
v->cur_image = SPR_IMG_QUERY;
|
||||
v->sprite_seq.Set(SPR_IMG_QUERY);
|
||||
v->random_bits = VehicleRandomBits();
|
||||
|
||||
if (flip_image) v->spritenum++;
|
||||
|
||||
@@ -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,13 +81,13 @@ 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.
|
||||
bool descending_sort_order; ///< Order of sorting vehicles.
|
||||
bool show_hidden_engines; ///< Whether to show the hidden engines.
|
||||
RailType sel_railtype; ///< Type of rail tracks selected.
|
||||
RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all.
|
||||
Scrollbar *vscroll[2];
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ class ReplaceVehicleWindow : public Window {
|
||||
/* Ensure that the wagon/engine selection fits the engine. */
|
||||
if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
|
||||
|
||||
if (draw_left && show_engines) {
|
||||
if (draw_left && this->sel_railtype != INVALID_RAILTYPE) {
|
||||
/* Ensure that the railtype is specific to the selected one */
|
||||
if (rvi->railtype != this->sel_railtype) return false;
|
||||
}
|
||||
@@ -211,24 +211,7 @@ class ReplaceVehicleWindow : public Window {
|
||||
public:
|
||||
ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc)
|
||||
{
|
||||
if (vehicletype == VEH_TRAIN) {
|
||||
/* For rail vehicles find the most used vehicle type, which is usually
|
||||
* better than 'just' the first/previous vehicle type. */
|
||||
uint type_count[RAILTYPE_END];
|
||||
memset(type_count, 0, sizeof(type_count));
|
||||
|
||||
const Engine *e;
|
||||
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
|
||||
if (e->u.rail.railveh_type == RAILVEH_WAGON) continue;
|
||||
type_count[e->u.rail.railtype] += GetGroupNumEngines(_local_company, id_g, e->index);
|
||||
}
|
||||
|
||||
this->sel_railtype = RAILTYPE_BEGIN;
|
||||
for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
|
||||
if (type_count[this->sel_railtype] < type_count[rt]) this->sel_railtype = rt;
|
||||
}
|
||||
}
|
||||
|
||||
this->sel_railtype = INVALID_RAILTYPE;
|
||||
this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool)
|
||||
this->engines[0].ForceRebuild();
|
||||
this->engines[1].ForceRebuild();
|
||||
@@ -288,12 +271,9 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE: {
|
||||
StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
|
||||
SetDParam(0, STR_REPLACE_ENGINES);
|
||||
Dimension d = GetStringBoundingBox(str);
|
||||
SetDParam(0, STR_REPLACE_WAGONS);
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
|
||||
Dimension d = GetStringBoundingBox(STR_REPLACE_ENGINES);
|
||||
d = maxdim(d, GetStringBoundingBox(STR_REPLACE_WAGONS));
|
||||
d.width += padding.width;
|
||||
d.height += padding.height;
|
||||
*size = maxdim(*size, d);
|
||||
@@ -367,7 +347,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE:
|
||||
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN:
|
||||
SetDParam(0, this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS);
|
||||
break;
|
||||
}
|
||||
@@ -432,12 +412,8 @@ public:
|
||||
this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
|
||||
|
||||
if (this->window_number == VEH_TRAIN) {
|
||||
/* sets the colour of that art thing */
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
|
||||
|
||||
/* Show the selected railtype in the pulldown menu */
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = GetRailTypeInfo(sel_railtype)->strings.replace_text;
|
||||
this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text;
|
||||
}
|
||||
|
||||
this->DrawWidgets();
|
||||
@@ -483,15 +459,16 @@ public:
|
||||
DisplayVehicleSortDropDown(this, static_cast<VehicleType>(this->window_number), this->sort_criteria, WID_RV_SORT_DROPDOWN);
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_TOGGLE:
|
||||
this->replace_engines = !(this->replace_engines);
|
||||
this->engines[0].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
this->SetDirty();
|
||||
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
|
||||
DropDownList *list = new DropDownList();
|
||||
*list->Append() = new DropDownListStringItem(STR_REPLACE_ENGINES, 1, false);
|
||||
*list->Append() = new DropDownListStringItem(STR_REPLACE_WAGONS, 0, false);
|
||||
ShowDropDownList(this, list, this->replace_engines ? 1 : 0, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
|
||||
ShowDropDownList(this, GetRailTypeDropDownList(true), sel_railtype, WID_RV_TRAIN_RAILTYPE_DROPDOWN);
|
||||
ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_TRAIN_RAILTYPE_DROPDOWN);
|
||||
break;
|
||||
|
||||
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length
|
||||
@@ -566,6 +543,14 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
|
||||
this->replace_engines = index != 0;
|
||||
this->engines[0].ForceRebuild();
|
||||
this->reset_sel_engine = true;
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RV_START_REPLACE:
|
||||
this->ReplaceClick_StartReplace(index != 0);
|
||||
break;
|
||||
@@ -611,7 +596,13 @@ static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1),
|
||||
@@ -631,20 +622,16 @@ static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP), SetFill(1, 0), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_TOGGLE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_ENGINE_WAGON_SELECT, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_TRAIN_FLUFF_LEFT), SetMinimalSize(15, 12), EndContainer(),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_TRAIN_FLUFF_RIGHT), SetMinimalSize(16, 12), EndContainer(),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -219,6 +221,11 @@ public:
|
||||
static bool HasSet(const ContentInfo *ci, bool md5sum);
|
||||
};
|
||||
|
||||
template <class Tbase_set> /* static */ const char *BaseMedia<Tbase_set>::ini_set;
|
||||
template <class Tbase_set> /* static */ const Tbase_set *BaseMedia<Tbase_set>::used_set;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::available_sets;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::duplicate_sets;
|
||||
|
||||
/**
|
||||
* Check whether there's a base set matching some information.
|
||||
* @param ci The content info to compare it to.
|
||||
@@ -280,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);
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
#include "ini_type.h"
|
||||
#include "string_func.h"
|
||||
|
||||
template <class Tbase_set> /* static */ const char *BaseMedia<Tbase_set>::ini_set;
|
||||
template <class Tbase_set> /* static */ const Tbase_set *BaseMedia<Tbase_set>::used_set;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::available_sets;
|
||||
template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::duplicate_sets;
|
||||
|
||||
/**
|
||||
* Try to read a single piece of metadata and return false if it doesn't exist.
|
||||
* @param name the name of the item to fetch.
|
||||
@@ -45,8 +40,6 @@ template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::duplica
|
||||
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;
|
||||
|
||||
@@ -134,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
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
/** Instantiation of the 32bpp with animation blitter factory. */
|
||||
static FBlitter_32bppAnim iFBlitter_32bppAnim;
|
||||
|
||||
Blitter_32bppAnim::~Blitter_32bppAnim()
|
||||
{
|
||||
free(this->anim_alloc);
|
||||
}
|
||||
|
||||
template <BlitterMode mode>
|
||||
inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
|
||||
{
|
||||
@@ -34,13 +39,13 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
|
||||
}
|
||||
|
||||
Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
|
||||
uint16 *anim = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_width + 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
|
||||
|
||||
for (int y = 0; y < bp->height; y++) {
|
||||
Colour *dst_ln = dst + bp->pitch;
|
||||
uint16 *anim_ln = anim + this->anim_buf_width;
|
||||
uint16 *anim_ln = anim + this->anim_buf_pitch;
|
||||
|
||||
const Colour *src_px_ln = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
|
||||
src_px++;
|
||||
@@ -274,9 +279,7 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
||||
}
|
||||
|
||||
Colour *udst = (Colour *)dst;
|
||||
uint16 *anim;
|
||||
|
||||
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 {
|
||||
@@ -287,7 +290,7 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
||||
anim++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
anim = anim - width + this->anim_buf_width;
|
||||
anim = anim - width + this->anim_buf_pitch;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
@@ -300,7 +303,7 @@ void Blitter_32bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
||||
anim++;
|
||||
}
|
||||
udst = udst - width + _screen.pitch;
|
||||
anim = anim - width + this->anim_buf_width;
|
||||
anim = anim - width + this->anim_buf_pitch;
|
||||
} while (--height);
|
||||
return;
|
||||
}
|
||||
@@ -314,7 +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;
|
||||
this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_width] = 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)
|
||||
@@ -326,9 +330,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colou
|
||||
}
|
||||
|
||||
Colour colour32 = LookupColourInPalette(colour);
|
||||
uint16 *anim_line;
|
||||
|
||||
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;
|
||||
@@ -342,7 +344,7 @@ void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colou
|
||||
anim++;
|
||||
}
|
||||
video = (uint32 *)video + _screen.pitch;
|
||||
anim_line += this->anim_buf_width;
|
||||
anim_line += this->anim_buf_pitch;
|
||||
} while (--height);
|
||||
}
|
||||
|
||||
@@ -352,20 +354,20 @@ 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;
|
||||
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 */
|
||||
memcpy(anim_line, usrc, width * sizeof(uint16));
|
||||
usrc = (const uint32 *)((const uint16 *)usrc + width);
|
||||
anim_line += this->anim_buf_width;
|
||||
anim_line += this->anim_buf_pitch;
|
||||
|
||||
/* Okay, it is *very* likely that the image we stored is using
|
||||
* the wrong palette animated colours. There are two things we
|
||||
@@ -392,11 +394,10 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
|
||||
assert(video >= _screen.dst_ptr && video <= (uint32 *)_screen.dst_ptr + _screen.width + _screen.height * _screen.pitch);
|
||||
uint32 *udst = (uint32 *)dst;
|
||||
const uint32 *src = (const uint32 *)video;
|
||||
const uint16 *anim_line;
|
||||
|
||||
if (this->anim_buf == NULL) return;
|
||||
|
||||
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));
|
||||
@@ -405,7 +406,7 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
|
||||
/* Copy the anim-buffer */
|
||||
memcpy(udst, anim_line, width * sizeof(uint16));
|
||||
udst = (uint32 *)((uint16 *)udst + width);
|
||||
anim_line += this->anim_buf_width;
|
||||
anim_line += this->anim_buf_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,8 +418,8 @@ void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
|
||||
|
||||
/* We need to scroll the anim-buffer too */
|
||||
if (scroll_y > 0) {
|
||||
dst = this->anim_buf + left + (top + height - 1) * this->anim_buf_width;
|
||||
src = dst - scroll_y * this->anim_buf_width;
|
||||
dst = this->anim_buf + left + (top + height - 1) * this->anim_buf_pitch;
|
||||
src = dst - scroll_y * this->anim_buf_pitch;
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
@@ -431,13 +432,13 @@ void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
|
||||
uint th = height - scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memcpy(dst, src, tw * sizeof(uint16));
|
||||
src -= this->anim_buf_width;
|
||||
dst -= this->anim_buf_width;
|
||||
src -= this->anim_buf_pitch;
|
||||
dst -= this->anim_buf_pitch;
|
||||
}
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
dst = this->anim_buf + left + top * this->anim_buf_width;
|
||||
src = dst - scroll_y * this->anim_buf_width;
|
||||
dst = this->anim_buf + left + top * this->anim_buf_pitch;
|
||||
src = dst - scroll_y * this->anim_buf_pitch;
|
||||
|
||||
/* Adjust left & width */
|
||||
if (scroll_x >= 0) {
|
||||
@@ -452,8 +453,8 @@ void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
|
||||
uint th = height + scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memmove(dst, src, tw * sizeof(uint16));
|
||||
src += this->anim_buf_width;
|
||||
dst += this->anim_buf_width;
|
||||
src += this->anim_buf_pitch;
|
||||
dst += this->anim_buf_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,17 +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;
|
||||
dst += pitch_offset;
|
||||
anim += anim_pitch_offset;
|
||||
}
|
||||
|
||||
/* Make sure the backend redraws the whole screen */
|
||||
@@ -503,11 +509,16 @@ Blitter::PaletteAnimation Blitter_32bppAnim::UsePaletteAnimation()
|
||||
|
||||
void Blitter_32bppAnim::PostResize()
|
||||
{
|
||||
if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height) {
|
||||
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);
|
||||
this->anim_buf = CallocT<uint16>(_screen.width * _screen.height);
|
||||
free(this->anim_alloc);
|
||||
this->anim_buf_width = _screen.width;
|
||||
this->anim_buf_height = _screen.height;
|
||||
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,16 +18,24 @@
|
||||
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 (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_height(0),
|
||||
anim_buf_pitch(0)
|
||||
{
|
||||
this->palette = _cur_palette;
|
||||
}
|
||||
|
||||
~Blitter_32bppAnim();
|
||||
|
||||
/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
|
||||
/* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal);
|
||||
@@ -52,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,7 +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;
|
||||
uint16 *anim_line = this->anim_buf + ((uint32 *)bp->dst - (uint32 *)_screen.dst_ptr) + bp->top * this->anim_buf_width + 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. */
|
||||
@@ -353,7 +353,7 @@ next_line:
|
||||
if (mode != BM_TRANSPARENT) src_mv_line += si->sprite_width;
|
||||
src_rgba_line = (const Colour*) ((const byte*) src_rgba_line + si->sprite_line_size);
|
||||
dst_line += bp->pitch;
|
||||
anim_line += this->anim_buf_width;
|
||||
anim_line += this->anim_buf_pitch;
|
||||
}
|
||||
}
|
||||
IGNORE_UNINITIALIZED_WARNING_STOP
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_32X ? 8 : 4) ///< Minimum width to use margins with BM_NORMAL.
|
||||
#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BM_COLOUR_REMAP.
|
||||
|
||||
#undef ALIGN
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ALIGN(n) __declspec(align(n))
|
||||
#else
|
||||
@@ -53,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 */
|
||||
|
||||
@@ -48,7 +48,7 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom
|
||||
break;
|
||||
|
||||
case BM_BLACK_REMAP:
|
||||
colour = 0;
|
||||
if (*src != 0) *dst = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -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. */
|
||||
@@ -264,6 +264,6 @@ bool HandleBootstrap()
|
||||
|
||||
/* Failure to get enough working to get a graphics set. */
|
||||
failure:
|
||||
usererror("Failed to find a graphics set. Please acquire a graphics set for OpenTTD. See section 4.1 of readme.txt.");
|
||||
usererror("Failed to find a graphics set. Please acquire a graphics set for OpenTTD. See section 4.1 of README.md.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
@@ -719,7 +719,15 @@ static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_numb
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Draw aircraft specific details */
|
||||
/**
|
||||
* Draw aircraft specific details in the buy window.
|
||||
* @param left Left edge of the window to draw in.
|
||||
* @param right Right edge of the window to draw in.
|
||||
* @param y Top of the area to draw in.
|
||||
* @param engine_number Engine to display.
|
||||
* @param refittable If set, the aircraft can be refitted.
|
||||
* @return Bottom of the used area.
|
||||
*/
|
||||
static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
|
||||
{
|
||||
const Engine *e = Engine::Get(engine_number);
|
||||
@@ -755,6 +763,12 @@ static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_
|
||||
DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
/* Aircraft type */
|
||||
SetDParam(0, e->GetAircraftTypeText());
|
||||
DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_TYPE);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
/* Aircraft range, if available. */
|
||||
uint16 range = e->GetRange();
|
||||
if (range != 0) {
|
||||
SetDParam(0, range);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -56,7 +56,7 @@ void DrawHillyLandTile(const TileInfo *ti)
|
||||
if (ti->tileh != SLOPE_FLAT) {
|
||||
DrawGroundSprite(SPR_FLAT_ROUGH_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
|
||||
} else {
|
||||
DrawGroundSprite(_landscape_clear_sprites_rough[GB(ti->x ^ ti->y, 4, 3)], PAL_NONE);
|
||||
DrawGroundSprite(_landscape_clear_sprites_rough[GB(TileHash(ti->x, ti->y), 0, 3)], PAL_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -167,6 +167,7 @@ CommandProc CmdSetStoryPageDate;
|
||||
CommandProc CmdShowStoryPage;
|
||||
CommandProc CmdRemoveStoryPage;
|
||||
CommandProc CmdRemoveStoryPageElement;
|
||||
CommandProc CmdScrollViewport;
|
||||
|
||||
CommandProc CmdLevelLand;
|
||||
|
||||
@@ -322,6 +323,7 @@ static const Command _command_proc_table[] = {
|
||||
DEF_CMD(CmdShowStoryPage, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SHOW_STORY_PAGE
|
||||
DEF_CMD(CmdRemoveStoryPage, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_STORY_PAGE
|
||||
DEF_CMD(CmdRemoveStoryPageElement, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_STORY_ELEMENT_PAGE
|
||||
DEF_CMD(CmdScrollViewport, CMD_DEITY, CMDT_OTHER_MANAGEMENT ), // CMD_SCROLL_VIEWPORT
|
||||
|
||||
DEF_CMD(CmdLevelLand, CMD_ALL_TILES | CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LEVEL_LAND; test run might clear tiles multiple times, in execution that only happens once
|
||||
|
||||
@@ -603,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; }
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ CommandCallback CcBuildBridge;
|
||||
|
||||
/* dock_gui.cpp */
|
||||
CommandCallback CcBuildDocks;
|
||||
CommandCallback CcBuildCanal;
|
||||
CommandCallback CcPlaySound_SPLAT_WATER;
|
||||
|
||||
/* depot_gui.cpp */
|
||||
CommandCallback CcCloneVehicle;
|
||||
@@ -97,19 +97,19 @@ CommandCallback CcAddVehicleNewGroup;
|
||||
CommandCallback CcBuildIndustry;
|
||||
|
||||
/* main_gui.cpp */
|
||||
CommandCallback CcPlaySound10;
|
||||
CommandCallback CcPlaySound_EXPLOSION;
|
||||
CommandCallback CcPlaceSign;
|
||||
CommandCallback CcTerraform;
|
||||
CommandCallback CcGiveMoney;
|
||||
|
||||
/* rail_gui.cpp */
|
||||
CommandCallback CcPlaySound1E;
|
||||
CommandCallback CcPlaySound_SPLAT_RAIL;
|
||||
CommandCallback CcRailDepot;
|
||||
CommandCallback CcStation;
|
||||
CommandCallback CcBuildRailTunnel;
|
||||
|
||||
/* road_gui.cpp */
|
||||
CommandCallback CcPlaySound1D;
|
||||
CommandCallback CcPlaySound_SPLAT_OTHER;
|
||||
CommandCallback CcBuildRoadTunnel;
|
||||
CommandCallback CcRoadDepot;
|
||||
CommandCallback CcRoadStop;
|
||||
|
||||
@@ -293,6 +293,8 @@ enum Commands {
|
||||
CMD_SHOW_STORY_PAGE, ///< show a story page
|
||||
CMD_REMOVE_STORY_PAGE, ///< remove a story page
|
||||
CMD_REMOVE_STORY_PAGE_ELEMENT, ///< remove a story page element
|
||||
CMD_SCROLL_VIEWPORT, ///< scroll main viewport of players
|
||||
|
||||
CMD_LEVEL_LAND, ///< level land
|
||||
|
||||
CMD_BUILD_LOCK, ///< build a lock
|
||||
|
||||
@@ -52,10 +52,10 @@ extern CompanyPool _company_pool;
|
||||
/** Statically loadable part of Company pool item */
|
||||
struct CompanyProperties {
|
||||
uint32 name_2; ///< Parameter of #name_1.
|
||||
uint16 name_1; ///< Name of the company if the user did not change it.
|
||||
StringID name_1; ///< Name of the company if the user did not change it.
|
||||
char *name; ///< Name of the company if the user changed it.
|
||||
|
||||
uint16 president_name_1; ///< Name of the president if the user did not change it.
|
||||
StringID president_name_1; ///< Name of the president if the user did not change it.
|
||||
uint32 president_name_2; ///< Parameter of #president_name_1
|
||||
char *president_name; ///< Name of the president if the user changed it.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -162,7 +162,7 @@ static bool IsValidCompanyManagerFace(CompanyManagerFace cmf)
|
||||
for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
|
||||
switch (cmfv) {
|
||||
case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
|
||||
case CMFV_LIPS: // FALL THROUGH
|
||||
case CMFV_LIPS:
|
||||
case CMFV_NOSE: if (has_moustache) continue; break;
|
||||
case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
|
||||
case CMFV_GLASSES: if (!has_glasses) continue; break;
|
||||
@@ -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'.
|
||||
@@ -318,7 +315,8 @@ struct CompanyFinancesWindow : Window {
|
||||
case WID_CF_EXPS_PRICE2:
|
||||
case WID_CF_EXPS_PRICE3:
|
||||
size->height = _expenses_list_types[type].GetHeight();
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
case WID_CF_BALANCE_VALUE:
|
||||
case WID_CF_LOAN_VALUE:
|
||||
case WID_CF_TOTAL_VALUE:
|
||||
@@ -636,7 +634,8 @@ public:
|
||||
size->width = 0;
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
case WID_SCL_PRI_COL_DROPDOWN: {
|
||||
int padding = this->square.width + NWidgetScrollbar::GetVerticalDimension().width + 10;
|
||||
for (const StringID *id = _colour_dropdown; id != endof(_colour_dropdown); id++) {
|
||||
@@ -892,7 +891,7 @@ void DrawCompanyManagerFace(CompanyManagerFace cmf, int colour, int x, int y)
|
||||
for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
|
||||
switch (cmfv) {
|
||||
case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
|
||||
case CMFV_LIPS: // FALL THROUGH
|
||||
case CMFV_LIPS:
|
||||
case CMFV_NOSE: if (has_moustache) continue; break;
|
||||
case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
|
||||
case CMFV_GLASSES: if (!has_glasses) continue; break;
|
||||
@@ -1365,7 +1364,7 @@ public:
|
||||
/* OK button */
|
||||
case WID_SCMF_ACCEPT:
|
||||
DoCommandP(0, 0, this->face, CMD_SET_COMPANY_MANAGER_FACE);
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
/* Cancel button */
|
||||
case WID_SCMF_CANCEL:
|
||||
@@ -1507,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)
|
||||
{
|
||||
@@ -2381,7 +2377,7 @@ struct CompanyWindow : Window
|
||||
|
||||
virtual void OnPlaceObject(Point pt, TileIndex tile)
|
||||
{
|
||||
if (DoCommandP(tile, OBJECT_HQ, 0, CMD_BUILD_OBJECT | CMD_MSG(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS))) {
|
||||
if (DoCommandP(tile, OBJECT_HQ, 0, CMD_BUILD_OBJECT | CMD_MSG(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS)) && !_shift_pressed) {
|
||||
ResetObjectToPlace();
|
||||
this->RaiseButtons();
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ static const CompanyManagerFaceBitsInfo _cmf_info[] = {
|
||||
/* CMFV_MOUSTACHE */ { 13, 2, { 3, 0, 3, 0 }, { 0x367, 0, 0x397, 0 } }, ///< Depends on CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_LIPS */ { 13, 4, { 12, 10, 9, 9 }, { 0x35B, 0x351, 0x3A5, 0x3C8 } }, ///< Depends on !CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_NOSE */ { 17, 3, { 8, 4, 4, 5 }, { 0x349, 0x34C, 0x393, 0x3B3 } }, ///< Depends on !CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_HAIR */ { 20, 4, { 9, 5, 5, 4 }, { 0x382, 0x38B, 0x3D4, 0x3D9 } },
|
||||
/* CMFV_HAIR */ { 20, 4, { 9, 5, 5, 5 }, { 0x382, 0x38B, 0x3D4, 0x3D9 } },
|
||||
/* CMFV_JACKET */ { 24, 2, { 3, 3, 3, 3 }, { 0x36B, 0x378, 0x36B, 0x378 } },
|
||||
/* CMFV_COLLAR */ { 26, 2, { 4, 4, 4, 4 }, { 0x36E, 0x37B, 0x36E, 0x37B } },
|
||||
/* CMFV_TIE_EARRING */ { 28, 3, { 6, 3, 6, 3 }, { 0x372, 0x37F, 0x372, 0x3D1 } }, ///< Depends on CMFV_HAS_TIE_EARRING
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -434,7 +434,10 @@ void IConsoleCmdExec(const char *cmdstr)
|
||||
* enclosed in "" are taken as one token. We can only go as far as the amount
|
||||
* of characters in our stream or the max amount of tokens we can handle */
|
||||
for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
|
||||
if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
|
||||
if (tstream_i >= lengthof(tokenstream)) {
|
||||
IConsoleError("command line too long");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (*cmdptr) {
|
||||
case ' ': // Token separator
|
||||
@@ -452,6 +455,10 @@ void IConsoleCmdExec(const char *cmdstr)
|
||||
case '"': // Tokens enclosed in "" are one token
|
||||
longtoken = !longtoken;
|
||||
if (!foundtoken) {
|
||||
if (t_index >= lengthof(tokens)) {
|
||||
IConsoleError("command line too long");
|
||||
return;
|
||||
}
|
||||
tokens[t_index++] = &tokenstream[tstream_i];
|
||||
foundtoken = true;
|
||||
}
|
||||
@@ -461,11 +468,15 @@ void IConsoleCmdExec(const char *cmdstr)
|
||||
tokenstream[tstream_i++] = *++cmdptr;
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
default: // Normal character
|
||||
tokenstream[tstream_i++] = *cmdptr;
|
||||
|
||||
if (!foundtoken) {
|
||||
if (t_index >= lengthof(tokens)) {
|
||||
IConsoleError("command line too long");
|
||||
return;
|
||||
}
|
||||
tokens[t_index++] = &tokenstream[tstream_i - 1];
|
||||
foundtoken = true;
|
||||
}
|
||||
@@ -473,7 +484,7 @@ void IConsoleCmdExec(const char *cmdstr)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; tokens[i] != NULL; i++) {
|
||||
for (uint i = 0; i < lengthof(tokens) && tokens[i] != NULL; i++) {
|
||||
DEBUG(console, 8, "Token %d is: '%s'", i, tokens[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,38 @@
|
||||
/* scriptfile handling */
|
||||
static bool _script_running; ///< Script is running (used to abort execution when #ConReturn is encountered).
|
||||
|
||||
/** File list storage for the console, for caching the last 'ls' command. */
|
||||
class ConsoleFileList : public FileList {
|
||||
public:
|
||||
ConsoleFileList() : FileList()
|
||||
{
|
||||
this->file_list_valid = false;
|
||||
}
|
||||
|
||||
/** Declare the file storage cache as being invalid, also clears all stored files. */
|
||||
void InvalidateFileList()
|
||||
{
|
||||
this->Clear();
|
||||
this->file_list_valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re-)validate the file storage cache. Only makes a change if the storage was invalid, or if \a force_reload.
|
||||
* @param force_reload Always reload the file storage cache.
|
||||
*/
|
||||
void ValidateFileList(bool force_reload = false)
|
||||
{
|
||||
if (force_reload || !this->file_list_valid) {
|
||||
this->BuildFileList(FT_SAVEGAME, SLO_LOAD);
|
||||
this->file_list_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool file_list_valid; ///< If set, the file list is valid.
|
||||
};
|
||||
|
||||
static ConsoleFileList _console_file_list; ///< File storage cache for the console.
|
||||
|
||||
/* console command defines */
|
||||
#define DEF_CONSOLE_CMD(function) static bool function(byte argc, char *argv[])
|
||||
#define DEF_CONSOLE_HOOK(function) static ConsoleHookResult function(bool echo)
|
||||
@@ -224,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.
|
||||
@@ -272,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)
|
||||
@@ -286,7 +318,7 @@ DEF_CONSOLE_CMD(ConSave)
|
||||
char *filename = str_fmt("%s.sav", argv[1]);
|
||||
IConsolePrint(CC_DEFAULT, "Saving map...");
|
||||
|
||||
if (SaveOrLoad(filename, SL_SAVE, SAVE_DIR) != SL_OK) {
|
||||
if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, SAVE_DIR) != SL_OK) {
|
||||
IConsolePrint(CC_ERROR, "Saving map failed");
|
||||
} else {
|
||||
IConsolePrintF(CC_DEFAULT, "Map successfully saved to %s", filename);
|
||||
@@ -315,42 +347,6 @@ DEF_CONSOLE_CMD(ConSaveConfig)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get savegame file informations.
|
||||
* @param file The savegame filename to return information about. Can be the actual name
|
||||
* or a numbered entry into the filename list.
|
||||
* @return FiosItem The information on the file.
|
||||
*/
|
||||
static const FiosItem *GetFiosItem(const char *file)
|
||||
{
|
||||
_saveload_mode = SLD_LOAD_GAME;
|
||||
BuildFileList();
|
||||
|
||||
for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) {
|
||||
if (strcmp(file, item->name) == 0) return item;
|
||||
if (strcmp(file, item->title) == 0) return item;
|
||||
}
|
||||
|
||||
/* If no name matches, try to parse it as number */
|
||||
char *endptr;
|
||||
int i = strtol(file, &endptr, 10);
|
||||
if (file == endptr || *endptr != '\0') i = -1;
|
||||
|
||||
if (IsInsideMM(i, 0, _fios_items.Length())) return _fios_items.Get(i);
|
||||
|
||||
/* As a last effort assume it is an OpenTTD savegame and
|
||||
* that the ".sav" part was not given. */
|
||||
char long_file[MAX_PATH];
|
||||
seprintf(long_file, lastof(long_file), "%s.sav", file);
|
||||
for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) {
|
||||
if (strcmp(long_file, item->name) == 0) return item;
|
||||
if (strcmp(long_file, item->title) == 0) return item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
DEF_CONSOLE_CMD(ConLoad)
|
||||
{
|
||||
if (argc == 0) {
|
||||
@@ -361,24 +357,21 @@ DEF_CONSOLE_CMD(ConLoad)
|
||||
if (argc != 2) return false;
|
||||
|
||||
const char *file = argv[1];
|
||||
const FiosItem *item = GetFiosItem(file);
|
||||
_console_file_list.ValidateFileList();
|
||||
const FiosItem *item = _console_file_list.FindItem(file);
|
||||
if (item != NULL) {
|
||||
switch (item->type) {
|
||||
case FIOS_TYPE_FILE: case FIOS_TYPE_OLDFILE: {
|
||||
_switch_mode = SM_LOAD_GAME;
|
||||
SetFiosType(item->type);
|
||||
|
||||
strecpy(_file_to_saveload.name, FiosBrowseTo(item), lastof(_file_to_saveload.name));
|
||||
strecpy(_file_to_saveload.title, item->title, lastof(_file_to_saveload.title));
|
||||
break;
|
||||
}
|
||||
default: IConsolePrintF(CC_ERROR, "%s: Not a savegame.", file);
|
||||
if (GetAbstractFileType(item->type) == FT_SAVEGAME) {
|
||||
_switch_mode = SM_LOAD_GAME;
|
||||
_file_to_saveload.SetMode(item->type);
|
||||
_file_to_saveload.SetName(FiosBrowseTo(item));
|
||||
_file_to_saveload.SetTitle(item->title);
|
||||
} else {
|
||||
IConsolePrintF(CC_ERROR, "%s: Not a savegame.", file);
|
||||
}
|
||||
} else {
|
||||
IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
|
||||
}
|
||||
|
||||
FiosFreeSavegameList();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -393,7 +386,8 @@ DEF_CONSOLE_CMD(ConRemove)
|
||||
if (argc != 2) return false;
|
||||
|
||||
const char *file = argv[1];
|
||||
const FiosItem *item = GetFiosItem(file);
|
||||
_console_file_list.ValidateFileList();
|
||||
const FiosItem *item = _console_file_list.FindItem(file);
|
||||
if (item != NULL) {
|
||||
if (!FiosDelete(item->name)) {
|
||||
IConsolePrintF(CC_ERROR, "%s: Failed to delete file", file);
|
||||
@@ -402,7 +396,7 @@ DEF_CONSOLE_CMD(ConRemove)
|
||||
IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
|
||||
}
|
||||
|
||||
FiosFreeSavegameList();
|
||||
_console_file_list.InvalidateFileList();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -415,13 +409,11 @@ DEF_CONSOLE_CMD(ConListFiles)
|
||||
return true;
|
||||
}
|
||||
|
||||
BuildFileList();
|
||||
|
||||
for (uint i = 0; i < _fios_items.Length(); i++) {
|
||||
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _fios_items[i].title);
|
||||
_console_file_list.ValidateFileList(true);
|
||||
for (uint i = 0; i < _console_file_list.Length(); i++) {
|
||||
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title);
|
||||
}
|
||||
|
||||
FiosFreeSavegameList();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -436,7 +428,8 @@ DEF_CONSOLE_CMD(ConChangeDirectory)
|
||||
if (argc != 2) return false;
|
||||
|
||||
const char *file = argv[1];
|
||||
const FiosItem *item = GetFiosItem(file);
|
||||
_console_file_list.ValidateFileList(true);
|
||||
const FiosItem *item = _console_file_list.FindItem(file);
|
||||
if (item != NULL) {
|
||||
switch (item->type) {
|
||||
case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT:
|
||||
@@ -448,7 +441,7 @@ DEF_CONSOLE_CMD(ConChangeDirectory)
|
||||
IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
|
||||
}
|
||||
|
||||
FiosFreeSavegameList();
|
||||
_console_file_list.InvalidateFileList();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -462,8 +455,8 @@ DEF_CONSOLE_CMD(ConPrintWorkingDirectory)
|
||||
}
|
||||
|
||||
/* XXX - Workaround for broken file handling */
|
||||
FiosGetSavegameList(SLD_LOAD_GAME);
|
||||
FiosFreeSavegameList();
|
||||
_console_file_list.ValidateFileList(true);
|
||||
_console_file_list.InvalidateFileList();
|
||||
|
||||
FiosGetDescText(&path, NULL);
|
||||
IConsolePrint(CC_DEFAULT, path);
|
||||
@@ -560,29 +553,36 @@ DEF_CONSOLE_CMD(ConBan)
|
||||
|
||||
DEF_CONSOLE_CMD(ConUnBan)
|
||||
{
|
||||
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Unban a client from a network game. Usage: 'unban <ip | client-id>'");
|
||||
IConsoleHelp("Unban a client from a network game. Usage: 'unban <ip | banlist-index>'");
|
||||
IConsoleHelp("For a list of banned IP's, see the command 'banlist'");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc != 2) return false;
|
||||
|
||||
uint index = (strchr(argv[1], '.') == NULL) ? atoi(argv[1]) : 0;
|
||||
index--;
|
||||
uint i = 0;
|
||||
|
||||
for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++, i++) {
|
||||
if (strcmp(_network_ban_list[i], argv[1]) == 0 || index == i) {
|
||||
free(_network_ban_list[i]);
|
||||
_network_ban_list.Erase(iter);
|
||||
IConsolePrint(CC_DEFAULT, "IP unbanned.");
|
||||
return true;
|
||||
}
|
||||
/* Try by IP. */
|
||||
uint index;
|
||||
for (index = 0; index < _network_ban_list.Length(); index++) {
|
||||
if (strcmp(_network_ban_list[index], argv[1]) == 0) break;
|
||||
}
|
||||
|
||||
/* Try by index. */
|
||||
if (index >= _network_ban_list.Length()) {
|
||||
index = atoi(argv[1]) - 1U; // let it wrap
|
||||
}
|
||||
|
||||
if (index < _network_ban_list.Length()) {
|
||||
char msg[64];
|
||||
seprintf(msg, lastof(msg), "Unbanned %s", _network_ban_list[index]);
|
||||
IConsolePrint(CC_DEFAULT, msg);
|
||||
free(_network_ban_list[index]);
|
||||
_network_ban_list.Erase(_network_ban_list.Get(index));
|
||||
} else {
|
||||
IConsolePrint(CC_DEFAULT, "Invalid list index or IP not in ban-list.");
|
||||
IConsolePrint(CC_DEFAULT, "For a list of banned IP's, see the command 'banlist'");
|
||||
}
|
||||
|
||||
IConsolePrint(CC_DEFAULT, "IP not in ban-list.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -365,13 +367,32 @@ static inline T ROR(const T x, const uint8 n)
|
||||
* (since it will use hardware swapping if available).
|
||||
* Even though they should return uint16 and uint32, we get
|
||||
* warnings if we don't cast those (why?) */
|
||||
#define BSWAP64(x) ((uint64)CFSwapInt64(x))
|
||||
#define BSWAP32(x) ((uint32)CFSwapInt32(x))
|
||||
#define BSWAP16(x) ((uint16)CFSwapInt16(x))
|
||||
#elif defined(_MSC_VER)
|
||||
/* MSVC has intrinsics for swapping, resulting in faster code */
|
||||
#define BSWAP64(x) (_byteswap_uint64(x))
|
||||
#define BSWAP32(x) (_byteswap_ulong(x))
|
||||
#define BSWAP16(x) (_byteswap_ushort(x))
|
||||
#else
|
||||
/**
|
||||
* Perform a 64 bits endianness bitswap on x.
|
||||
* @param x the variable to bitswap
|
||||
* @return the bitswapped value.
|
||||
*/
|
||||
static inline uint64 BSWAP64(uint64 x)
|
||||
{
|
||||
#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__))
|
||||
/* GCC >= 4.3 provides a builtin, resulting in faster code */
|
||||
return (uint64)__builtin_bswap64((uint64)x);
|
||||
#else
|
||||
return ((x >> 56) & 0xFFULL) | ((x >> 40) & 0xFF00ULL) | ((x >> 24) & 0xFF0000ULL) | ((x >> 8) & 0xFF000000ULL) |
|
||||
((x << 8) & 0xFF00000000ULL) | ((x << 24) & 0xFF0000000000ULL) | ((x << 40) & 0xFF000000000000ULL) | ((x << 56) & 0xFF000000000000ULL);
|
||||
;
|
||||
#endif /* __GNUC__ || __clang__ */
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a 32 bits endianness bitswap on x.
|
||||
* @param x the variable to bitswap
|
||||
|
||||
42
src/core/container_func.hpp
Normal file
42
src/core/container_func.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/* $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 container_func.hpp Functions related to use of containers. */
|
||||
|
||||
#ifndef CONTAINER_FUNC_HPP
|
||||
#define CONTAINER_FUNC_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
template <typename C, typename UP> unsigned int container_unordered_remove_if (C &container, UP predicate) {
|
||||
unsigned int removecount = 0;
|
||||
for (auto it = container.begin(); it != container.end();) {
|
||||
if (predicate(*it)) {
|
||||
removecount++;
|
||||
if (std::next(it) != container.end()) {
|
||||
*it = std::move(container.back());
|
||||
container.pop_back();
|
||||
} else {
|
||||
container.pop_back();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return removecount;
|
||||
}
|
||||
|
||||
template <typename C, typename V> unsigned int container_unordered_remove(C &container, const V &value) {
|
||||
return container_unordered_remove_if (container, [&](const typename C::value_type &v) {
|
||||
return v == value;
|
||||
});
|
||||
}
|
||||
|
||||
#endif /* CONTAINER_FUNC_HPP */
|
||||
102
src/core/dyn_arena_alloc.hpp
Normal file
102
src/core/dyn_arena_alloc.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/* $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 dyn_arena_alloc.hpp Dynamic chunk-size arena allocator. */
|
||||
|
||||
#ifndef DYN_ARENA_ALLOC_HPP
|
||||
#define DYN_ARENA_ALLOC_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Custom arena allocator for uniform-size allocations of a variable size.
|
||||
* The allocation and chunk sizes may only be changed when the arena is empty.
|
||||
*/
|
||||
class DynUniformArenaAllocator {
|
||||
std::vector<void *> used_blocks;
|
||||
|
||||
void *current_block = nullptr;
|
||||
void *last_freed = nullptr;
|
||||
size_t next_position = 0;
|
||||
|
||||
size_t item_size = 0;
|
||||
size_t items_per_chunk = 0;
|
||||
|
||||
void NewBlock()
|
||||
{
|
||||
current_block = malloc(item_size * items_per_chunk);
|
||||
assert(current_block != nullptr);
|
||||
next_position = 0;
|
||||
used_blocks.push_back(current_block);
|
||||
}
|
||||
|
||||
public:
|
||||
DynUniformArenaAllocator() = default;
|
||||
DynUniformArenaAllocator(const DynUniformArenaAllocator &other) = delete;
|
||||
DynUniformArenaAllocator& operator=(const DynUniformArenaAllocator &other) = delete;
|
||||
|
||||
~DynUniformArenaAllocator()
|
||||
{
|
||||
EmptyArena();
|
||||
}
|
||||
|
||||
void EmptyArena()
|
||||
{
|
||||
current_block = nullptr;
|
||||
last_freed = nullptr;
|
||||
next_position = 0;
|
||||
for (void *block : used_blocks) {
|
||||
free(block);
|
||||
}
|
||||
used_blocks.clear();
|
||||
}
|
||||
|
||||
void ResetArena()
|
||||
{
|
||||
EmptyArena();
|
||||
item_size = 0;
|
||||
items_per_chunk = 0;
|
||||
}
|
||||
|
||||
void *Allocate() {
|
||||
assert(item_size != 0);
|
||||
if (last_freed) {
|
||||
void *ptr = last_freed;
|
||||
last_freed = *reinterpret_cast<void**>(ptr);
|
||||
return ptr;
|
||||
} else {
|
||||
if (current_block == nullptr || next_position == items_per_chunk) {
|
||||
NewBlock();
|
||||
}
|
||||
void *out = reinterpret_cast<char *>(current_block) + (item_size * next_position);
|
||||
next_position++;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
void Free(void *ptr) {
|
||||
if (!ptr) return;
|
||||
assert(current_block != nullptr);
|
||||
|
||||
*reinterpret_cast<void**>(ptr) = last_freed;
|
||||
last_freed = ptr;
|
||||
}
|
||||
|
||||
void SetParameters(size_t item_size, size_t items_per_chunk)
|
||||
{
|
||||
if (item_size < sizeof(void *)) item_size = sizeof(void *);
|
||||
if (this->item_size == item_size && this->items_per_chunk == items_per_chunk) return;
|
||||
|
||||
assert(current_block == nullptr);
|
||||
this->item_size = item_size;
|
||||
this->items_per_chunk = items_per_chunk;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DYN_ARENA_ALLOC_HPP */
|
||||
@@ -19,25 +19,33 @@
|
||||
#if TTD_ENDIAN == TTD_BIG_ENDIAN
|
||||
#define FROM_BE16(x) (x)
|
||||
#define FROM_BE32(x) (x)
|
||||
#define FROM_BE64(x) (x)
|
||||
#define TO_BE16(x) (x)
|
||||
#define TO_BE32(x) (x)
|
||||
#define TO_BE32X(x) (x)
|
||||
#define TO_BE64(x) (x)
|
||||
#define FROM_LE16(x) BSWAP16(x)
|
||||
#define FROM_LE32(x) BSWAP32(x)
|
||||
#define FROM_LE64(x) BSWAP64(x)
|
||||
#define TO_LE16(x) BSWAP16(x)
|
||||
#define TO_LE32(x) BSWAP32(x)
|
||||
#define TO_LE32X(x) BSWAP32(x)
|
||||
#define TO_LE64(x) BSWAP64(x)
|
||||
#else
|
||||
#define FROM_BE16(x) BSWAP16(x)
|
||||
#define FROM_BE32(x) BSWAP32(x)
|
||||
#define FROM_BE64(x) BSWAP64(x)
|
||||
#define TO_BE16(x) BSWAP16(x)
|
||||
#define TO_BE32(x) BSWAP32(x)
|
||||
#define TO_BE32X(x) BSWAP32(x)
|
||||
#define TO_BE64(x) BSWAP64(x)
|
||||
#define FROM_LE16(x) (x)
|
||||
#define FROM_LE32(x) (x)
|
||||
#define FROM_LE64(x) (x)
|
||||
#define TO_LE16(x) (x)
|
||||
#define TO_LE32(x) (x)
|
||||
#define TO_LE32X(x) (x)
|
||||
#define TO_LE64(x) (x)
|
||||
#endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
|
||||
|
||||
static inline uint16 ReadLE16Aligned(const void *x)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -318,6 +318,18 @@ static inline uint CeilDiv(uint a, uint b)
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes ceil(a / b) for non-negative a and b (templated).
|
||||
* @param a Numerator
|
||||
* @param b Denominator
|
||||
* @return Quotient, rounded up
|
||||
*/
|
||||
template <typename T>
|
||||
static inline T CeilDivT(T a, T b)
|
||||
{
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes ceil(a / b) * b for non-negative a and b.
|
||||
* @param a Numerator
|
||||
@@ -329,6 +341,18 @@ static inline uint Ceil(uint a, uint b)
|
||||
return CeilDiv(a, b) * b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes ceil(a / b) * b for non-negative a and b (templated).
|
||||
* @param a Numerator
|
||||
* @param b Denominator
|
||||
* @return a rounded up to the nearest multiple of b.
|
||||
*/
|
||||
template <typename T>
|
||||
static inline T CeilT(T a, T b)
|
||||
{
|
||||
return CeilDivT<T>(a, b) * b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes round(a / b) for signed a and unsigned b.
|
||||
* @param a Numerator
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
template<typename Tkey, typename Tvalue, typename Tcompare>
|
||||
template<typename Tkey, typename Tvalue, typename Tcontainer, typename Tcompare>
|
||||
class MultiMap;
|
||||
|
||||
/**
|
||||
@@ -23,14 +23,15 @@ class MultiMap;
|
||||
* @tparam Tmap_iter Iterator type for the map in the MultiMap.
|
||||
* @tparam Tlist_iter Iterator type for the lists in the MultiMap.
|
||||
* @tparam Tkey Key type of the MultiMap.
|
||||
* @tparam Tvalue Value type of the MultMap.
|
||||
* @tparam Tvalue Value type of the MultiMap.
|
||||
* @tparam Tcontainer Container type for the values of the MultiMap.
|
||||
* @tparam Tcompare Comparator type for keys of the MultiMap.
|
||||
*/
|
||||
template<class Tmap_iter, class Tlist_iter, class Tkey, class Tvalue, class Tcompare>
|
||||
template<class Tmap_iter, class Tlist_iter, class Tkey, class Tvalue, class Tcontainer, class Tcompare>
|
||||
class MultiMapIterator {
|
||||
protected:
|
||||
friend class MultiMap<Tkey, Tvalue, Tcompare>;
|
||||
typedef MultiMapIterator<Tmap_iter, Tlist_iter, Tkey, Tvalue, Tcompare> Self;
|
||||
friend class MultiMap<Tkey, Tvalue, Tcontainer, Tcompare>;
|
||||
typedef MultiMapIterator<Tmap_iter, Tlist_iter, Tkey, Tvalue, Tcontainer, Tcompare> Self;
|
||||
|
||||
Tlist_iter list_iter; ///< Iterator pointing to current position in the current list of items with equal keys.
|
||||
Tmap_iter map_iter; ///< Iterator pointing to the position of the current list of items with equal keys in the map.
|
||||
@@ -201,8 +202,8 @@ public:
|
||||
* @param iter2 Second iterator to compare.
|
||||
* @return If iter1 and iter2 are equal.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tlist_iter2, class Tkey, class Tvalue1, class Tvalue2, class Tcompare>
|
||||
bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcompare> &iter1, const MultiMapIterator<Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcompare> &iter2)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tlist_iter2, class Tkey, class Tvalue1, class Tvalue2, class Tcontainer1, class Tcontainer2, class Tcompare>
|
||||
bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcontainer1, Tcompare> &iter1, const MultiMapIterator<Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcontainer2, Tcompare> &iter2)
|
||||
{
|
||||
if (iter1.GetMapIter() != iter2.GetMapIter()) return false;
|
||||
if (!iter1.ListValid()) return !iter2.ListValid();
|
||||
@@ -218,8 +219,8 @@ bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, T
|
||||
* @param iter2 Second iterator to compare.
|
||||
* @return If iter1 and iter2 are not equal.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tlist_iter2, class Tkey, class Tvalue1, class Tvalue2, class Tcompare>
|
||||
bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcompare> &iter1, const MultiMapIterator<Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcompare> &iter2)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tlist_iter2, class Tkey, class Tvalue1, class Tvalue2, class Tcontainer1, class Tcontainer2, class Tcompare>
|
||||
bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcontainer1, Tcompare> &iter1, const MultiMapIterator<Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcontainer2, Tcompare> &iter2)
|
||||
{
|
||||
return !(iter1 == iter2);
|
||||
}
|
||||
@@ -232,8 +233,8 @@ bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, T
|
||||
* @param iter2 Map iterator.
|
||||
* @return If iter1 points to the begin of the list pointed to by iter2.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcompare >
|
||||
bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcompare> &iter1, const Tmap_iter2 &iter2)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcontainer, class Tcompare >
|
||||
bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcontainer, Tcompare> &iter1, const Tmap_iter2 &iter2)
|
||||
{
|
||||
return !iter1.ListValid() && iter1.GetMapIter() == iter2;
|
||||
}
|
||||
@@ -244,8 +245,8 @@ bool operator==(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tc
|
||||
* @param iter2 Map iterator.
|
||||
* @return If iter1 doesn't point to the begin of the list pointed to by iter2.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcompare >
|
||||
bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcompare> &iter1, const Tmap_iter2 &iter2)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcontainer, class Tcompare >
|
||||
bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcontainer, Tcompare> &iter1, const Tmap_iter2 &iter2)
|
||||
{
|
||||
return iter1.ListValid() || iter1.GetMapIter() != iter2;
|
||||
}
|
||||
@@ -256,8 +257,8 @@ bool operator!=(const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tc
|
||||
* @param iter1 MultiMap iterator.
|
||||
* @return If iter1 points to the begin of the list pointed to by iter2.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcompare >
|
||||
bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcompare> &iter1)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcontainer, class Tcompare >
|
||||
bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcontainer, Tcompare> &iter1)
|
||||
{
|
||||
return !iter1.ListValid() && iter1.GetMapIter() == iter2;
|
||||
}
|
||||
@@ -268,8 +269,8 @@ bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlis
|
||||
* @param iter1 MultiMap iterator.
|
||||
* @return If iter1 doesn't point to the begin of the list pointed to by iter2.
|
||||
*/
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcompare >
|
||||
bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcompare> &iter1)
|
||||
template<class Tmap_iter1, class Tlist_iter1, class Tmap_iter2, class Tkey, class Tvalue, class Tcontainer, class Tcompare >
|
||||
bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlist_iter1, Tkey, Tvalue, Tcontainer, Tcompare> &iter1)
|
||||
{
|
||||
return iter1.ListValid() || iter1.GetMapIter() != iter2;
|
||||
}
|
||||
@@ -282,10 +283,10 @@ bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator<Tmap_iter1, Tlis
|
||||
* STL-compatible members are named in STL style, all others are named in OpenTTD
|
||||
* style.
|
||||
*/
|
||||
template<typename Tkey, typename Tvalue, typename Tcompare = std::less<Tkey> >
|
||||
class MultiMap : public std::map<Tkey, std::list<Tvalue>, Tcompare > {
|
||||
template<typename Tkey, typename Tvalue, typename Tcontainer = std::list<Tvalue>, typename Tcompare = std::less<Tkey> >
|
||||
class MultiMap : public std::map<Tkey, Tcontainer, Tcompare > {
|
||||
public:
|
||||
typedef typename std::list<Tvalue> List;
|
||||
typedef Tcontainer List;
|
||||
typedef typename List::iterator ListIterator;
|
||||
typedef typename List::const_iterator ConstListIterator;
|
||||
|
||||
@@ -293,8 +294,8 @@ public:
|
||||
typedef typename Map::iterator MapIterator;
|
||||
typedef typename Map::const_iterator ConstMapIterator;
|
||||
|
||||
typedef MultiMapIterator<MapIterator, ListIterator, Tkey, Tvalue, Tcompare> iterator;
|
||||
typedef MultiMapIterator<ConstMapIterator, ConstListIterator, Tkey, const Tvalue, Tcompare> const_iterator;
|
||||
typedef MultiMapIterator<MapIterator, ListIterator, Tkey, Tvalue, Tcontainer, Tcompare> iterator;
|
||||
typedef MultiMapIterator<ConstMapIterator, ConstListIterator, Tkey, const Tvalue, Tcontainer, Tcompare> const_iterator;
|
||||
|
||||
/**
|
||||
* Erase the value pointed to by an iterator. The iterator may be invalid afterwards.
|
||||
|
||||
@@ -196,6 +196,7 @@ DEFINE_POOL_METHOD(void)::FreeItem(size_t index)
|
||||
DEFINE_POOL_METHOD(void)::CleanPool()
|
||||
{
|
||||
this->cleaning = true;
|
||||
Titem::PreCleanPool();
|
||||
for (size_t i = 0; i < this->first_unused; i++) {
|
||||
delete this->Get(i); // 'delete NULL;' is very valid
|
||||
}
|
||||
|
||||
@@ -286,6 +286,13 @@ struct Pool : PoolBase {
|
||||
* @note it's called only when !CleaningPool()
|
||||
*/
|
||||
static inline void PostDestructor(size_t index) { }
|
||||
|
||||
/**
|
||||
* Dummy function called before a pool is about to be cleaned.
|
||||
* If you want to use it, override it in PoolItem's subclass.
|
||||
* @note it's called only when CleaningPool()
|
||||
*/
|
||||
static inline void PreCleanPool() { }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -195,10 +195,10 @@ public:
|
||||
inline void Push(const Titem &item)
|
||||
{
|
||||
if (this->value != Tinvalid) {
|
||||
ThreadMutexLocker lock(_pool.GetMutex());
|
||||
Tindex new_item = _pool.Create();
|
||||
ThreadMutexLocker lock(SmallStack::GetPool().GetMutex());
|
||||
Tindex new_item = SmallStack::GetPool().Create();
|
||||
if (new_item != Tmax_size) {
|
||||
PooledSmallStack &pushed = _pool.Get(new_item);
|
||||
PooledSmallStack &pushed = SmallStack::GetPool().Get(new_item);
|
||||
pushed.value = this->value;
|
||||
pushed.next = this->next;
|
||||
pushed.branch_count = 0;
|
||||
@@ -218,16 +218,16 @@ public:
|
||||
if (this->next == Tmax_size) {
|
||||
this->value = Tinvalid;
|
||||
} else {
|
||||
ThreadMutexLocker lock(_pool.GetMutex());
|
||||
PooledSmallStack &popped = _pool.Get(this->next);
|
||||
ThreadMutexLocker lock(SmallStack::GetPool().GetMutex());
|
||||
PooledSmallStack &popped = SmallStack::GetPool().Get(this->next);
|
||||
this->value = popped.value;
|
||||
if (popped.branch_count == 0) {
|
||||
_pool.Destroy(this->next);
|
||||
SmallStack::GetPool().Destroy(this->next);
|
||||
} else {
|
||||
--popped.branch_count;
|
||||
/* We can't use Branch() here as we already have the mutex.*/
|
||||
if (popped.next != Tmax_size) {
|
||||
++(_pool.Get(popped.next).branch_count);
|
||||
++(SmallStack::GetPool().Get(popped.next).branch_count);
|
||||
}
|
||||
}
|
||||
/* Accessing popped here is no problem as the pool will only set
|
||||
@@ -257,11 +257,11 @@ public:
|
||||
{
|
||||
if (item == Tinvalid || item == this->value) return true;
|
||||
if (this->next != Tmax_size) {
|
||||
ThreadMutexLocker lock(_pool.GetMutex());
|
||||
ThreadMutexLocker lock(SmallStack::GetPool().GetMutex());
|
||||
const SmallStack *in_list = this;
|
||||
do {
|
||||
in_list = static_cast<const SmallStack *>(
|
||||
static_cast<const Item *>(&_pool.Get(in_list->next)));
|
||||
static_cast<const Item *>(&SmallStack::GetPool().Get(in_list->next)));
|
||||
if (in_list->value == item) return true;
|
||||
} while (in_list->next != Tmax_size);
|
||||
}
|
||||
@@ -269,7 +269,11 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
static SmallStackPool _pool;
|
||||
static SmallStackPool &GetPool()
|
||||
{
|
||||
static SmallStackPool pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a branch in the pool if necessary.
|
||||
@@ -277,8 +281,8 @@ protected:
|
||||
inline void Branch()
|
||||
{
|
||||
if (this->next != Tmax_size) {
|
||||
ThreadMutexLocker lock(_pool.GetMutex());
|
||||
++(_pool.Get(this->next).branch_count);
|
||||
ThreadMutexLocker lock(SmallStack::GetPool().GetMutex());
|
||||
++(SmallStack::GetPool().Get(this->next).branch_count);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -158,6 +158,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new item at a specific position into the vector, moving all following items.
|
||||
* @param item Position at which the new item should be inserted
|
||||
* @return pointer to the new item
|
||||
*/
|
||||
inline T *Insert(T *item)
|
||||
{
|
||||
assert(item >= this->Begin() && item <= this->End());
|
||||
|
||||
size_t to_move = this->End() - item;
|
||||
size_t start = item - this->Begin();
|
||||
|
||||
this->Append();
|
||||
if (to_move > 0) MemMoveT(this->Begin() + start + 1, this->Begin() + start, to_move);
|
||||
return this->Begin() + start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the first occurrence of an item.
|
||||
* The '!=' operator of T is used for comparison.
|
||||
@@ -232,13 +249,24 @@ public:
|
||||
* @param count Number of consecutive items to remove.
|
||||
*/
|
||||
void ErasePreservingOrder(uint pos, uint count = 1)
|
||||
{
|
||||
ErasePreservingOrder(this->data + pos, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove items from the vector while preserving the order of other items.
|
||||
* @param item First item to remove.
|
||||
* @param count Number of consecutive items to remove.
|
||||
*/
|
||||
inline void ErasePreservingOrder(T *item, uint count = 1)
|
||||
{
|
||||
if (count == 0) return;
|
||||
assert(pos < this->items);
|
||||
assert(pos + count <= this->items);
|
||||
assert(item >= this->Begin());
|
||||
assert(item + count <= this->End());
|
||||
|
||||
this->items -= count;
|
||||
uint to_move = this->items - pos;
|
||||
if (to_move > 0) MemMoveT(this->data + pos, this->data + pos + count, to_move);
|
||||
ptrdiff_t to_move = this->End() - item;
|
||||
if (to_move > 0) MemMoveT(item, item + count, to_move);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,6 +284,8 @@ public:
|
||||
|
||||
/**
|
||||
* Get the number of items in the list.
|
||||
*
|
||||
* @return The number of items in the list.
|
||||
*/
|
||||
inline uint Length() const
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "network/network.h"
|
||||
#include "language.h"
|
||||
#include "fontcache.h"
|
||||
#include "news_gui.h"
|
||||
|
||||
#include "ai/ai_info.hpp"
|
||||
#include "game/game.hpp"
|
||||
@@ -36,6 +37,38 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WITH_ALLEGRO
|
||||
# include <allegro.h>
|
||||
#endif /* WITH_ALLEGRO */
|
||||
#ifdef WITH_FONTCONFIG
|
||||
# include <fontconfig/fontconfig.h>
|
||||
#endif /* WITH_FONTCONFIG */
|
||||
#ifdef WITH_PNG
|
||||
/* pngconf.h, included by png.h doesn't like something in the
|
||||
* freetype headers. As such it's not alphabetically sorted. */
|
||||
# include <png.h>
|
||||
#endif /* WITH_PNG */
|
||||
#ifdef WITH_FREETYPE
|
||||
# include <ft2build.h>
|
||||
# include FT_FREETYPE_H
|
||||
#endif /* WITH_FREETYPE */
|
||||
#if defined(WITH_ICU_LAYOUT) || defined(WITH_ICU_SORT)
|
||||
# include <unicode/uversion.h>
|
||||
#endif /* WITH_ICU_SORT || WITH_ICU_LAYOUT */
|
||||
#ifdef WITH_LZMA
|
||||
# include <lzma.h>
|
||||
#endif
|
||||
#ifdef WITH_LZO
|
||||
#include <lzo/lzo1x.h>
|
||||
#endif
|
||||
#ifdef WITH_SDL
|
||||
# include "sdl.h"
|
||||
# include <SDL.h>
|
||||
#endif /* WITH_SDL */
|
||||
#ifdef WITH_ZLIB
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/* static */ const char *CrashLog::message = NULL;
|
||||
@@ -180,39 +213,6 @@ char *CrashLog::LogConfiguration(char *buffer, const char *last) const
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Include these here so it's close to where it's actually used. */
|
||||
#ifdef WITH_ALLEGRO
|
||||
# include <allegro.h>
|
||||
#endif /* WITH_ALLEGRO */
|
||||
#ifdef WITH_FONTCONFIG
|
||||
# include <fontconfig/fontconfig.h>
|
||||
#endif /* WITH_FONTCONFIG */
|
||||
#ifdef WITH_PNG
|
||||
/* pngconf.h, included by png.h doesn't like something in the
|
||||
* freetype headers. As such it's not alphabetically sorted. */
|
||||
# include <png.h>
|
||||
#endif /* WITH_PNG */
|
||||
#ifdef WITH_FREETYPE
|
||||
# include <ft2build.h>
|
||||
# include FT_FREETYPE_H
|
||||
#endif /* WITH_FREETYPE */
|
||||
#if defined(WITH_ICU_LAYOUT) || defined(WITH_ICU_SORT)
|
||||
# include <unicode/uversion.h>
|
||||
#endif /* WITH_ICU_SORT || WITH_ICU_LAYOUT */
|
||||
#ifdef WITH_LZMA
|
||||
# include <lzma.h>
|
||||
#endif
|
||||
#ifdef WITH_LZO
|
||||
#include <lzo/lzo1x.h>
|
||||
#endif
|
||||
#ifdef WITH_SDL
|
||||
# include "sdl.h"
|
||||
# include <SDL.h>
|
||||
#endif /* WITH_SDL */
|
||||
#ifdef WITH_ZLIB
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Writes information (versions) of the used libraries.
|
||||
* @param buffer The begin where to write at.
|
||||
@@ -309,6 +309,27 @@ char *CrashLog::LogGamelog(char *buffer, const char *last) const
|
||||
return CrashLog::gamelog_buffer + seprintf(CrashLog::gamelog_buffer, last, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes any recent news messages to the buffer.
|
||||
* @param buffer The begin where to write at.
|
||||
* @param last The last position in the buffer to write to.
|
||||
* @return the position of the \c '\0' character after the buffer.
|
||||
*/
|
||||
char *CrashLog::LogRecentNews(char *buffer, const char *last) const
|
||||
{
|
||||
buffer += seprintf(buffer, last, "Recent news messages:\n");
|
||||
|
||||
for (NewsItem *news = _oldest_news; news != NULL; news = news->next) {
|
||||
YearMonthDay ymd;
|
||||
ConvertDateToYMD(news->date, &ymd);
|
||||
buffer += seprintf(buffer, last, "(%i-%02i-%02i) StringID: %u, Type: %u, Ref1: %u, %u, Ref2: %u, %u\n",
|
||||
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
|
||||
news->reftype1, news->ref1, news->reftype2, news->ref2);
|
||||
}
|
||||
buffer += seprintf(buffer, last, "\n");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the crash log buffer with all data of a crash log.
|
||||
* @param buffer The begin where to write at.
|
||||
@@ -335,6 +356,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const
|
||||
buffer = this->LogLibraries(buffer, last);
|
||||
buffer = this->LogModules(buffer, last);
|
||||
buffer = this->LogGamelog(buffer, last);
|
||||
buffer = this->LogRecentNews(buffer, last);
|
||||
|
||||
buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n");
|
||||
return buffer;
|
||||
@@ -389,7 +411,7 @@ bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const
|
||||
seprintf(filename, filename_last, "%scrash.sav", _personal_dir);
|
||||
|
||||
/* Don't do a threaded saveload. */
|
||||
return SaveOrLoad(filename, SL_SAVE, NO_DIRECTORY, false) == SL_OK;
|
||||
return SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, NO_DIRECTORY, false) == SL_OK;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ protected:
|
||||
char *LogConfiguration(char *buffer, const char *last) const;
|
||||
char *LogLibraries(char *buffer, const char *last) const;
|
||||
char *LogGamelog(char *buffer, const char *last) const;
|
||||
char *LogRecentNews(char *buffer, const char *list) const;
|
||||
|
||||
public:
|
||||
/** Stub destructor to silence some compilers. */
|
||||
|
||||
@@ -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)
|
||||
@@ -100,8 +104,6 @@ char *DumpDebugFacilityNames(char *buf, char *last)
|
||||
return buf;
|
||||
}
|
||||
|
||||
#if !defined(NO_DEBUG_MESSAGES)
|
||||
|
||||
/**
|
||||
* Internal function for outputting the debug line.
|
||||
* @param dbg Debug category.
|
||||
@@ -137,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
|
||||
@@ -168,7 +170,6 @@ void CDECL debug(const char *dbg, const char *format, ...)
|
||||
|
||||
debug_print(dbg, buf);
|
||||
}
|
||||
#endif /* NO_DEBUG_MESSAGES */
|
||||
|
||||
/**
|
||||
* Set debugging levels by parsing the text in \a s.
|
||||
|
||||
50
src/debug.h
50
src/debug.h
@@ -28,37 +28,33 @@
|
||||
* 6.. - extremely detailed spamming
|
||||
*/
|
||||
|
||||
#ifdef NO_DEBUG_MESSAGES
|
||||
#define DEBUG(name, level, ...) { }
|
||||
#else /* NO_DEBUG_MESSAGES */
|
||||
/**
|
||||
* Output a line of debugging information.
|
||||
* @param name Category
|
||||
* @param level Debugging level, higher levels means more detailed information.
|
||||
*/
|
||||
#define DEBUG(name, level, ...) if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug(#name, __VA_ARGS__)
|
||||
/**
|
||||
* Output a line of debugging information.
|
||||
* @param name Category
|
||||
* @param level Debugging level, higher levels means more detailed information.
|
||||
*/
|
||||
#define DEBUG(name, level, ...) if ((level) == 0 || _debug_ ## name ## _level >= (level)) debug(#name, __VA_ARGS__)
|
||||
|
||||
extern int _debug_driver_level;
|
||||
extern int _debug_grf_level;
|
||||
extern int _debug_map_level;
|
||||
extern int _debug_misc_level;
|
||||
extern int _debug_net_level;
|
||||
extern int _debug_sprite_level;
|
||||
extern int _debug_oldloader_level;
|
||||
extern int _debug_npf_level;
|
||||
extern int _debug_yapf_level;
|
||||
extern int _debug_freetype_level;
|
||||
extern int _debug_script_level;
|
||||
extern int _debug_sl_level;
|
||||
extern int _debug_gamelog_level;
|
||||
extern int _debug_desync_level;
|
||||
extern int _debug_console_level;
|
||||
extern int _debug_driver_level;
|
||||
extern int _debug_grf_level;
|
||||
extern int _debug_map_level;
|
||||
extern int _debug_misc_level;
|
||||
extern int _debug_net_level;
|
||||
extern int _debug_sprite_level;
|
||||
extern int _debug_oldloader_level;
|
||||
extern int _debug_npf_level;
|
||||
extern int _debug_yapf_level;
|
||||
extern int _debug_freetype_level;
|
||||
extern int _debug_script_level;
|
||||
extern int _debug_sl_level;
|
||||
extern int _debug_gamelog_level;
|
||||
extern int _debug_desync_level;
|
||||
extern int _debug_console_level;
|
||||
#ifdef RANDOM_DEBUG
|
||||
extern int _debug_random_level;
|
||||
extern int _debug_random_level;
|
||||
#endif
|
||||
|
||||
void CDECL debug(const char *dbg, const char *format, ...) WARN_FORMAT(2, 3);
|
||||
#endif /* NO_DEBUG_MESSAGES */
|
||||
void CDECL debug(const char *dbg, const char *format, ...) WARN_FORMAT(2, 3);
|
||||
|
||||
char *DumpDebugFacilityNames(char *buf, char *last);
|
||||
void SetDebugString(const char *s);
|
||||
|
||||
@@ -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.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,7 @@ static void TrainDepotMoveVehicle(const Vehicle *wagon, VehicleID sel, const Veh
|
||||
|
||||
static VehicleCellSize _base_block_sizes_depot[VEH_COMPANY_END]; ///< Cell size for vehicle images in the depot view.
|
||||
static VehicleCellSize _base_block_sizes_purchase[VEH_COMPANY_END]; ///< Cell size for vehicle images in the purchase list.
|
||||
static uint _consistent_train_width; ///< Whether trains of all lengths are consistently scaled. Either TRAININFO_DEFAULT_VEHICLE_WIDTH, VEHICLEINFO_FULL_VEHICLE_WIDTH, or 0.
|
||||
|
||||
/**
|
||||
* Get the GUI cell size for a vehicle image.
|
||||
@@ -219,6 +220,34 @@ void InitDepotWindowBlockSizes()
|
||||
InitBlocksizeForVehicles(vt, EIT_IN_DEPOT);
|
||||
InitBlocksizeForVehicles(vt, EIT_PURCHASE);
|
||||
}
|
||||
|
||||
_consistent_train_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
|
||||
bool first = true;
|
||||
const Engine *e;
|
||||
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
|
||||
if (!e->IsEnabled()) continue;
|
||||
|
||||
uint w = TRAININFO_DEFAULT_VEHICLE_WIDTH;
|
||||
if (e->GetGRF() != NULL && is_custom_sprite(e->u.rail.image_index)) {
|
||||
w = e->GetGRF()->traininfo_vehicle_width;
|
||||
if (w != VEHICLEINFO_FULL_VEHICLE_WIDTH) {
|
||||
/* Hopeless.
|
||||
* This is a NewGRF vehicle that uses TRAININFO_DEFAULT_VEHICLE_WIDTH.
|
||||
* If the vehicles are shorter than 8/8 we have fractional lengths, which are not consistent after rounding.
|
||||
*/
|
||||
_consistent_train_width = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first) {
|
||||
_consistent_train_width = w;
|
||||
first = false;
|
||||
} else if (w != _consistent_train_width) {
|
||||
_consistent_train_width = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DepotSellAllConfirmationCallback(Window *w, bool confirmed);
|
||||
@@ -292,7 +321,10 @@ struct DepotWindow : Window {
|
||||
const Train *u = Train::From(v);
|
||||
free_wagon = u->IsFreeWagon();
|
||||
|
||||
uint x_space = free_wagon ? ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) : 0;
|
||||
uint x_space = free_wagon ?
|
||||
ScaleGUITrad(_consistent_train_width != 0 ? _consistent_train_width : TRAININFO_DEFAULT_VEHICLE_WIDTH) :
|
||||
0;
|
||||
|
||||
DrawTrainImage(u, image_left + (rtl ? 0 : x_space), image_right - (rtl ? x_space : 0), sprite_y - 1,
|
||||
this->sel, EIT_IN_DEPOT, free_wagon ? 0 : this->hscroll->GetPosition(), this->vehicle_over);
|
||||
|
||||
@@ -340,6 +372,29 @@ struct DepotWindow : Window {
|
||||
|
||||
/* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */
|
||||
const NWidgetCore *wid = this->GetWidget<NWidgetCore>(WID_D_MATRIX);
|
||||
|
||||
/* Draw vertical separators at whole tiles.
|
||||
* This only works in two cases:
|
||||
* - All vehicles use VEHICLEINFO_FULL_VEHICLE_WIDTH as reference width.
|
||||
* - All vehicles are 8/8. This cannot be checked for NewGRF, so instead we check for "all vehicles are original vehicles".
|
||||
*/
|
||||
if (this->type == VEH_TRAIN && _consistent_train_width != 0) {
|
||||
int w = ScaleGUITrad(2 * _consistent_train_width);
|
||||
int col = _colour_gradient[wid->colour][4];
|
||||
int image_left = rtl ? r.left + this->count_width : r.left + this->header_width;
|
||||
int image_right = rtl ? r.right - this->header_width : r.right - this->count_width;
|
||||
int first_line = w + (-this->hscroll->GetPosition()) % w;
|
||||
if (rtl) {
|
||||
for (int x = image_right - first_line; x >= image_left; x -= w) {
|
||||
GfxDrawLine(x, r.top, x, r.bottom, col, 1, 3);
|
||||
}
|
||||
} else {
|
||||
for (int x = image_left + first_line; x <= image_right; x += w) {
|
||||
GfxDrawLine(x, r.top, x, r.bottom, col, 1, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16 rows_in_display = wid->current_y / wid->resize_y;
|
||||
|
||||
uint16 num = this->vscroll->GetPosition() * this->num_columns;
|
||||
@@ -445,7 +500,8 @@ struct DepotWindow : Window {
|
||||
switch (this->type) {
|
||||
case VEH_TRAIN:
|
||||
if (wagon) return MODE_ERROR;
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
case VEH_ROAD:
|
||||
if (xm <= this->flag_width) return MODE_START_STOP;
|
||||
break;
|
||||
@@ -505,15 +561,12 @@ struct DepotWindow : Window {
|
||||
this->sel = INVALID_VEHICLE;
|
||||
TrainDepotMoveVehicle(v, sel, gdvp.head);
|
||||
} else if (v != NULL) {
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
int image = v->GetImage(rtl ? DIR_E : DIR_W, EIT_IN_DEPOT);
|
||||
SetObjectToPlaceWnd(image, GetVehiclePalette(v), HT_DRAG, this);
|
||||
SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
|
||||
SetMouseCursorVehicle(v, EIT_IN_DEPOT);
|
||||
_cursor.vehchain = _ctrl_pressed;
|
||||
|
||||
this->sel = v->index;
|
||||
this->SetDirty();
|
||||
|
||||
_cursor.short_vehicle_offset = v->IsGroundVehicle() ? (16 - v->GetGroundVehicleCache()->cached_veh_length * 2) * (rtl ? -1 : 1) : 0;
|
||||
_cursor.vehchain = _ctrl_pressed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -663,7 +716,8 @@ struct DepotWindow : Window {
|
||||
DepotSortList(&this->vehicle_list);
|
||||
|
||||
uint new_unitnumber_digits = GetUnitNumberDigits(this->vehicle_list);
|
||||
if (this->unitnumber_digits != new_unitnumber_digits) {
|
||||
/* Only increase the size; do not decrease to prevent constant changes */
|
||||
if (this->unitnumber_digits < new_unitnumber_digits) {
|
||||
this->unitnumber_digits = new_unitnumber_digits;
|
||||
this->ReInit();
|
||||
}
|
||||
@@ -681,7 +735,8 @@ struct DepotWindow : Window {
|
||||
}
|
||||
/* Always have 1 empty row, so people can change the setting of the train */
|
||||
this->vscroll->SetCount(this->vehicle_list.Length() + this->wagon_list.Length() + 1);
|
||||
this->hscroll->SetCount(max_width);
|
||||
/* Always make it longer than the longest train, so you can attach vehicles at the end, and also see the next vertical tile separator line */
|
||||
this->hscroll->SetCount(max_width + ScaleGUITrad(2 * VEHICLEINFO_FULL_VEHICLE_WIDTH + 1));
|
||||
} else {
|
||||
this->vscroll->SetCount(CeilDiv(this->vehicle_list.Length(), this->num_columns));
|
||||
}
|
||||
@@ -853,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ void DisasterVehicle::UpdateImage()
|
||||
{
|
||||
SpriteID img = this->image_override;
|
||||
if (img == 0) img = _disaster_images[this->subtype][this->direction];
|
||||
this->cur_image = img;
|
||||
this->sprite_seq.Set(img);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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();
|
||||
@@ -499,7 +499,8 @@ static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
|
||||
v->tick_counter++;
|
||||
if (HasBit(v->tick_counter, 0)) return true;
|
||||
|
||||
if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
|
||||
SpriteID &cur_image = v->sprite_seq.seq[0].sprite;
|
||||
if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1;
|
||||
|
||||
v->UpdatePositionAndViewport();
|
||||
|
||||
@@ -972,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();
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ void CcBuildDocks(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
}
|
||||
|
||||
void CcBuildCanal(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
||||
void CcPlaySound_SPLAT_WATER(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_02_SPLAT_WATER, tile);
|
||||
}
|
||||
@@ -56,8 +56,8 @@ void CcBuildCanal(const CommandCost &result, TileIndex tile, uint32 p1, 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)
|
||||
@@ -235,10 +235,10 @@ struct BuildDocksToolbarWindow : Window {
|
||||
GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
|
||||
break;
|
||||
case DDSP_CREATE_WATER:
|
||||
DoCommandP(end_tile, start_tile, (_game_mode == GM_EDITOR && _ctrl_pressed) ? WATER_CLASS_SEA : WATER_CLASS_CANAL, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_BUILD_CANALS), CcBuildCanal);
|
||||
DoCommandP(end_tile, start_tile, (_game_mode == GM_EDITOR && _ctrl_pressed) ? WATER_CLASS_SEA : WATER_CLASS_CANAL, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_BUILD_CANALS), CcPlaySound_SPLAT_WATER);
|
||||
break;
|
||||
case DDSP_CREATE_RIVER:
|
||||
DoCommandP(end_tile, start_tile, WATER_CLASS_RIVER, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_PLACE_RIVERS), CcBuildCanal);
|
||||
DoCommandP(end_tile, start_tile, WATER_CLASS_RIVER, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_PLACE_RIVERS), CcPlaySound_SPLAT_WATER);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
@@ -433,7 +433,7 @@ public:
|
||||
* Never make the window smaller to avoid oscillating if the size change affects the acceptance.
|
||||
* (This is the case, if making the window bigger moves the mouse into the window.) */
|
||||
if (top > bottom) {
|
||||
ResizeWindow(this, 0, top - bottom);
|
||||
ResizeWindow(this, 0, top - bottom, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.. */
|
||||
@@ -654,12 +654,16 @@ static void CompanyCheckBankrupt(Company *c)
|
||||
*/
|
||||
static void CompaniesGenStatistics()
|
||||
{
|
||||
Station *st;
|
||||
/* Check for bankruptcy each month */
|
||||
Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
CompanyCheckBankrupt(c);
|
||||
}
|
||||
|
||||
Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
|
||||
Company *c;
|
||||
|
||||
if (!_settings_game.economy.infrastructure_maintenance) {
|
||||
Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
cur_company.Change(st->owner);
|
||||
CommandCost cost(EXPENSES_PROPERTY, _price[PR_STATION_VALUE] >> 1);
|
||||
@@ -688,18 +692,14 @@ static void CompaniesGenStatistics()
|
||||
}
|
||||
cur_company.Restore();
|
||||
|
||||
/* Check for bankruptcy each month */
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
CompanyCheckBankrupt(c);
|
||||
}
|
||||
|
||||
/* Only run the economic statics and update company stats every 3rd month (1st of quarter). */
|
||||
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++;
|
||||
|
||||
@@ -1062,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;
|
||||
}
|
||||
@@ -1138,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)) {
|
||||
@@ -1147,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,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)
|
||||
@@ -1317,7 +1317,8 @@ static uint GetLoadAmount(Vehicle *v)
|
||||
/* Scale load amount the same as capacity */
|
||||
if (HasBit(e->info.misc_flags, EF_NO_DEFAULT_CARGO_MULTIPLIER) && !air_mail) load_amount = CeilDiv(load_amount * CargoSpec::Get(v->cargo_type)->multiplier, 0x100);
|
||||
|
||||
return load_amount;
|
||||
/* Zero load amount breaks a lot of things. */
|
||||
return max(1u, load_amount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
|
||||
@@ -1642,15 +1643,14 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
if (v->cargo_cap == 0) continue;
|
||||
artic_part++;
|
||||
|
||||
uint load_amount = GetLoadAmount(v);
|
||||
|
||||
GoodsEntry *ge = &st->goods[v->cargo_type];
|
||||
|
||||
if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (front->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
||||
uint cargo_count = v->cargo.UnloadCount();
|
||||
uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
|
||||
uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, GetLoadAmount(v)) : cargo_count;
|
||||
bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here?
|
||||
|
||||
assert(payment != NULL);
|
||||
payment->SetCargo(v->cargo_type);
|
||||
|
||||
if (!HasBit(ge->status, GoodsEntry::GES_ACCEPTANCE) && v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER) > 0) {
|
||||
@@ -1726,7 +1726,7 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
/* update stats */
|
||||
int t;
|
||||
switch (front->type) {
|
||||
case VEH_TRAIN: /* FALL THROUGH */
|
||||
case VEH_TRAIN:
|
||||
case VEH_SHIP:
|
||||
t = front->vcache.cached_max_speed;
|
||||
break;
|
||||
@@ -1752,8 +1752,8 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
* has capacity for it, load it on the vehicle. */
|
||||
uint cap_left = v->cargo_cap - v->cargo.StoredCount();
|
||||
if (cap_left > 0 && (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0 || ge->cargo.AvailableCount() > 0)) {
|
||||
if (_settings_game.order.gradual_loading) cap_left = min(cap_left, load_amount);
|
||||
if (v->cargo.StoredCount() == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
|
||||
if (_settings_game.order.gradual_loading) cap_left = min(cap_left, GetLoadAmount(v));
|
||||
|
||||
uint loaded = ge->cargo.Load(cap_left, &v->cargo, st->xy, next_station);
|
||||
if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
|
||||
@@ -1839,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.
|
||||
|
||||
@@ -22,10 +22,26 @@
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
/**
|
||||
* Increment the sprite unless it has reached the end of the animation.
|
||||
* @param v Vehicle to increment sprite of.
|
||||
* @param last Last sprite of animation.
|
||||
* @return true if the sprite was incremented, false if the end was reached.
|
||||
*/
|
||||
static bool IncrementSprite(EffectVehicle *v, SpriteID last)
|
||||
{
|
||||
if (v->sprite_seq.seq[0].sprite != last) {
|
||||
v->sprite_seq.seq[0].sprite++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ChimneySmokeInit(EffectVehicle *v)
|
||||
{
|
||||
uint32 r = Random();
|
||||
v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
|
||||
v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
|
||||
v->progress = GB(r, 16, 3);
|
||||
}
|
||||
|
||||
@@ -40,10 +56,8 @@ static bool ChimneySmokeTick(EffectVehicle *v)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
|
||||
v->cur_image++;
|
||||
} else {
|
||||
v->cur_image = SPR_CHIMNEY_SMOKE_0;
|
||||
if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
|
||||
v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
|
||||
}
|
||||
v->progress = 7;
|
||||
v->UpdatePositionAndViewport();
|
||||
@@ -54,7 +68,7 @@ static bool ChimneySmokeTick(EffectVehicle *v)
|
||||
|
||||
static void SteamSmokeInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_STEAM_SMOKE_0;
|
||||
v->sprite_seq.Set(SPR_STEAM_SMOKE_0);
|
||||
v->progress = 12;
|
||||
}
|
||||
|
||||
@@ -70,9 +84,7 @@ static bool SteamSmokeTick(EffectVehicle *v)
|
||||
}
|
||||
|
||||
if ((v->progress & 0xF) == 4) {
|
||||
if (v->cur_image != SPR_STEAM_SMOKE_4) {
|
||||
v->cur_image++;
|
||||
} else {
|
||||
if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
@@ -86,7 +98,7 @@ static bool SteamSmokeTick(EffectVehicle *v)
|
||||
|
||||
static void DieselSmokeInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_DIESEL_SMOKE_0;
|
||||
v->sprite_seq.Set(SPR_DIESEL_SMOKE_0);
|
||||
v->progress = 0;
|
||||
}
|
||||
|
||||
@@ -98,13 +110,11 @@ static bool DieselSmokeTick(EffectVehicle *v)
|
||||
v->z_pos++;
|
||||
v->UpdatePositionAndViewport();
|
||||
} else if ((v->progress & 7) == 1) {
|
||||
if (v->cur_image != SPR_DIESEL_SMOKE_5) {
|
||||
v->cur_image++;
|
||||
v->UpdatePositionAndViewport();
|
||||
} else {
|
||||
if (!IncrementSprite(v, SPR_DIESEL_SMOKE_5)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
v->UpdatePositionAndViewport();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -112,7 +122,7 @@ static bool DieselSmokeTick(EffectVehicle *v)
|
||||
|
||||
static void ElectricSparkInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_ELECTRIC_SPARK_0;
|
||||
v->sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
|
||||
v->progress = 1;
|
||||
}
|
||||
|
||||
@@ -122,13 +132,12 @@ static bool ElectricSparkTick(EffectVehicle *v)
|
||||
v->progress++;
|
||||
} else {
|
||||
v->progress = 0;
|
||||
if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
|
||||
v->cur_image++;
|
||||
v->UpdatePositionAndViewport();
|
||||
} else {
|
||||
|
||||
if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
v->UpdatePositionAndViewport();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -136,7 +145,7 @@ static bool ElectricSparkTick(EffectVehicle *v)
|
||||
|
||||
static void SmokeInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_SMOKE_0;
|
||||
v->sprite_seq.Set(SPR_SMOKE_0);
|
||||
v->progress = 12;
|
||||
}
|
||||
|
||||
@@ -152,9 +161,7 @@ static bool SmokeTick(EffectVehicle *v)
|
||||
}
|
||||
|
||||
if ((v->progress & 0xF) == 4) {
|
||||
if (v->cur_image != SPR_SMOKE_4) {
|
||||
v->cur_image++;
|
||||
} else {
|
||||
if (!IncrementSprite(v, SPR_SMOKE_4)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
@@ -168,7 +175,7 @@ static bool SmokeTick(EffectVehicle *v)
|
||||
|
||||
static void ExplosionLargeInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_EXPLOSION_LARGE_0;
|
||||
v->sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
|
||||
v->progress = 0;
|
||||
}
|
||||
|
||||
@@ -176,13 +183,11 @@ static bool ExplosionLargeTick(EffectVehicle *v)
|
||||
{
|
||||
v->progress++;
|
||||
if ((v->progress & 3) == 0) {
|
||||
if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
|
||||
v->cur_image++;
|
||||
v->UpdatePositionAndViewport();
|
||||
} else {
|
||||
if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
v->UpdatePositionAndViewport();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -190,7 +195,7 @@ static bool ExplosionLargeTick(EffectVehicle *v)
|
||||
|
||||
static void BreakdownSmokeInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_BREAKDOWN_SMOKE_0;
|
||||
v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
|
||||
v->progress = 0;
|
||||
}
|
||||
|
||||
@@ -198,10 +203,8 @@ static bool BreakdownSmokeTick(EffectVehicle *v)
|
||||
{
|
||||
v->progress++;
|
||||
if ((v->progress & 7) == 0) {
|
||||
if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
|
||||
v->cur_image++;
|
||||
} else {
|
||||
v->cur_image = SPR_BREAKDOWN_SMOKE_0;
|
||||
if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) {
|
||||
v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
|
||||
}
|
||||
v->UpdatePositionAndViewport();
|
||||
}
|
||||
@@ -217,7 +220,7 @@ static bool BreakdownSmokeTick(EffectVehicle *v)
|
||||
|
||||
static void ExplosionSmallInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_EXPLOSION_SMALL_0;
|
||||
v->sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
|
||||
v->progress = 0;
|
||||
}
|
||||
|
||||
@@ -225,13 +228,11 @@ static bool ExplosionSmallTick(EffectVehicle *v)
|
||||
{
|
||||
v->progress++;
|
||||
if ((v->progress & 3) == 0) {
|
||||
if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
|
||||
v->cur_image++;
|
||||
v->UpdatePositionAndViewport();
|
||||
} else {
|
||||
if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) {
|
||||
delete v;
|
||||
return false;
|
||||
}
|
||||
v->UpdatePositionAndViewport();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -239,7 +240,7 @@ static bool ExplosionSmallTick(EffectVehicle *v)
|
||||
|
||||
static void BulldozerInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_BULLDOZER_NE;
|
||||
v->sprite_seq.Set(SPR_BULLDOZER_NE);
|
||||
v->progress = 0;
|
||||
v->animation_state = 0;
|
||||
v->animation_substate = 0;
|
||||
@@ -290,7 +291,7 @@ static bool BulldozerTick(EffectVehicle *v)
|
||||
if ((v->progress & 7) == 0) {
|
||||
const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
|
||||
|
||||
v->cur_image = SPR_BULLDOZER_NE + b->image;
|
||||
v->sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
|
||||
|
||||
v->x_pos += _inc_by_dir[b->direction].x;
|
||||
v->y_pos += _inc_by_dir[b->direction].y;
|
||||
@@ -312,7 +313,7 @@ static bool BulldozerTick(EffectVehicle *v)
|
||||
|
||||
static void BubbleInit(EffectVehicle *v)
|
||||
{
|
||||
v->cur_image = SPR_BUBBLE_GENERATE_0;
|
||||
v->sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
|
||||
v->spritenum = 0;
|
||||
v->progress = 0;
|
||||
}
|
||||
@@ -475,8 +476,8 @@ static bool BubbleTick(EffectVehicle *v)
|
||||
if ((v->progress & 3) != 0) return true;
|
||||
|
||||
if (v->spritenum == 0) {
|
||||
v->cur_image++;
|
||||
if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
|
||||
v->sprite_seq.seq[0].sprite++;
|
||||
if (v->sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
|
||||
v->UpdatePositionAndViewport();
|
||||
return true;
|
||||
}
|
||||
@@ -521,7 +522,7 @@ static bool BubbleTick(EffectVehicle *v)
|
||||
v->x_pos += b->x;
|
||||
v->y_pos += b->y;
|
||||
v->z_pos += b->z;
|
||||
v->cur_image = SPR_BUBBLE_0 + b->image;
|
||||
v->sprite_seq.Set(SPR_BUBBLE_0 + b->image);
|
||||
|
||||
v->UpdatePositionAndViewport();
|
||||
|
||||
@@ -602,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);
|
||||
@@ -646,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;
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
|
||||
{
|
||||
switch (GetTileType(t)) {
|
||||
case MP_RAILWAY:
|
||||
if (!HasCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
switch (GetRailTileType(t)) {
|
||||
case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS:
|
||||
return GetTrackBits(t);
|
||||
@@ -99,7 +99,8 @@ static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
|
||||
break;
|
||||
|
||||
case MP_TUNNELBRIDGE:
|
||||
if (!HasCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
if (GetTunnelBridgeTransportType(t) != TRANSPORT_RAIL) return TRACK_BIT_NONE;
|
||||
if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) {
|
||||
*override = 1 << GetTunnelBridgeDirection(t);
|
||||
}
|
||||
@@ -107,12 +108,12 @@ static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
|
||||
|
||||
case MP_ROAD:
|
||||
if (!IsLevelCrossing(t)) return TRACK_BIT_NONE;
|
||||
if (!HasCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
return GetCrossingRailBits(t);
|
||||
|
||||
case MP_STATION:
|
||||
if (!HasStationRail(t)) return TRACK_BIT_NONE;
|
||||
if (!HasCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
if (!HasRailCatenary(GetRailType(t))) return TRACK_BIT_NONE;
|
||||
return TrackToTrackBits(GetRailStationTrack(t));
|
||||
|
||||
default:
|
||||
@@ -135,7 +136,7 @@ static TrackBits MaskWireBits(TileIndex t, TrackBits tracks)
|
||||
* axis that still display wires to preserve visual continuity. */
|
||||
TileIndex next_tile = TileAddByDiagDir(t, d);
|
||||
RailType rt = GetTileRailType(next_tile);
|
||||
if (rt == INVALID_RAILTYPE || !HasCatenary(rt) ||
|
||||
if (rt == INVALID_RAILTYPE || !HasRailCatenary(rt) ||
|
||||
((TrackStatusToTrackBits(GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0)) & DiagdirReachesTracks(d)) == TRACK_BIT_NONE &&
|
||||
(!HasStationTileRail(next_tile) || GetRailStationAxis(next_tile) != DiagDirToAxis(d) || !CanStationTileHaveWires(next_tile)))) {
|
||||
neighbour_tdb |= DiagdirReachesTrackdirs(ReverseDiagDir(d));
|
||||
@@ -241,7 +242,7 @@ static int GetPCPElevation(TileIndex tile, DiagDirection PCPpos)
|
||||
*
|
||||
* @param ti The Tileinfo to draw the tile for
|
||||
*/
|
||||
void DrawCatenaryOnTunnel(const TileInfo *ti)
|
||||
void DrawRailCatenaryOnTunnel(const TileInfo *ti)
|
||||
{
|
||||
/* xmin, ymin, xmax + 1, ymax + 1 of BB */
|
||||
static const int _tunnel_wire_BB[4][4] = {
|
||||
@@ -255,7 +256,7 @@ void DrawCatenaryOnTunnel(const TileInfo *ti)
|
||||
|
||||
SpriteID wire_base = GetWireBase(ti->tile);
|
||||
|
||||
const SortableSpriteStruct *sss = &CatenarySpriteData_Tunnel[dir];
|
||||
const SortableSpriteStruct *sss = &RailCatenarySpriteData_Tunnel[dir];
|
||||
const int *BB_data = _tunnel_wire_BB[dir];
|
||||
AddSortableSpriteToDraw(
|
||||
wire_base + sss->image_offset, PAL_NONE, ti->x + sss->x_offset, ti->y + sss->y_offset,
|
||||
@@ -270,7 +271,7 @@ void DrawCatenaryOnTunnel(const TileInfo *ti)
|
||||
* Draws wires and, if required, pylons on a given tile
|
||||
* @param ti The Tileinfo to draw the tile for
|
||||
*/
|
||||
static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
static void DrawRailCatenaryRailway(const TileInfo *ti)
|
||||
{
|
||||
/* Pylons are placed on a tile edge, so we need to take into account
|
||||
* the track configuration of 2 adjacent tiles. trackconfig[0] stores the
|
||||
@@ -379,7 +380,7 @@ static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
if (IsTileType(neighbour, MP_STATION) || IsTileType(neighbour, MP_ROAD)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
|
||||
|
||||
/* Read the foundations if they are present, and adjust the tileh */
|
||||
if (trackconfig[TS_NEIGHBOUR] != TRACK_BIT_NONE && IsTileType(neighbour, MP_RAILWAY) && HasCatenary(GetRailType(neighbour))) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
|
||||
if (trackconfig[TS_NEIGHBOUR] != TRACK_BIT_NONE && IsTileType(neighbour, MP_RAILWAY) && HasRailCatenary(GetRailType(neighbour))) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
|
||||
if (IsBridgeTile(neighbour)) {
|
||||
foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetTunnelBridgeDirection(neighbour)));
|
||||
}
|
||||
@@ -442,7 +443,7 @@ static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
}
|
||||
}
|
||||
|
||||
/* The wire above the tunnel is drawn together with the tunnel-roof (see DrawCatenaryOnTunnel()) */
|
||||
/* The wire above the tunnel is drawn together with the tunnel-roof (see DrawRailCatenaryOnTunnel()) */
|
||||
if (IsTunnelTile(ti->tile)) return;
|
||||
|
||||
/* Don't draw a wire under a low bridge */
|
||||
@@ -478,7 +479,7 @@ static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
|
||||
assert(PCPconfig != 0); // We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that)
|
||||
assert(!IsSteepSlope(tileh[TS_HOME]));
|
||||
sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
|
||||
sss = &RailCatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
|
||||
|
||||
/*
|
||||
* The "wire"-sprite position is inside the tile, i.e. 0 <= sss->?_offset < TILE_SIZE.
|
||||
@@ -498,7 +499,7 @@ static void DrawCatenaryRailway(const TileInfo *ti)
|
||||
*
|
||||
* @param ti The Tileinfo to draw the tile for
|
||||
*/
|
||||
void DrawCatenaryOnBridge(const TileInfo *ti)
|
||||
void DrawRailCatenaryOnBridge(const TileInfo *ti)
|
||||
{
|
||||
TileIndex end = GetSouthernBridgeEnd(ti->tile);
|
||||
TileIndex start = GetOtherBridgeEnd(end);
|
||||
@@ -511,15 +512,15 @@ void DrawCatenaryOnBridge(const TileInfo *ti)
|
||||
Axis axis = GetBridgeAxis(ti->tile);
|
||||
TLG tlg = GetTLG(ti->tile);
|
||||
|
||||
CatenarySprite offset = (CatenarySprite)(axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH);
|
||||
RailCatenarySprite offset = (RailCatenarySprite)(axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH);
|
||||
|
||||
if ((length % 2) && num == length) {
|
||||
/* Draw the "short" wire on the southern end of the bridge
|
||||
* only needed if the length of the bridge is odd */
|
||||
sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
|
||||
sss = &RailCatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
|
||||
} else {
|
||||
/* Draw "long" wires on all other tiles of the bridge (one pylon every two tiles) */
|
||||
sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
|
||||
sss = &RailCatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
|
||||
}
|
||||
|
||||
height = GetBridgePixelHeight(end);
|
||||
@@ -558,14 +559,14 @@ void DrawCatenaryOnBridge(const TileInfo *ti)
|
||||
/**
|
||||
* Draws overhead wires and pylons for electric railways.
|
||||
* @param ti The TileInfo struct of the tile being drawn
|
||||
* @see DrawCatenaryRailway
|
||||
* @see DrawRailCatenaryRailway
|
||||
*/
|
||||
void DrawCatenary(const TileInfo *ti)
|
||||
void DrawRailCatenary(const TileInfo *ti)
|
||||
{
|
||||
switch (GetTileType(ti->tile)) {
|
||||
case MP_RAILWAY:
|
||||
if (IsRailDepot(ti->tile)) {
|
||||
const SortableSpriteStruct *sss = &CatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
|
||||
const SortableSpriteStruct *sss = &RailCatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
|
||||
|
||||
SpriteID wire_base = GetWireBase(ti->tile);
|
||||
|
||||
@@ -587,7 +588,7 @@ void DrawCatenary(const TileInfo *ti)
|
||||
|
||||
default: return;
|
||||
}
|
||||
DrawCatenaryRailway(ti);
|
||||
DrawRailCatenaryRailway(ti);
|
||||
}
|
||||
|
||||
bool SettingsDisableElrail(int32 p1)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Test if a rail type has catenary
|
||||
* @param rt Rail type to test
|
||||
*/
|
||||
static inline bool HasCatenary(RailType rt)
|
||||
static inline bool HasRailCatenary(RailType rt)
|
||||
{
|
||||
return HasBit(GetRailTypeInfo(rt)->flags, RTF_CATENARY);
|
||||
}
|
||||
@@ -29,14 +29,14 @@ static inline bool HasCatenary(RailType rt)
|
||||
* Test if we should draw rail catenary
|
||||
* @param rt Rail type to test
|
||||
*/
|
||||
static inline bool HasCatenaryDrawn(RailType rt)
|
||||
static inline bool HasRailCatenaryDrawn(RailType rt)
|
||||
{
|
||||
return HasCatenary(rt) && !IsInvisibilitySet(TO_CATENARY) && !_settings_game.vehicle.disable_elrails;
|
||||
return HasRailCatenary(rt) && !IsInvisibilitySet(TO_CATENARY) && !_settings_game.vehicle.disable_elrails;
|
||||
}
|
||||
|
||||
void DrawCatenary(const TileInfo *ti);
|
||||
void DrawCatenaryOnTunnel(const TileInfo *ti);
|
||||
void DrawCatenaryOnBridge(const TileInfo *ti);
|
||||
void DrawRailCatenary(const TileInfo *ti);
|
||||
void DrawRailCatenaryOnTunnel(const TileInfo *ti);
|
||||
void DrawRailCatenaryOnBridge(const TileInfo *ti);
|
||||
|
||||
bool SettingsDisableElrail(int32 p1); ///< _settings_game.disable_elrail callback
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -48,11 +48,6 @@ EngineOverrideManager _engine_mngr;
|
||||
*/
|
||||
static Year _year_engine_aging_stops;
|
||||
|
||||
/**
|
||||
* The railtypes that have been or never will be introduced, or
|
||||
* an inverse bitmap of rail types that have to be introduced. */
|
||||
static uint16 _introduced_railtypes;
|
||||
|
||||
/** Number of engines of each vehicle type in original engine data */
|
||||
const uint8 _engine_counts[4] = {
|
||||
lengthof(_orig_rail_vehicle_info),
|
||||
@@ -85,6 +80,7 @@ Engine::Engine(VehicleType type, EngineID base)
|
||||
this->type = type;
|
||||
this->grf_prop.local_id = base;
|
||||
this->list_position = base;
|
||||
this->preview_company = INVALID_COMPANY;
|
||||
|
||||
/* Check if this base engine is within the original engine data range */
|
||||
if (base >= _engine_counts[type]) {
|
||||
@@ -435,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();
|
||||
}
|
||||
@@ -468,7 +464,26 @@ uint16 Engine::GetRange() const
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the EngineOverrideManager with the default engines.
|
||||
* Get the name of the aircraft type for display purposes.
|
||||
* @return Aircraft type string.
|
||||
*/
|
||||
StringID Engine::GetAircraftTypeText() const
|
||||
{
|
||||
switch (this->type) {
|
||||
case VEH_AIRCRAFT:
|
||||
switch (this->u.air.subtype) {
|
||||
case AIR_HELI: return STR_LIVERY_HELICOPTER;
|
||||
case AIR_CTOL: return STR_LIVERY_SMALL_PLANE;
|
||||
case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the #EngineOverrideManager with the default engines.
|
||||
*/
|
||||
void EngineOverrideManager::ResetToDefaultMapping()
|
||||
{
|
||||
@@ -542,29 +557,6 @@ void SetupEngines()
|
||||
const Engine *e = new Engine(eid->type, eid->internal_id);
|
||||
assert(e->index == index);
|
||||
}
|
||||
|
||||
_introduced_railtypes = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the railtypes should be introduced.
|
||||
*/
|
||||
static void CheckRailIntroduction()
|
||||
{
|
||||
/* All railtypes have been introduced. */
|
||||
if (_introduced_railtypes == UINT16_MAX || Company::GetPoolSize() == 0) return;
|
||||
|
||||
/* We need to find the railtypes that are known to all companies. */
|
||||
RailTypes rts = (RailTypes)UINT16_MAX;
|
||||
|
||||
/* We are at, or past the introduction date of the rail. */
|
||||
Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date);
|
||||
rts &= c->avail_railtypes;
|
||||
}
|
||||
|
||||
_introduced_railtypes |= rts;
|
||||
}
|
||||
|
||||
void ShowEnginePreviewWindow(EngineID engine);
|
||||
@@ -581,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)
|
||||
@@ -710,19 +702,6 @@ void StartupEngines()
|
||||
c->avail_roadtypes = GetCompanyRoadtypes(c->index);
|
||||
}
|
||||
|
||||
/* Rail types that are invalid or never introduced are marked as
|
||||
* being introduced upon start. That way we can easily check whether
|
||||
* there is any date related introduction that is still going to
|
||||
* happen somewhere in the future. */
|
||||
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
|
||||
const RailtypeInfo *rti = GetRailTypeInfo(rt);
|
||||
if (rti->label != 0 && IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue;
|
||||
|
||||
SetBit(_introduced_railtypes, rt);
|
||||
}
|
||||
|
||||
CheckRailIntroduction();
|
||||
|
||||
/* Invalidate any open purchase lists */
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE);
|
||||
}
|
||||
@@ -773,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;
|
||||
@@ -819,7 +798,10 @@ static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
|
||||
/** Daily check to offer an exclusive engine preview to the companies. */
|
||||
void EnginesDailyLoop()
|
||||
{
|
||||
CheckRailIntroduction();
|
||||
Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date);
|
||||
}
|
||||
|
||||
if (_cur_year >= _year_engine_aging_stops) return;
|
||||
|
||||
@@ -879,7 +861,7 @@ CommandCost CmdSetVehicleVisibility(TileIndex tile, DoCommandFlag flags, uint32
|
||||
{
|
||||
Engine *e = Engine::GetIfValid(GB(p2, 0, 31));
|
||||
if (e == NULL || _current_company >= MAX_COMPANIES) return CMD_ERROR;
|
||||
if ((e->flags & ENGINE_AVAILABLE) == 0 || !HasBit(e->company_avail, _current_company)) return CMD_ERROR;
|
||||
if (!IsEngineBuildable(e->index, e->type, _current_company)) return CMD_ERROR;
|
||||
|
||||
if ((flags & DC_EXEC) != 0) {
|
||||
SB(e->company_hidden, _current_company, 1, GB(p2, 31, 1));
|
||||
@@ -902,7 +884,7 @@ CommandCost CmdSetVehicleVisibility(TileIndex tile, DoCommandFlag flags, uint32
|
||||
CommandCost CmdWantEnginePreview(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||
{
|
||||
Engine *e = Engine::GetIfValid(p1);
|
||||
if (e == NULL || e->preview_company != _current_company) return CMD_ERROR;
|
||||
if (e == NULL || !(e->flags & ENGINE_EXCLUSIVE_PREVIEW) || e->preview_company != _current_company) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
|
||||
|
||||
@@ -1135,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,7 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
|
||||
uint GetDisplayMaxTractiveEffort() const;
|
||||
Date GetLifeLengthInDays() const;
|
||||
uint16 GetRange() const;
|
||||
StringID GetAircraftTypeText() const;
|
||||
|
||||
/**
|
||||
* Check whether the engine is hidden in the GUI for the given company.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -126,9 +126,9 @@ struct EnginePreviewWindow : Window {
|
||||
switch (widget) {
|
||||
case WID_EP_YES:
|
||||
DoCommandP(0, this->window_number, 0, CMD_WANT_ENGINE_PREVIEW);
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
case WID_EP_NO:
|
||||
delete this;
|
||||
if (!_shift_pressed) delete this;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -196,6 +196,7 @@ static StringID GetAircraftEngineInfoString(const Engine *e)
|
||||
uint i = 0;
|
||||
SetDParam(i++, e->GetCost());
|
||||
SetDParam(i++, e->GetDisplayMaxSpeed());
|
||||
SetDParam(i++, e->GetAircraftTypeText());
|
||||
if (range > 0) SetDParam(i++, range);
|
||||
SetDParam(i++, cargo);
|
||||
SetDParam(i++, capacity);
|
||||
@@ -204,10 +205,10 @@ static StringID GetAircraftEngineInfoString(const Engine *e)
|
||||
SetDParam(i++, CT_MAIL);
|
||||
SetDParam(i++, mail_capacity);
|
||||
SetDParam(i++, e->GetRunningCost());
|
||||
return range > 0 ? STR_ENGINE_PREVIEW_COST_MAX_SPEED_RANGE_CAPACITY_CAPACITY_RUNCOST : STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAPACITY_CAPACITY_RUNCOST;
|
||||
return range > 0 ? STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST : STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST;
|
||||
} else {
|
||||
SetDParam(i++, e->GetRunningCost());
|
||||
return range > 0 ? STR_ENGINE_PREVIEW_COST_MAX_SPEED_RANGE_CAPACITY_RUNCOST : STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAPACITY_RUNCOST;
|
||||
return range > 0 ? STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_RUNCOST : STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_RUNCOST;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +225,7 @@ static StringID GetRoadVehEngineInfoString(const Engine *e)
|
||||
SetDParam(2, CT_INVALID);
|
||||
}
|
||||
SetDParam(4, e->GetRunningCost());
|
||||
return STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAPACITY_RUNCOST;
|
||||
return STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST;
|
||||
} else {
|
||||
SetDParam(0, e->GetCost());
|
||||
SetDParam(2, e->GetDisplayMaxSpeed());
|
||||
@@ -252,7 +253,7 @@ static StringID GetShipEngineInfoString(const Engine *e)
|
||||
SetDParam(2, e->GetDefaultCargoType());
|
||||
SetDParam(3, e->GetDisplayDefaultCapacity());
|
||||
SetDParam(4, e->GetRunningCost());
|
||||
return STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAPACITY_RUNCOST;
|
||||
return STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -158,6 +158,7 @@ enum EngineMiscFlags {
|
||||
EF_AUTO_REFIT = 4, ///< Automatic refitting is allowed
|
||||
EF_NO_DEFAULT_CARGO_MULTIPLIER = 5, ///< Use the new capacity algorithm. The default cargotype of the vehicle does not affect capacity multipliers. CB 15 is also called in purchase list.
|
||||
EF_NO_BREAKDOWN_SMOKE = 6, ///< Do not show black smoke during a breakdown.
|
||||
EF_SPRITE_STACK = 7, ///< Draw vehicle by stacking multiple sprites.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
@@ -522,7 +513,7 @@ FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir,
|
||||
case BASESET_DIR:
|
||||
f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
|
||||
if (f != NULL) break;
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
case NEWGRF_DIR:
|
||||
f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
|
||||
break;
|
||||
@@ -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));
|
||||
@@ -584,32 +575,6 @@ bool AppendPathSeparator(char *buf, const char *last)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and files a variable with the full path
|
||||
* based on the given directory.
|
||||
* @param dir the directory to base the path on
|
||||
* @return the malloced full path
|
||||
*/
|
||||
char *BuildWithFullPath(const char *dir)
|
||||
{
|
||||
char *dest = MallocT<char>(MAX_PATH);
|
||||
char *last = dest + MAX_PATH - 1;
|
||||
strecpy(dest, dir, last);
|
||||
|
||||
/* Check if absolute or relative path */
|
||||
const char *s = strchr(dest, PATHSEPCHAR);
|
||||
|
||||
/* Add absolute path */
|
||||
if (s == NULL || dest != s) {
|
||||
if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
|
||||
AppendPathSeparator(dest, last);
|
||||
strecat(dest, dir, last);
|
||||
}
|
||||
AppendPathSeparator(dest, last);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first directory in a tar archive.
|
||||
* @param tarname the name of the tar archive to look in.
|
||||
@@ -666,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)
|
||||
@@ -1014,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.
|
||||
@@ -1032,30 +997,29 @@ extern void DetermineBasePaths(const char *exe);
|
||||
*/
|
||||
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
|
||||
{
|
||||
char tmp[MAX_PATH];
|
||||
strecpy(tmp, exe, lastof(tmp));
|
||||
|
||||
bool success = false;
|
||||
#ifdef WITH_COCOA
|
||||
char *app_bundle = strchr(exe, '.');
|
||||
char *app_bundle = strchr(tmp, '.');
|
||||
while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
|
||||
|
||||
if (app_bundle != NULL) app_bundle[0] = '\0';
|
||||
if (app_bundle != NULL) *app_bundle = '\0';
|
||||
#endif /* WITH_COCOA */
|
||||
char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
|
||||
char *s = strrchr(tmp, PATHSEPCHAR);
|
||||
if (s != NULL) {
|
||||
*s = '\0';
|
||||
#if defined(__DJGPP__)
|
||||
/* If we want to go to the root, we can't use cd C:, but we must use '/' */
|
||||
if (s[-1] == ':') chdir("/");
|
||||
if (s > tmp && *(s - 1) == ':') chdir("/");
|
||||
#endif
|
||||
if (chdir(exe) != 0) {
|
||||
if (chdir(tmp) != 0) {
|
||||
DEBUG(misc, 0, "Directory with the binary does not exist?");
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
*s = PATHSEPCHAR;
|
||||
}
|
||||
#ifdef WITH_COCOA
|
||||
if (app_bundle != NULL) app_bundle[0] = '.';
|
||||
#endif /* WITH_COCOA */
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -1185,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;
|
||||
|
||||
@@ -1338,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.
|
||||
@@ -1478,7 +1442,7 @@ uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool r
|
||||
switch (sd) {
|
||||
case BASESET_DIR:
|
||||
num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
case NEWGRF_DIR:
|
||||
num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
|
||||
break;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -14,6 +14,96 @@
|
||||
|
||||
#include "core/enum_type.hpp"
|
||||
|
||||
/** The different abstract types of files that the system knows about. */
|
||||
enum AbstractFileType {
|
||||
FT_NONE, ///< nothing to do
|
||||
FT_SAVEGAME, ///< old or new savegame
|
||||
FT_SCENARIO, ///< old or new scenario
|
||||
FT_HEIGHTMAP, ///< heightmap file
|
||||
|
||||
FT_INVALID = 7, ///< Invalid or unknown file type.
|
||||
FT_NUMBITS = 3, ///< Number of bits required for storing a #AbstractFileType value.
|
||||
FT_MASK = (1 << FT_NUMBITS) - 1, ///< Bitmask for extracting an abstract file type.
|
||||
};
|
||||
|
||||
/** Kinds of files in each #AbstractFileType. */
|
||||
enum DetailedFileType {
|
||||
/* Save game and scenario files. */
|
||||
DFT_OLD_GAME_FILE, ///< Old save game or scenario file.
|
||||
DFT_GAME_FILE, ///< Save game or scenario file.
|
||||
|
||||
/* Heightmap files. */
|
||||
DFT_HEIGHTMAP_BMP, ///< BMP file.
|
||||
DFT_HEIGHTMAP_PNG, ///< PNG file.
|
||||
|
||||
/* fios 'files' */
|
||||
DFT_FIOS_DRIVE, ///< A drive (letter) entry.
|
||||
DFT_FIOS_PARENT, ///< A parent directory entry.
|
||||
DFT_FIOS_DIR, ///< A directory entry.
|
||||
DFT_FIOS_DIRECT, ///< Direct filename.
|
||||
|
||||
DFT_INVALID = 255, ///< Unknown or invalid file.
|
||||
};
|
||||
|
||||
/** Operation performed on the file. */
|
||||
enum SaveLoadOperation {
|
||||
SLO_CHECK, ///< Load file for checking and/or preview.
|
||||
SLO_LOAD, ///< File is being loaded.
|
||||
SLO_SAVE, ///< File is being saved.
|
||||
|
||||
SLO_INVALID, ///< Unknown file operation.
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct an enum value for #FiosType as a combination of an abstract and a detailed file type.
|
||||
* @param abstract Abstract file type (one of #AbstractFileType).
|
||||
* @param detailed Detailed file type (one of #DetailedFileType).
|
||||
*/
|
||||
#define MAKE_FIOS_TYPE(abstract, detailed) ((abstract) | ((detailed) << FT_NUMBITS))
|
||||
|
||||
/**
|
||||
* Elements of a file system that are recognized.
|
||||
* Values are a combination of #AbstractFileType and #DetailedFileType.
|
||||
* @see GetAbstractFileType GetDetailedFileType
|
||||
*/
|
||||
enum FiosType {
|
||||
FIOS_TYPE_DRIVE = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DRIVE),
|
||||
FIOS_TYPE_PARENT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_PARENT),
|
||||
FIOS_TYPE_DIR = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIR),
|
||||
FIOS_TYPE_DIRECT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIRECT),
|
||||
|
||||
FIOS_TYPE_FILE = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_GAME_FILE),
|
||||
FIOS_TYPE_OLDFILE = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_OLD_GAME_FILE),
|
||||
FIOS_TYPE_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_GAME_FILE),
|
||||
FIOS_TYPE_OLD_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_OLD_GAME_FILE),
|
||||
FIOS_TYPE_PNG = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG),
|
||||
FIOS_TYPE_BMP = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_BMP),
|
||||
|
||||
FIOS_TYPE_INVALID = MAKE_FIOS_TYPE(FT_INVALID, DFT_INVALID),
|
||||
};
|
||||
|
||||
#undef MAKE_FIOS_TYPE
|
||||
|
||||
/**
|
||||
* Extract the abstract file type from a #FiosType.
|
||||
* @param fios_type Type to query.
|
||||
* @return The Abstract file type of the \a fios_type.
|
||||
*/
|
||||
inline AbstractFileType GetAbstractFileType(FiosType fios_type)
|
||||
{
|
||||
return static_cast<AbstractFileType>(fios_type & FT_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the detailed file type from a #FiosType.
|
||||
* @param fios_type Type to query.
|
||||
* @return The Detailed file type of the \a fios_type.
|
||||
*/
|
||||
inline DetailedFileType GetDetailedFileType(FiosType fios_type)
|
||||
{
|
||||
return static_cast<DetailedFileType>(fios_type >> FT_NUMBITS);
|
||||
}
|
||||
|
||||
/**
|
||||
* The different kinds of subdirectories OpenTTD uses
|
||||
*/
|
||||
|
||||
171
src/fios.cpp
171
src/fios.cpp
@@ -20,26 +20,24 @@
|
||||
#include "string_func.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif /* WIN32 */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/* Variables to display file lists */
|
||||
SmallVector<FiosItem, 32> _fios_items;
|
||||
static char *_fios_path;
|
||||
static const char *_fios_path_last;
|
||||
SmallFiosItem _file_to_saveload;
|
||||
SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
|
||||
|
||||
/* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
|
||||
extern bool FiosIsRoot(const char *path);
|
||||
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
|
||||
extern bool FiosIsHiddenFile(const struct dirent *ent);
|
||||
extern void FiosGetDrives();
|
||||
extern void FiosGetDrives(FileList &file_list);
|
||||
extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
|
||||
|
||||
/* get the name of an oldstyle savegame */
|
||||
@@ -65,11 +63,72 @@ int CDECL CompareFiosItems(const FiosItem *da, const FiosItem *db)
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Free the list of savegames. */
|
||||
void FiosFreeSavegameList()
|
||||
FileList::~FileList()
|
||||
{
|
||||
_fios_items.Clear();
|
||||
_fios_items.Compact();
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a file list with the given kind of files, for the stated purpose.
|
||||
* @param abstract_filetype Kind of files to collect.
|
||||
* @param fop Purpose of the collection, either #SLO_LOAD or #SLO_SAVE.
|
||||
*/
|
||||
void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop)
|
||||
{
|
||||
this->Clear();
|
||||
|
||||
assert(fop == SLO_LOAD || fop == SLO_SAVE);
|
||||
switch (abstract_filetype) {
|
||||
case FT_NONE:
|
||||
break;
|
||||
|
||||
case FT_SAVEGAME:
|
||||
FiosGetSavegameList(fop, *this);
|
||||
break;
|
||||
|
||||
case FT_SCENARIO:
|
||||
FiosGetScenarioList(fop, *this);
|
||||
break;
|
||||
|
||||
case FT_HEIGHTMAP:
|
||||
FiosGetHeightmapList(fop, *this);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find file information of a file by its name from the file list.
|
||||
* @param file The filename to return information about. Can be the actual name
|
||||
* or a numbered entry into the filename list.
|
||||
* @return The information on the file, or \c NULL if the file is not available.
|
||||
*/
|
||||
const FiosItem *FileList::FindItem(const char *file)
|
||||
{
|
||||
for (const FiosItem *item = this->Begin(); item != this->End(); item++) {
|
||||
if (strcmp(file, item->name) == 0) return item;
|
||||
if (strcmp(file, item->title) == 0) return item;
|
||||
}
|
||||
|
||||
/* If no name matches, try to parse it as number */
|
||||
char *endptr;
|
||||
int i = strtol(file, &endptr, 10);
|
||||
if (file == endptr || *endptr != '\0') i = -1;
|
||||
|
||||
if (IsInsideMM(i, 0, this->Length())) return this->Get(i);
|
||||
|
||||
/* As a last effort assume it is an OpenTTD savegame and
|
||||
* that the ".sav" part was not given. */
|
||||
char long_file[MAX_PATH];
|
||||
seprintf(long_file, lastof(long_file), "%s.sav", file);
|
||||
for (const FiosItem *item = this->Begin(); item != this->End(); item++) {
|
||||
if (strcmp(long_file, item->name) == 0) return item;
|
||||
if (strcmp(long_file, item->title) == 0) return item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,12 +153,11 @@ 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
|
||||
/* FALL THROUGH */
|
||||
break;
|
||||
|
||||
case FIOS_TYPE_INVALID:
|
||||
break;
|
||||
|
||||
@@ -215,23 +273,24 @@ bool FiosDelete(const char *name)
|
||||
return unlink(filename) == 0;
|
||||
}
|
||||
|
||||
typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
|
||||
typedef FiosType fios_getlist_callback_proc(SaveLoadOperation fop, const char *filename, const char *ext, char *title, const char *last);
|
||||
|
||||
/**
|
||||
* Scanner to scan for a particular type of FIOS file.
|
||||
*/
|
||||
class FiosFileScanner : public FileScanner {
|
||||
SaveLoadDialogMode mode; ///< The mode we want to search for
|
||||
SaveLoadOperation fop; ///< The kind of file we are looking for.
|
||||
fios_getlist_callback_proc *callback_proc; ///< Callback to check whether the file may be added
|
||||
FileList &file_list; ///< Destination of the found files.
|
||||
public:
|
||||
/**
|
||||
* Create the scanner
|
||||
* @param mode The mode we are in. Some modes don't allow 'parent'.
|
||||
* @param callback_proc The function that is called where you need to do the filtering.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param callback_proc The function that is called where you need to do the filtering.
|
||||
* @param file_list Destination of the found files.
|
||||
*/
|
||||
FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
|
||||
mode(mode),
|
||||
callback_proc(callback_proc)
|
||||
FiosFileScanner(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, FileList &file_list) :
|
||||
fop(fop), callback_proc(callback_proc), file_list(file_list)
|
||||
{}
|
||||
|
||||
/* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename);
|
||||
@@ -251,15 +310,15 @@ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, cons
|
||||
char fios_title[64];
|
||||
fios_title[0] = '\0'; // reset the title;
|
||||
|
||||
FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
|
||||
FiosType type = this->callback_proc(this->fop, filename, ext, fios_title, lastof(fios_title));
|
||||
if (type == FIOS_TYPE_INVALID) return false;
|
||||
|
||||
for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
|
||||
for (const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) {
|
||||
if (strcmp(fios->name, filename) == 0) return false;
|
||||
}
|
||||
|
||||
FiosItem *fios = _fios_items.Append();
|
||||
#ifdef WIN32
|
||||
FiosItem *fios = file_list.Append();
|
||||
#ifdef _WIN32
|
||||
struct _stat sb;
|
||||
if (_tstat(OTTD2FS(filename), &sb) == 0) {
|
||||
#else
|
||||
@@ -289,11 +348,12 @@ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, cons
|
||||
|
||||
/**
|
||||
* Fill the list of the files in a directory, according to some arbitrary rule.
|
||||
* @param mode The mode we are in. Some modes don't allow 'parent'.
|
||||
* @param callback_proc The function that is called where you need to do the filtering.
|
||||
* @param subdir The directory from where to start (global) searching.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param callback_proc The function that is called where you need to do the filtering.
|
||||
* @param subdir The directory from where to start (global) searching.
|
||||
* @param file_list Destination of the found files.
|
||||
*/
|
||||
static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
|
||||
static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, Subdirectory subdir, FileList &file_list)
|
||||
{
|
||||
struct stat sb;
|
||||
struct dirent *dirent;
|
||||
@@ -302,11 +362,11 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc
|
||||
int sort_start;
|
||||
char d_name[sizeof(fios->name)];
|
||||
|
||||
_fios_items.Clear();
|
||||
file_list.Clear();
|
||||
|
||||
/* A parent directory link exists if we are not in the root directory */
|
||||
if (!FiosIsRoot(_fios_path)) {
|
||||
fios = _fios_items.Append();
|
||||
fios = file_list.Append();
|
||||
fios->type = FIOS_TYPE_PARENT;
|
||||
fios->mtime = 0;
|
||||
strecpy(fios->name, "..", lastof(fios->name));
|
||||
@@ -322,7 +382,7 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc
|
||||
if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
|
||||
(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
|
||||
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
|
||||
fios = _fios_items.Append();
|
||||
fios = file_list.Append();
|
||||
fios->type = FIOS_TYPE_DIR;
|
||||
fios->mtime = 0;
|
||||
strecpy(fios->name, d_name, lastof(fios->name));
|
||||
@@ -337,27 +397,27 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc
|
||||
{
|
||||
SortingBits order = _savegame_sort_order;
|
||||
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
|
||||
QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems);
|
||||
QSortT(file_list.files.Begin(), file_list.files.Length(), CompareFiosItems);
|
||||
_savegame_sort_order = order;
|
||||
}
|
||||
|
||||
/* This is where to start sorting for the filenames */
|
||||
sort_start = _fios_items.Length();
|
||||
sort_start = file_list.Length();
|
||||
|
||||
/* Show files */
|
||||
FiosFileScanner scanner(mode, callback_proc);
|
||||
FiosFileScanner scanner(fop, callback_proc, file_list);
|
||||
if (subdir == NO_DIRECTORY) {
|
||||
scanner.Scan(NULL, _fios_path, false);
|
||||
} else {
|
||||
scanner.Scan(NULL, subdir, true, true);
|
||||
}
|
||||
|
||||
QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems);
|
||||
QSortT(file_list.Get(sort_start), file_list.Length() - sort_start, CompareFiosItems);
|
||||
|
||||
/* Show drives */
|
||||
FiosGetDrives();
|
||||
FiosGetDrives(file_list);
|
||||
|
||||
_fios_items.Compact();
|
||||
file_list.Compact();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,7 +446,7 @@ static void GetFileTitle(const char *file, char *title, const char *last, Subdir
|
||||
|
||||
/**
|
||||
* Callback for FiosGetFileList. It tells if a file is a savegame or not.
|
||||
* @param mode Save/load mode.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param file Name of the file to check.
|
||||
* @param ext A pointer to the extension identifier inside file
|
||||
* @param title Buffer if a callback wants to lookup the title of the file; NULL to skip the lookup
|
||||
@@ -395,7 +455,7 @@ static void GetFileTitle(const char *file, char *title, const char *last, Subdir
|
||||
* @see FiosGetFileList
|
||||
* @see FiosGetSavegameList
|
||||
*/
|
||||
FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
|
||||
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last)
|
||||
{
|
||||
/* Show savegame files
|
||||
* .SAV OpenTTD saved game
|
||||
@@ -411,7 +471,7 @@ FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file,
|
||||
return FIOS_TYPE_FILE;
|
||||
}
|
||||
|
||||
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
|
||||
if (fop == SLO_LOAD) {
|
||||
if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
|
||||
strcasecmp(ext, ".sv2") == 0) {
|
||||
if (title != NULL) GetOldSaveGameName(file, title, last);
|
||||
@@ -424,10 +484,11 @@ FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file,
|
||||
|
||||
/**
|
||||
* Get a list of savegames.
|
||||
* @param mode Save/load mode.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param file_list Destination of the found files.
|
||||
* @see FiosGetFileList
|
||||
*/
|
||||
void FiosGetSavegameList(SaveLoadDialogMode mode)
|
||||
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
|
||||
{
|
||||
static char *fios_save_path = NULL;
|
||||
static char *fios_save_path_last = NULL;
|
||||
@@ -441,12 +502,12 @@ void FiosGetSavegameList(SaveLoadDialogMode mode)
|
||||
_fios_path = fios_save_path;
|
||||
_fios_path_last = fios_save_path_last;
|
||||
|
||||
FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
|
||||
FiosGetFileList(fop, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for FiosGetFileList. It tells if a file is a scenario or not.
|
||||
* @param mode Save/load mode.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param file Name of the file to check.
|
||||
* @param ext A pointer to the extension identifier inside file
|
||||
* @param title Buffer if a callback wants to lookup the title of the file
|
||||
@@ -455,7 +516,7 @@ void FiosGetSavegameList(SaveLoadDialogMode mode)
|
||||
* @see FiosGetFileList
|
||||
* @see FiosGetScenarioList
|
||||
*/
|
||||
static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
|
||||
static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last)
|
||||
{
|
||||
/* Show scenario files
|
||||
* .SCN OpenTTD style scenario file
|
||||
@@ -466,7 +527,7 @@ static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char
|
||||
return FIOS_TYPE_SCENARIO;
|
||||
}
|
||||
|
||||
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
|
||||
if (fop == SLO_LOAD) {
|
||||
if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
|
||||
GetOldSaveGameName(file, title, last);
|
||||
return FIOS_TYPE_OLD_SCENARIO;
|
||||
@@ -478,10 +539,11 @@ static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char
|
||||
|
||||
/**
|
||||
* Get a list of scenarios.
|
||||
* @param mode Save/load mode.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param file_list Destination of the found files.
|
||||
* @see FiosGetFileList
|
||||
*/
|
||||
void FiosGetScenarioList(SaveLoadDialogMode mode)
|
||||
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list)
|
||||
{
|
||||
static char *fios_scn_path = NULL;
|
||||
static char *fios_scn_path_last = NULL;
|
||||
@@ -499,10 +561,11 @@ void FiosGetScenarioList(SaveLoadDialogMode mode)
|
||||
char base_path[MAX_PATH];
|
||||
FioGetDirectory(base_path, lastof(base_path), SCENARIO_DIR);
|
||||
|
||||
FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
|
||||
Subdirectory subdir = (fop == SLO_LOAD && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY;
|
||||
FiosGetFileList(fop, &FiosGetScenarioListCallback, subdir, file_list);
|
||||
}
|
||||
|
||||
static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
|
||||
static FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last)
|
||||
{
|
||||
/* Show heightmap files
|
||||
* .PNG PNG Based heightmap files
|
||||
@@ -548,9 +611,10 @@ static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char
|
||||
|
||||
/**
|
||||
* Get a list of heightmaps.
|
||||
* @param mode Save/load mode.
|
||||
* @param fop Purpose of collecting the list.
|
||||
* @param file_list Destination of the found files.
|
||||
*/
|
||||
void FiosGetHeightmapList(SaveLoadDialogMode mode)
|
||||
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list)
|
||||
{
|
||||
static char *fios_hmap_path = NULL;
|
||||
static char *fios_hmap_path_last = NULL;
|
||||
@@ -567,7 +631,8 @@ void FiosGetHeightmapList(SaveLoadDialogMode mode)
|
||||
char base_path[MAX_PATH];
|
||||
FioGetDirectory(base_path, lastof(base_path), HEIGHTMAP_DIR);
|
||||
|
||||
FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
|
||||
Subdirectory subdir = strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY;
|
||||
FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
164
src/fios.h
164
src/fios.h
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -81,52 +88,17 @@ extern LoadCheckData _load_check_data;
|
||||
|
||||
enum FileSlots {
|
||||
/**
|
||||
* Slot used for the GRF scanning and such. This slot cannot be reused
|
||||
* as it will otherwise cause issues when pressing "rescan directories".
|
||||
* It can furthermore not be larger than LAST_GRF_SLOT as that complicates
|
||||
* the testing for "too much NewGRFs".
|
||||
* Slot used for the GRF scanning and such.
|
||||
* This slot is used for all temporary accesses to files when scanning/testing files,
|
||||
* and thus cannot be used for files, which are continuously accessed during a game.
|
||||
*/
|
||||
CONFIG_SLOT = 0,
|
||||
/** Slot for the sound. */
|
||||
SOUND_SLOT = 1,
|
||||
/** First slot usable for (New)GRFs used during the game. */
|
||||
FIRST_GRF_SLOT = 2,
|
||||
/** Last slot usable for (New)GRFs used during the game. */
|
||||
LAST_GRF_SLOT = 63,
|
||||
/** Maximum number of slots. */
|
||||
MAX_FILE_SLOTS = 64
|
||||
};
|
||||
|
||||
/** Mode of the file dialogue window. */
|
||||
enum SaveLoadDialogMode {
|
||||
SLD_LOAD_GAME, ///< Load a game.
|
||||
SLD_LOAD_SCENARIO, ///< Load a scenario.
|
||||
SLD_SAVE_GAME, ///< Save a game.
|
||||
SLD_SAVE_SCENARIO, ///< Save a scenario.
|
||||
SLD_LOAD_HEIGHTMAP, ///< Load a heightmap.
|
||||
SLD_SAVE_HEIGHTMAP, ///< Save a heightmap.
|
||||
};
|
||||
|
||||
/** The different types of files that the system knows about. */
|
||||
enum FileType {
|
||||
FT_NONE, ///< nothing to do
|
||||
FT_SAVEGAME, ///< old or new savegame
|
||||
FT_SCENARIO, ///< old or new scenario
|
||||
FT_HEIGHTMAP, ///< heightmap file
|
||||
};
|
||||
|
||||
enum FiosType {
|
||||
FIOS_TYPE_DRIVE,
|
||||
FIOS_TYPE_PARENT,
|
||||
FIOS_TYPE_DIR,
|
||||
FIOS_TYPE_FILE,
|
||||
FIOS_TYPE_OLDFILE,
|
||||
FIOS_TYPE_SCENARIO,
|
||||
FIOS_TYPE_OLD_SCENARIO,
|
||||
FIOS_TYPE_DIRECT,
|
||||
FIOS_TYPE_PNG,
|
||||
FIOS_TYPE_BMP,
|
||||
FIOS_TYPE_INVALID = 255,
|
||||
MAX_FILE_SLOTS = 128,
|
||||
};
|
||||
|
||||
/** Deals with finding savegames */
|
||||
@@ -137,12 +109,95 @@ struct FiosItem {
|
||||
char name[MAX_PATH];
|
||||
};
|
||||
|
||||
/** Deals with the type of the savegame, independent of extension */
|
||||
struct SmallFiosItem {
|
||||
int mode; ///< savegame/scenario type (old, new)
|
||||
FileType filetype; ///< what type of file are we dealing with
|
||||
char name[MAX_PATH]; ///< name
|
||||
char title[255]; ///< internal name of the game
|
||||
/** List of file information. */
|
||||
class FileList {
|
||||
public:
|
||||
~FileList();
|
||||
|
||||
/**
|
||||
* Construct a new entry in the file list.
|
||||
* @return Pointer to the new items to be initialized.
|
||||
*/
|
||||
inline FiosItem *Append()
|
||||
{
|
||||
return this->files.Append();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of files in the list.
|
||||
* @return The number of files stored in the list.
|
||||
*/
|
||||
inline uint Length() const
|
||||
{
|
||||
return this->files.Length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the first file information.
|
||||
* @return Address of the first file information.
|
||||
*/
|
||||
inline const FiosItem *Begin() const
|
||||
{
|
||||
return this->files.Begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer behind the last file information.
|
||||
* @return Address behind the last file information.
|
||||
*/
|
||||
inline const FiosItem *End() const
|
||||
{
|
||||
return this->files.End();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the indicated file information. File information must exist.
|
||||
* @return Address of the indicated existing file information.
|
||||
*/
|
||||
inline const FiosItem *Get(uint index) const
|
||||
{
|
||||
return this->files.Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the indicated file information. File information must exist.
|
||||
* @return Address of the indicated existing file information.
|
||||
*/
|
||||
inline FiosItem *Get(uint index)
|
||||
{
|
||||
return this->files.Get(index);
|
||||
}
|
||||
|
||||
inline const FiosItem &operator[](uint index) const
|
||||
{
|
||||
return this->files[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the indicated file information. File information must exist.
|
||||
* @return The requested file information.
|
||||
*/
|
||||
inline FiosItem &operator[](uint index)
|
||||
{
|
||||
return this->files[index];
|
||||
}
|
||||
|
||||
/** Remove all items from the list. */
|
||||
inline void Clear()
|
||||
{
|
||||
this->files.Clear();
|
||||
}
|
||||
|
||||
/** Compact the list down to the smallest block size boundary. */
|
||||
inline void Compact()
|
||||
{
|
||||
this->files.Compact();
|
||||
}
|
||||
|
||||
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop);
|
||||
const FiosItem *FindItem(const char *file);
|
||||
|
||||
SmallVector<FiosItem, 32> files; ///< The list of files.
|
||||
};
|
||||
|
||||
enum SortingBits {
|
||||
@@ -154,18 +209,14 @@ enum SortingBits {
|
||||
DECLARE_ENUM_AS_BIT_SET(SortingBits)
|
||||
|
||||
/* Variables to display file lists */
|
||||
extern SmallVector<FiosItem, 32> _fios_items;
|
||||
extern SmallFiosItem _file_to_saveload;
|
||||
extern SaveLoadDialogMode _saveload_mode;
|
||||
extern SortingBits _savegame_sort_order;
|
||||
|
||||
void ShowSaveLoadDialog(SaveLoadDialogMode mode);
|
||||
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop);
|
||||
|
||||
void FiosGetSavegameList(SaveLoadDialogMode mode);
|
||||
void FiosGetScenarioList(SaveLoadDialogMode mode);
|
||||
void FiosGetHeightmapList(SaveLoadDialogMode mode);
|
||||
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list);
|
||||
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list);
|
||||
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list);
|
||||
|
||||
void FiosFreeSavegameList();
|
||||
const char *FiosBrowseTo(const FiosItem *item);
|
||||
|
||||
StringID FiosGetDescText(const char **path, uint64 *total_free);
|
||||
@@ -173,13 +224,8 @@ bool FiosDelete(const char *name);
|
||||
void FiosMakeHeightmapName(char *buf, const char *name, const char *last);
|
||||
void FiosMakeSavegameName(char *buf, const char *name, const char *last);
|
||||
|
||||
FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last);
|
||||
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last);
|
||||
|
||||
int CDECL CompareFiosItems(const FiosItem *a, const FiosItem *b);
|
||||
|
||||
extern const TextColour _fios_colours[];
|
||||
|
||||
void BuildFileList();
|
||||
void SetFiosType(const byte fiostype);
|
||||
|
||||
#endif /* FIOS_H */
|
||||
|
||||
442
src/fios_gui.cpp
442
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"
|
||||
|
||||
@@ -36,7 +37,6 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
SaveLoadDialogMode _saveload_mode;
|
||||
LoadCheckData _load_check_data; ///< Data loaded from save during SL_LOAD_CHECK.
|
||||
|
||||
static bool _fios_path_changed;
|
||||
@@ -77,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),
|
||||
@@ -87,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),
|
||||
@@ -99,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),
|
||||
@@ -120,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),
|
||||
@@ -129,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),
|
||||
@@ -153,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),
|
||||
@@ -163,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),
|
||||
@@ -172,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),
|
||||
@@ -187,33 +226,24 @@ static const NWidgetPart _nested_save_dialog_widgets[] = {
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
/** Colours for fios types, indexed by #FiosType. */
|
||||
const TextColour _fios_colours[] = {
|
||||
TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN,
|
||||
TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW
|
||||
/** Text colours of #DetailedFileType fios entries in the window. */
|
||||
static const TextColour _fios_colours[] = {
|
||||
TC_LIGHT_BROWN, // DFT_OLD_GAME_FILE
|
||||
TC_ORANGE, // DFT_GAME_FILE
|
||||
TC_YELLOW, // DFT_HEIGHTMAP_BMP
|
||||
TC_ORANGE, // DFT_HEIGHTMAP_PNG
|
||||
TC_LIGHT_BLUE, // DFT_FIOS_DRIVE
|
||||
TC_DARK_GREEN, // DFT_FIOS_PARENT
|
||||
TC_DARK_GREEN, // DFT_FIOS_DIR
|
||||
TC_ORANGE, // DFT_FIOS_DIRECT
|
||||
};
|
||||
|
||||
void BuildFileList()
|
||||
{
|
||||
_fios_path_changed = true;
|
||||
FiosFreeSavegameList();
|
||||
|
||||
switch (_saveload_mode) {
|
||||
case SLD_LOAD_SCENARIO:
|
||||
case SLD_SAVE_SCENARIO:
|
||||
FiosGetScenarioList(_saveload_mode); break;
|
||||
case SLD_SAVE_HEIGHTMAP:
|
||||
case SLD_LOAD_HEIGHTMAP:
|
||||
FiosGetHeightmapList(_saveload_mode); break;
|
||||
|
||||
default: FiosGetSavegameList(_saveload_mode); break;
|
||||
}
|
||||
|
||||
/* Invalidate saveload window */
|
||||
InvalidateWindowData(WC_SAVELOAD, 0, 2, true);
|
||||
}
|
||||
|
||||
static void MakeSortedSaveGameList()
|
||||
/**
|
||||
* Sort the collected list save games prior to displaying it in the save/load gui.
|
||||
* @param[in,out] file_list List of save game files found in the directory.
|
||||
*/
|
||||
static void SortSaveGameList(FileList &file_list)
|
||||
{
|
||||
uint sort_start = 0;
|
||||
uint sort_end = 0;
|
||||
@@ -222,7 +252,7 @@ static void MakeSortedSaveGameList()
|
||||
* Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE)
|
||||
* Only sort savegames/scenarios, not directories
|
||||
*/
|
||||
for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) {
|
||||
for (const FiosItem *item = file_list.Begin(); item != file_list.End(); item++) {
|
||||
switch (item->type) {
|
||||
case FIOS_TYPE_DIR: sort_start++; break;
|
||||
case FIOS_TYPE_PARENT: sort_start++; break;
|
||||
@@ -231,16 +261,26 @@ static void MakeSortedSaveGameList()
|
||||
}
|
||||
}
|
||||
|
||||
uint s_amount = _fios_items.Length() - sort_start - sort_end;
|
||||
QSortT(_fios_items.Get(sort_start), s_amount, CompareFiosItems);
|
||||
uint s_amount = file_list.Length() - sort_start - sort_end;
|
||||
QSortT(file_list.Get(sort_start), s_amount, CompareFiosItems);
|
||||
}
|
||||
|
||||
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.
|
||||
FileList fios_items; ///< Save game list.
|
||||
FiosItem o_dir;
|
||||
const FiosItem *selected;
|
||||
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. */
|
||||
@@ -250,38 +290,61 @@ public:
|
||||
this->filename_editbox.text.UpdateSize();
|
||||
}
|
||||
|
||||
SaveLoadWindow(WindowDesc *desc, SaveLoadDialogMode mode) : Window(desc), filename_editbox(64)
|
||||
SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop)
|
||||
: Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop), filter_editbox(EDITBOX_MAX_SIZE)
|
||||
{
|
||||
static const StringID saveload_captions[] = {
|
||||
STR_SAVELOAD_LOAD_CAPTION,
|
||||
STR_SAVELOAD_LOAD_SCENARIO,
|
||||
STR_SAVELOAD_SAVE_CAPTION,
|
||||
STR_SAVELOAD_SAVE_SCENARIO,
|
||||
STR_SAVELOAD_LOAD_HEIGHTMAP,
|
||||
STR_SAVELOAD_SAVE_HEIGHTMAP,
|
||||
};
|
||||
assert((uint)mode < lengthof(saveload_captions));
|
||||
assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD);
|
||||
|
||||
/* Use an array to define what will be the current file type being handled
|
||||
* by current file mode */
|
||||
switch (mode) {
|
||||
case SLD_SAVE_GAME: this->GenerateFileName(); break;
|
||||
case SLD_SAVE_HEIGHTMAP:
|
||||
case SLD_SAVE_SCENARIO: this->filename_editbox.text.Assign("UNNAMED"); break;
|
||||
default: break;
|
||||
/* For saving, construct an initial file name. */
|
||||
if (this->fop == SLO_SAVE) {
|
||||
switch (this->abstract_filetype) {
|
||||
case FT_SAVEGAME:
|
||||
this->GenerateFileName();
|
||||
break;
|
||||
|
||||
case FT_SCENARIO:
|
||||
case FT_HEIGHTMAP:
|
||||
this->filename_editbox.text.Assign("UNNAMED");
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
this->querystrings[WID_SL_SAVE_OSK_TITLE] = &this->filename_editbox;
|
||||
this->filename_editbox.ok_button = WID_SL_SAVE_GAME;
|
||||
|
||||
this->CreateNestedTree(true);
|
||||
if (mode == SLD_LOAD_GAME) this->GetWidget<NWidgetStacked>(WID_SL_CONTENT_DOWNLOAD_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetCore>(WID_SL_CAPTION)->widget_data = saveload_captions[mode];
|
||||
this->vscroll = this->GetScrollbar(WID_SL_SCROLLBAR);
|
||||
if (this->fop == SLO_LOAD && this->abstract_filetype == FT_SAVEGAME) {
|
||||
this->GetWidget<NWidgetStacked>(WID_SL_CONTENT_DOWNLOAD_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL);
|
||||
}
|
||||
|
||||
/* Select caption string of the window. */
|
||||
StringID caption_string;
|
||||
switch (this->abstract_filetype) {
|
||||
case FT_SAVEGAME:
|
||||
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_CAPTION : STR_SAVELOAD_LOAD_CAPTION;
|
||||
break;
|
||||
|
||||
case FT_SCENARIO:
|
||||
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_SCENARIO : STR_SAVELOAD_LOAD_SCENARIO;
|
||||
break;
|
||||
|
||||
case FT_HEIGHTMAP:
|
||||
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP;
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
this->GetWidget<NWidgetCore>(WID_SL_CAPTION)->widget_data = caption_string;
|
||||
|
||||
this->vscroll = this->GetScrollbar(WID_SL_SCROLLBAR);
|
||||
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. */
|
||||
@@ -290,24 +353,22 @@ public:
|
||||
}
|
||||
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
|
||||
|
||||
this->OnInvalidateData(0);
|
||||
this->OnInvalidateData(SLIWD_RESCAN_FILES);
|
||||
|
||||
ResetObjectToPlace();
|
||||
|
||||
/* Select the initial directory. */
|
||||
o_dir.type = FIOS_TYPE_DIRECT;
|
||||
switch (_saveload_mode) {
|
||||
case SLD_SAVE_GAME:
|
||||
case SLD_LOAD_GAME:
|
||||
switch (this->abstract_filetype) {
|
||||
case FT_SAVEGAME:
|
||||
FioGetDirectory(o_dir.name, lastof(o_dir.name), SAVE_DIR);
|
||||
break;
|
||||
|
||||
case SLD_SAVE_SCENARIO:
|
||||
case SLD_LOAD_SCENARIO:
|
||||
case FT_SCENARIO:
|
||||
FioGetDirectory(o_dir.name, lastof(o_dir.name), SCENARIO_DIR);
|
||||
break;
|
||||
|
||||
case SLD_SAVE_HEIGHTMAP:
|
||||
case SLD_LOAD_HEIGHTMAP:
|
||||
case FT_HEIGHTMAP:
|
||||
FioGetDirectory(o_dir.name, lastof(o_dir.name), HEIGHTMAP_DIR);
|
||||
break;
|
||||
|
||||
@@ -315,9 +376,14 @@ public:
|
||||
strecpy(o_dir.name, _personal_dir, lastof(o_dir.name));
|
||||
}
|
||||
|
||||
/* Focus the edit box by default in the save windows */
|
||||
if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +393,6 @@ public:
|
||||
if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
|
||||
DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE);
|
||||
}
|
||||
FiosFreeSavegameList();
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
@@ -360,13 +425,17 @@ public:
|
||||
GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, PC_BLACK);
|
||||
|
||||
uint y = r.top + WD_FRAMERECT_TOP;
|
||||
for (uint pos = this->vscroll->GetPosition(); pos < _fios_items.Length(); pos++) {
|
||||
const FiosItem *item = _fios_items.Get(pos);
|
||||
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) {
|
||||
GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_BLUE);
|
||||
}
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[item->type]);
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[GetDetailedFileType(item->type)]);
|
||||
y += this->resize.step_height;
|
||||
if (y >= this->vscroll->GetCapacity() * this->resize.step_height + r.top + WD_FRAMERECT_TOP) break;
|
||||
}
|
||||
@@ -421,7 +490,7 @@ public:
|
||||
if (y > y_max) break;
|
||||
|
||||
/* Hide current date for scenarios */
|
||||
if (_saveload_mode != SLD_LOAD_SCENARIO && _saveload_mode != SLD_SAVE_SCENARIO) {
|
||||
if (this->abstract_filetype != FT_SCENARIO) {
|
||||
/* Current date */
|
||||
SetDParam(0, _load_check_data.current_date);
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CURRENT_DATE);
|
||||
@@ -429,7 +498,7 @@ public:
|
||||
}
|
||||
|
||||
/* Hide the NewGRF stuff when saving. We also hide the button. */
|
||||
if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
|
||||
if (this->fop == SLO_LOAD && (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO)) {
|
||||
y += WD_PAR_VSEP_NORMAL;
|
||||
if (y > y_max) break;
|
||||
|
||||
@@ -442,7 +511,7 @@ public:
|
||||
if (y > y_max) break;
|
||||
|
||||
/* Hide the company stuff for scenarios */
|
||||
if (_saveload_mode != SLD_LOAD_SCENARIO && _saveload_mode != SLD_SAVE_SCENARIO) {
|
||||
if (this->abstract_filetype != FT_SCENARIO) {
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
if (y > y_max) break;
|
||||
|
||||
@@ -495,10 +564,9 @@ public:
|
||||
{
|
||||
if (_savegame_sort_dirty) {
|
||||
_savegame_sort_dirty = false;
|
||||
MakeSortedSaveGameList();
|
||||
SortSaveGameList(this->fios_items);
|
||||
}
|
||||
|
||||
this->vscroll->SetCount(_fios_items.Length());
|
||||
this->DrawWidgets();
|
||||
}
|
||||
|
||||
@@ -521,20 +589,20 @@ 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:
|
||||
if (this->selected != NULL && !_load_check_data.HasErrors()) {
|
||||
const char *name = FiosBrowseTo(this->selected);
|
||||
SetFiosType(this->selected->type);
|
||||
_file_to_saveload.SetMode(this->selected->type);
|
||||
_file_to_saveload.SetName(name);
|
||||
_file_to_saveload.SetTitle(this->selected->title);
|
||||
|
||||
strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
|
||||
strecpy(_file_to_saveload.title, this->selected->title, lastof(_file_to_saveload.title));
|
||||
|
||||
if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
|
||||
if (this->abstract_filetype == FT_HEIGHTMAP) {
|
||||
delete this;
|
||||
ShowHeightmapLoad();
|
||||
|
||||
} else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) {
|
||||
_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
|
||||
ClearErrorMessages();
|
||||
@@ -563,7 +631,13 @@ public:
|
||||
int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WD_FRAMERECT_TOP);
|
||||
if (y == INT_MAX) return;
|
||||
|
||||
const FiosItem *file = _fios_items.Get(y);
|
||||
/* 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);
|
||||
if (name != NULL) {
|
||||
@@ -572,33 +646,37 @@ public:
|
||||
this->selected = file;
|
||||
_load_check_data.Clear();
|
||||
|
||||
if (file->type == FIOS_TYPE_FILE || file->type == FIOS_TYPE_SCENARIO) {
|
||||
SaveOrLoad(name, SL_LOAD_CHECK, NO_DIRECTORY, false);
|
||||
if (GetDetailedFileType(file->type) == DFT_GAME_FILE) {
|
||||
/* Other detailed file types cannot be checked before. */
|
||||
SaveOrLoad(name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false);
|
||||
}
|
||||
|
||||
this->InvalidateData(1);
|
||||
this->InvalidateData(SLIWD_SELECTION_CHANGES);
|
||||
}
|
||||
if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP) {
|
||||
if (this->fop == SLO_SAVE) {
|
||||
/* Copy clicked name to editbox */
|
||||
this->filename_editbox.text.Assign(file->title);
|
||||
this->SetWidgetDirty(WID_SL_SAVE_OSK_TITLE);
|
||||
}
|
||||
} else if (!_load_check_data.HasErrors()) {
|
||||
this->selected = file;
|
||||
if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
|
||||
this->OnClick(pt, WID_SL_LOAD_BUTTON, 1);
|
||||
} else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
|
||||
SetFiosType(file->type);
|
||||
strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
|
||||
strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
|
||||
if (this->fop == SLO_LOAD) {
|
||||
if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) {
|
||||
this->OnClick(pt, WID_SL_LOAD_BUTTON, 1);
|
||||
} else {
|
||||
assert(this->abstract_filetype == FT_HEIGHTMAP);
|
||||
_file_to_saveload.SetMode(file->type);
|
||||
_file_to_saveload.SetName(name);
|
||||
_file_to_saveload.SetTitle(file->title);
|
||||
|
||||
delete this;
|
||||
ShowHeightmapLoad();
|
||||
delete this;
|
||||
ShowHeightmapLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Changed directory, need refresh. */
|
||||
this->InvalidateData();
|
||||
this->InvalidateData(SLIWD_RESCAN_FILES);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -608,10 +686,11 @@ public:
|
||||
ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
|
||||
} else {
|
||||
#if defined(ENABLE_NETWORK)
|
||||
switch (_saveload_mode) {
|
||||
assert(this->fop == SLO_LOAD);
|
||||
switch (this->abstract_filetype) {
|
||||
default: NOT_REACHED();
|
||||
case SLD_LOAD_SCENARIO: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO); break;
|
||||
case SLD_LOAD_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break;
|
||||
case FT_SCENARIO: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO); break;
|
||||
case FT_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -639,20 +718,19 @@ public:
|
||||
|
||||
virtual void OnTimeout()
|
||||
{
|
||||
/* This test protects against using widgets 11 and 12 which are only available
|
||||
* in those saveload modes. */
|
||||
if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP)) return;
|
||||
/* Widgets WID_SL_DELETE_SELECTION and WID_SL_SAVE_GAME only exist when saving to a file. */
|
||||
if (this->fop != SLO_SAVE) return;
|
||||
|
||||
if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked
|
||||
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 (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
|
||||
if (this->abstract_filetype == FT_SAVEGAME) GenerateFileName();
|
||||
}
|
||||
} else if (this->IsWidgetLowered(WID_SL_SAVE_GAME)) { // Save button clicked
|
||||
if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
|
||||
if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) {
|
||||
_switch_mode = SM_SAVE_GAME;
|
||||
FiosMakeSavegameName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name));
|
||||
} else {
|
||||
@@ -678,34 +756,86 @@ 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();
|
||||
if (!gui_scope) break;
|
||||
BuildFileList();
|
||||
/* FALL THROUGH */
|
||||
case 1:
|
||||
/* Selection changes */
|
||||
if (!gui_scope) break;
|
||||
if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
|
||||
this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, this->selected == NULL || _load_check_data.HasErrors());
|
||||
}
|
||||
if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
|
||||
this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON,
|
||||
this->selected == NULL || _load_check_data.HasErrors() || !(!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()));
|
||||
this->SetWidgetDisabledState(WID_SL_NEWGRF_INFO,
|
||||
!_load_check_data.HasNewGrfs());
|
||||
this->SetWidgetDisabledState(WID_SL_MISSING_NEWGRFS,
|
||||
!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* _fios_items changed */
|
||||
this->vscroll->SetCount(_fios_items.Length());
|
||||
|
||||
_fios_path_changed = true;
|
||||
this->fios_items.BuildFileList(this->abstract_filetype, this->fop);
|
||||
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 SLIWD_SELECTION_CHANGES:
|
||||
/* Selection changes */
|
||||
if (!gui_scope) break;
|
||||
|
||||
if (this->fop != SLO_LOAD) break;
|
||||
|
||||
switch (this->abstract_filetype) {
|
||||
case FT_HEIGHTMAP:
|
||||
this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, this->selected == NULL || _load_check_data.HasErrors());
|
||||
break;
|
||||
|
||||
case FT_SAVEGAME:
|
||||
case FT_SCENARIO: {
|
||||
bool disabled = this->selected == NULL || _load_check_data.HasErrors();
|
||||
if (!_settings_client.gui.UserIsAllowedToChangeNewGRFs()) {
|
||||
disabled |= _load_check_data.HasNewGrfs() && _load_check_data.grf_compatibility == GLC_NOT_FOUND;
|
||||
}
|
||||
this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, disabled);
|
||||
this->SetWidgetDisabledState(WID_SL_NEWGRF_INFO, !_load_check_data.HasNewGrfs());
|
||||
this->SetWidgetDisabledState(WID_SL_MISSING_NEWGRFS,
|
||||
!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -734,70 +864,24 @@ static WindowDesc _save_dialog_desc(
|
||||
_nested_save_dialog_widgets, lengthof(_nested_save_dialog_widgets)
|
||||
);
|
||||
|
||||
/**
|
||||
* These values are used to convert the file/operations mode into a corresponding file type.
|
||||
* So each entry, as expressed by the related comment, is based on the enum
|
||||
*/
|
||||
static const FileType _file_modetotype[] = {
|
||||
FT_SAVEGAME, // used for SLD_LOAD_GAME
|
||||
FT_SCENARIO, // used for SLD_LOAD_SCENARIO
|
||||
FT_SAVEGAME, // used for SLD_SAVE_GAME
|
||||
FT_SCENARIO, // used for SLD_SAVE_SCENARIO
|
||||
FT_HEIGHTMAP, // used for SLD_LOAD_HEIGHTMAP
|
||||
FT_HEIGHTMAP, // used for SLD_SAVE_HEIGHTMAP
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch save/load dialog in the given mode.
|
||||
* @param mode Save/load mode.
|
||||
* @param abstract_filetype Kind of file to handle.
|
||||
* @param fop File operation to perform (load or save).
|
||||
*/
|
||||
void ShowSaveLoadDialog(SaveLoadDialogMode mode)
|
||||
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop)
|
||||
{
|
||||
DeleteWindowById(WC_SAVELOAD, 0);
|
||||
|
||||
WindowDesc *sld;
|
||||
switch (mode) {
|
||||
case SLD_SAVE_GAME:
|
||||
case SLD_SAVE_SCENARIO:
|
||||
case SLD_SAVE_HEIGHTMAP:
|
||||
sld = &_save_dialog_desc; break;
|
||||
case SLD_LOAD_HEIGHTMAP:
|
||||
sld = &_load_heightmap_dialog_desc; break;
|
||||
default:
|
||||
sld = &_load_dialog_desc; break;
|
||||
if (fop == SLO_SAVE) {
|
||||
sld = &_save_dialog_desc;
|
||||
} else {
|
||||
/* Dialogue for loading a file. */
|
||||
sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc;
|
||||
}
|
||||
|
||||
_saveload_mode = mode;
|
||||
_file_to_saveload.filetype = _file_modetotype[mode];
|
||||
_file_to_saveload.abstract_ftype = abstract_filetype;
|
||||
|
||||
new SaveLoadWindow(sld, mode);
|
||||
}
|
||||
|
||||
void SetFiosType(const byte fiostype)
|
||||
{
|
||||
switch (fiostype) {
|
||||
case FIOS_TYPE_FILE:
|
||||
case FIOS_TYPE_SCENARIO:
|
||||
_file_to_saveload.mode = SL_LOAD;
|
||||
break;
|
||||
|
||||
case FIOS_TYPE_OLDFILE:
|
||||
case FIOS_TYPE_OLD_SCENARIO:
|
||||
_file_to_saveload.mode = SL_OLD_LOAD;
|
||||
break;
|
||||
|
||||
#ifdef WITH_PNG
|
||||
case FIOS_TYPE_PNG:
|
||||
_file_to_saveload.mode = SL_PNG;
|
||||
break;
|
||||
#endif /* WITH_PNG */
|
||||
|
||||
case FIOS_TYPE_BMP:
|
||||
_file_to_saveload.mode = SL_BMP;
|
||||
break;
|
||||
|
||||
default:
|
||||
_file_to_saveload.mode = SL_INVALID;
|
||||
break;
|
||||
}
|
||||
new SaveLoadWindow(sld, abstract_filetype, fop);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs), height(_
|
||||
ascender(_default_font_ascender[fs]), descender(_default_font_ascender[fs] - _default_font_height[fs]),
|
||||
units_per_em(1)
|
||||
{
|
||||
assert(parent == NULL || this->fs == parent->fs);
|
||||
assert(this->parent == NULL || this->fs == this->parent->fs);
|
||||
FontCache::caches[this->fs] = this;
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs), height(_
|
||||
/** Clean everything up. */
|
||||
FontCache::~FontCache()
|
||||
{
|
||||
assert(this->fs == parent->fs);
|
||||
assert(this->fs == this->parent->fs);
|
||||
FontCache::caches[this->fs] = this->parent;
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
}
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -126,7 +127,7 @@ void SpriteFontCache::InitializeUnicodeGlyphMap()
|
||||
SpriteID base;
|
||||
switch (this->fs) {
|
||||
default: NOT_REACHED();
|
||||
case FS_MONO: // Use normal as default for mono spaced font, i.e. FALL THROUGH
|
||||
case FS_MONO: // Use normal as default for mono spaced font
|
||||
case FS_NORMAL: base = SPR_ASCII_SPACE; break;
|
||||
case FS_SMALL: base = SPR_ASCII_SPACE_SMALL; break;
|
||||
case FS_LARGE: base = SPR_ASCII_SPACE_BIG; break;
|
||||
@@ -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)
|
||||
@@ -474,7 +491,7 @@ static bool GetFontAAState(FontSize size)
|
||||
|
||||
const Sprite *FreeTypeFontCache::GetGlyph(GlyphID key)
|
||||
{
|
||||
if ((key & SPRITE_GLYPH) != 0) return parent->GetGlyph(key);
|
||||
if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyph(key);
|
||||
|
||||
/* Check for the glyph in our cache */
|
||||
GlyphEntry *glyph = this->GetGlyphPtr(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>
|
||||
@@ -382,38 +382,67 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
/* Get font reference from name. */
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
|
||||
ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
|
||||
CFRelease(name);
|
||||
if (font == kInvalidFont) return err;
|
||||
|
||||
/* Get a file system reference for the font. */
|
||||
FSRef ref;
|
||||
UInt8 file_path[PATH_MAX];
|
||||
OSStatus os_err = -1;
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
if (MacOSVersionIsAtLeast(10, 5, 0)) {
|
||||
os_err = ATSFontGetFileReference(font, &ref);
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
if (MacOSVersionIsAtLeast(10, 6, 0)) {
|
||||
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
|
||||
* something, no matter the name. As such, we can't use it to check for existance.
|
||||
* We instead query the list of all font descriptors that match the given name which
|
||||
* does not do this stupid name fallback. */
|
||||
CTFontDescriptorRef name_desc = CTFontDescriptorCreateWithNameAndSize(name, 0.0);
|
||||
CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontNameAttribute, 1, &kCFTypeSetCallBacks);
|
||||
CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(name_desc, mandatory_attribs);
|
||||
CFRelease(mandatory_attribs);
|
||||
CFRelease(name_desc);
|
||||
CFRelease(name);
|
||||
|
||||
/* Loop over all matches until we can get a path for one of them. */
|
||||
for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs) && os_err != noErr; i++) {
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i), 0.0, NULL);
|
||||
CFURLRef fontURL = (CFURLRef)CTFontCopyAttribute(font, kCTFontURLAttribute);
|
||||
if (CFURLGetFileSystemRepresentation(fontURL, true, file_path, lengthof(file_path))) os_err = noErr;
|
||||
CFRelease(font);
|
||||
CFRelease(fontURL);
|
||||
}
|
||||
if (descs != NULL) CFRelease(descs);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
|
||||
/* This type was introduced with the 10.5 SDK. */
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
|
||||
#define ATSFSSpec FSSpec
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6)
|
||||
ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
|
||||
CFRelease(name);
|
||||
if (font == kInvalidFont) return err;
|
||||
|
||||
/* Get a file system reference for the font. */
|
||||
FSRef ref;
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
if (MacOSVersionIsAtLeast(10, 5, 0)) {
|
||||
os_err = ATSFontGetFileReference(font, &ref);
|
||||
} else
|
||||
#endif
|
||||
FSSpec spec;
|
||||
os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
|
||||
if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
|
||||
/* This type was introduced with the 10.5 SDK. */
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
|
||||
#define ATSFSSpec FSSpec
|
||||
#endif
|
||||
FSSpec spec;
|
||||
os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
|
||||
if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get unix path for file. */
|
||||
if (os_err == noErr) os_err = FSRefMakePath(&ref, file_path, sizeof(file_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (os_err == noErr) {
|
||||
/* Get unix path for file. */
|
||||
UInt8 file_path[PATH_MAX];
|
||||
if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
|
||||
DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
|
||||
err = FT_New_Face(_library, (const char *)file_path, 0, face);
|
||||
}
|
||||
DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
|
||||
err = FT_New_Face(_library, (const char *)file_path, 0, face);
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -496,6 +525,7 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6)
|
||||
/* Create a font iterator and iterate over all fonts that
|
||||
* are available to the application. */
|
||||
ATSFontIterator itr;
|
||||
@@ -529,6 +559,7 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i
|
||||
}
|
||||
}
|
||||
ATSFontIteratorRelease(&itr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user