mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r27668) -Feature: [NewGRF] Allow composing vehicles from multiple sprites.
This commit is contained in:
parent
c175067ed9
commit
117e73751c
|
@ -525,7 +525,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->sprite_seq.sprite = v->sprite_seq.sprite;
|
||||
u->sprite_seq.CopyWithoutPalette(v->sprite_seq); // the shadow is never coloured
|
||||
|
||||
u->UpdatePositionAndViewport();
|
||||
|
||||
|
|
|
@ -499,7 +499,7 @@ static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
|
|||
v->tick_counter++;
|
||||
if (HasBit(v->tick_counter, 0)) return true;
|
||||
|
||||
SpriteID &cur_image = v->sprite_seq.sprite;
|
||||
SpriteID &cur_image = v->sprite_seq.seq[0].sprite;
|
||||
if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1;
|
||||
|
||||
v->UpdatePositionAndViewport();
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
*/
|
||||
static bool IncrementSprite(EffectVehicle *v, SpriteID last)
|
||||
{
|
||||
if (v->sprite_seq.sprite != last) {
|
||||
v->sprite_seq.sprite++;
|
||||
if (v->sprite_seq.seq[0].sprite != last) {
|
||||
v->sprite_seq.seq[0].sprite++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -476,9 +476,8 @@ static bool BubbleTick(EffectVehicle *v)
|
|||
if ((v->progress & 3) != 0) return true;
|
||||
|
||||
if (v->spritenum == 0) {
|
||||
SpriteID &cur_image = v->sprite_seq.sprite;
|
||||
cur_image++;
|
||||
if (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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1025,13 +1025,23 @@ VehicleResolverObject::VehicleResolverObject(EngineID engine_type, const Vehicle
|
|||
|
||||
void GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction, EngineImageType image_type, VehicleSpriteSeq *result)
|
||||
{
|
||||
VehicleResolverObject object(engine, v, VehicleResolverObject::WO_CACHED, false, CBID_NO_CALLBACK, image_type);
|
||||
VehicleResolverObject object(engine, v, VehicleResolverObject::WO_CACHED, false, CBID_NO_CALLBACK);
|
||||
result->Clear();
|
||||
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
if (group == NULL || group->GetNumResults() == 0) return;
|
||||
|
||||
result->Set(group->GetResult() + (direction % group->GetNumResults()));
|
||||
bool sprite_stack = HasBit(EngInfo(engine)->misc_flags, EF_SPRITE_STACK);
|
||||
uint max_stack = sprite_stack ? lengthof(result->seq) : 1;
|
||||
for (uint stack = 0; stack < max_stack; ++stack) {
|
||||
object.ResetState();
|
||||
object.callback_param1 = image_type | (stack << 8);
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
uint32 reg100 = sprite_stack ? GetRegister(0x100) : 0;
|
||||
if (group != NULL && group->GetNumResults() != 0) {
|
||||
result->seq[result->count].sprite = group->GetResult() + (direction % group->GetNumResults());
|
||||
result->seq[result->count].pal = GB(reg100, 0, 16); // zero means default recolouring
|
||||
result->count++;
|
||||
}
|
||||
if (!HasBit(reg100, 31)) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1043,15 +1053,24 @@ void GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, bool info
|
|||
assert(e->type == VEH_AIRCRAFT);
|
||||
assert(!(e->u.air.subtype & AIR_CTOL));
|
||||
|
||||
VehicleResolverObject object(engine, v, VehicleResolverObject::WO_SELF, info_view, CBID_NO_CALLBACK, image_type);
|
||||
VehicleResolverObject object(engine, v, VehicleResolverObject::WO_SELF, info_view, CBID_NO_CALLBACK);
|
||||
result->Clear();
|
||||
uint rotor_pos = v == NULL || info_view ? 0 : v->Next()->Next()->state;
|
||||
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
|
||||
if (group == NULL || group->GetNumResults() == 0) return;
|
||||
|
||||
result->Set(group->GetResult() + (rotor_pos % group->GetNumResults()));
|
||||
bool sprite_stack = HasBit(e->info.misc_flags, EF_SPRITE_STACK);
|
||||
uint max_stack = sprite_stack ? lengthof(result->seq) : 1;
|
||||
for (uint stack = 0; stack < max_stack; ++stack) {
|
||||
object.ResetState();
|
||||
object.callback_param1 = image_type | (stack << 8);
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
uint32 reg100 = sprite_stack ? GetRegister(0x100) : 0;
|
||||
if (group != NULL && group->GetNumResults() != 0) {
|
||||
result->seq[result->count].sprite = group->GetResult() + (rotor_pos % group->GetNumResults());
|
||||
result->seq[result->count].pal = GB(reg100, 0, 16); // zero means default recolouring
|
||||
result->count++;
|
||||
}
|
||||
if (!HasBit(reg100, 31)) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1165,7 +1165,7 @@ static const OldChunks vehicle_chunk[] = {
|
|||
|
||||
OCL_SVAR( OC_UINT8, Vehicle, owner ),
|
||||
OCL_SVAR( OC_TILE, Vehicle, tile ),
|
||||
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_seq.sprite ),
|
||||
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_seq.seq[0].sprite ),
|
||||
|
||||
OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically
|
||||
|
||||
|
@ -1258,7 +1258,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
|
|||
if (v == NULL) continue;
|
||||
v->refit_cap = v->cargo_cap;
|
||||
|
||||
SpriteID sprite = v->sprite_seq.sprite;
|
||||
SpriteID sprite = v->sprite_seq.seq[0].sprite;
|
||||
/* no need to override other sprites */
|
||||
if (IsInsideMM(sprite, 1460, 1465)) {
|
||||
sprite += 580; // aircraft smoke puff
|
||||
|
@ -1269,7 +1269,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
|
|||
} else if (IsInsideMM(sprite, 2516, 2539)) {
|
||||
sprite += 1385; // rotor or disaster-related vehicles
|
||||
}
|
||||
v->sprite_seq.sprite = sprite;
|
||||
v->sprite_seq.seq[0].sprite = sprite;
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN: {
|
||||
|
|
|
@ -445,7 +445,7 @@ void AfterLoadVehicles(bool part_of_load)
|
|||
|
||||
/* The plane's shadow will have the same image as the plane, but no colour */
|
||||
Vehicle *shadow = v->Next();
|
||||
shadow->sprite_seq.sprite = v->sprite_seq.sprite;
|
||||
shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq);
|
||||
|
||||
/* In the case of a helicopter we will update the rotor sprites */
|
||||
if (v->subtype == AIR_HELICOPTER) {
|
||||
|
@ -796,7 +796,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
|
|||
SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, 0, 163),
|
||||
SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, 164, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(Vehicle, sprite_seq.sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_CONDNULL(5, 0, 57),
|
||||
SLE_VAR(Vehicle, progress, SLE_UINT8),
|
||||
SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
|
||||
|
@ -836,7 +836,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
|
|||
SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
|
||||
SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, 5, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(Vehicle, sprite_seq.sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
|
||||
SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
|
||||
SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
|
||||
|
|
|
@ -75,11 +75,23 @@ INSTANTIATE_POOL_METHODS(Vehicle)
|
|||
*/
|
||||
void VehicleSpriteSeq::GetBounds(Rect *bounds) const
|
||||
{
|
||||
const Sprite *spr = GetSprite(this->sprite, ST_NORMAL);
|
||||
bounds->left = spr->x_offs;
|
||||
bounds->top = spr->y_offs;
|
||||
bounds->right = spr->width + spr->x_offs - 1;
|
||||
bounds->bottom = spr->height + spr->y_offs - 1;
|
||||
bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
|
||||
for (uint i = 0; i < this->count; ++i) {
|
||||
const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
|
||||
if (i == 0) {
|
||||
bounds->left = spr->x_offs;
|
||||
bounds->top = spr->y_offs;
|
||||
bounds->right = spr->width + spr->x_offs - 1;
|
||||
bounds->bottom = spr->height + spr->y_offs - 1;
|
||||
} else {
|
||||
if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
|
||||
if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
|
||||
int right = spr->width + spr->x_offs - 1;
|
||||
int bottom = spr->height + spr->y_offs - 1;
|
||||
if (right > bounds->right) bounds->right = right;
|
||||
if (bottom > bounds->bottom) bounds->bottom = bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +103,10 @@ void VehicleSpriteSeq::GetBounds(Rect *bounds) const
|
|||
*/
|
||||
void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
|
||||
{
|
||||
DrawSprite(this->sprite, default_pal, x, y);
|
||||
for (uint i = 0; i < this->count; ++i) {
|
||||
PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
|
||||
DrawSprite(this->seq[i].sprite, pal, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1048,8 +1063,14 @@ static void DoDrawVehicle(const Vehicle *v)
|
|||
if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
|
||||
}
|
||||
|
||||
AddSortableSpriteToDraw(v->sprite_seq.sprite, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
|
||||
v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
|
||||
StartSpriteCombine();
|
||||
for (uint i = 0; i < v->sprite_seq.count; ++i) {
|
||||
PaletteID pal2 = v->sprite_seq.seq[i].pal;
|
||||
if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
|
||||
AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
|
||||
v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
|
||||
}
|
||||
EndSpriteCombine();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,11 +128,12 @@ struct VehicleCache {
|
|||
|
||||
/** Sprite sequence for a vehicle part. */
|
||||
struct VehicleSpriteSeq {
|
||||
SpriteID sprite;
|
||||
PalSpriteID seq[4];
|
||||
uint count;
|
||||
|
||||
bool operator==(const VehicleSpriteSeq &other) const
|
||||
{
|
||||
return this->sprite == other.sprite;
|
||||
return this->count == other.count && MemCmpT<PalSpriteID>(this->seq, other.seq, this->count) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const VehicleSpriteSeq &other) const
|
||||
|
@ -145,7 +146,7 @@ struct VehicleSpriteSeq {
|
|||
*/
|
||||
bool IsValid() const
|
||||
{
|
||||
return this->sprite != 0;
|
||||
return this->count != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +154,7 @@ struct VehicleSpriteSeq {
|
|||
*/
|
||||
void Clear()
|
||||
{
|
||||
this->sprite = 0;
|
||||
this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +162,21 @@ struct VehicleSpriteSeq {
|
|||
*/
|
||||
void Set(SpriteID sprite)
|
||||
{
|
||||
this->sprite = sprite;
|
||||
this->count = 1;
|
||||
this->seq[0].sprite = sprite;
|
||||
this->seq[0].pal = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from another sprite sequence, while dropping all recolouring information.
|
||||
*/
|
||||
void CopyWithoutPalette(const VehicleSpriteSeq &src)
|
||||
{
|
||||
this->count = src.count;
|
||||
for (uint i = 0; i < src.count; ++i) {
|
||||
this->seq[i].sprite = src.seq[i].sprite;
|
||||
this->seq[i].pal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GetBounds(Rect *bounds) const;
|
||||
|
@ -982,7 +997,10 @@ struct SpecializedVehicle : public Vehicle {
|
|||
/**
|
||||
* Set vehicle type correctly
|
||||
*/
|
||||
inline SpecializedVehicle<T, Type>() : Vehicle(Type) { }
|
||||
inline SpecializedVehicle<T, Type>() : Vehicle(Type)
|
||||
{
|
||||
this->sprite_seq.count = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first vehicle in the chain
|
||||
|
|
|
@ -2880,20 +2880,24 @@ void SetMouseCursorVehicle(const Vehicle *v, EngineImageType image_type)
|
|||
_cursor.sprite_count = 0;
|
||||
int total_width = 0;
|
||||
for (; v != NULL; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : NULL) {
|
||||
if (_cursor.sprite_count == lengthof(_cursor.sprite_seq)) break;
|
||||
if (total_width >= 2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH) break;
|
||||
|
||||
PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
|
||||
VehicleSpriteSeq seq;
|
||||
v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq);
|
||||
|
||||
_cursor.sprite_seq[_cursor.sprite_count].sprite = seq.sprite;
|
||||
_cursor.sprite_seq[_cursor.sprite_count].pal = pal;
|
||||
_cursor.sprite_pos[_cursor.sprite_count].x = rtl ? -total_width : total_width;
|
||||
_cursor.sprite_pos[_cursor.sprite_count].y = 0;
|
||||
if (_cursor.sprite_count + seq.count > lengthof(_cursor.sprite_seq)) break;
|
||||
|
||||
for (uint i = 0; i < seq.count; ++i) {
|
||||
PaletteID pal2 = (v->vehstatus & VS_CRASHED) || !seq.seq[i].pal ? pal : seq.seq[i].pal;
|
||||
_cursor.sprite_seq[_cursor.sprite_count].sprite = seq.seq[i].sprite;
|
||||
_cursor.sprite_seq[_cursor.sprite_count].pal = pal2;
|
||||
_cursor.sprite_pos[_cursor.sprite_count].x = rtl ? -total_width : total_width;
|
||||
_cursor.sprite_pos[_cursor.sprite_count].y = 0;
|
||||
_cursor.sprite_count++;
|
||||
}
|
||||
|
||||
total_width += GetSingleVehicleWidth(v, image_type);
|
||||
_cursor.sprite_count++;
|
||||
}
|
||||
|
||||
int offs = ((int)VEHICLEINFO_FULL_VEHICLE_WIDTH - total_width) / 2;
|
||||
|
|
Loading…
Reference in New Issue