OpenTTD Source 20260512-master-g20b387b91f
effectvehicle.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "landscape.h"
12#include "core/random_func.hpp"
13#include "industry_map.h"
14#include "vehicle_func.h"
15#include "sound_func.h"
16#include "animated_tile_func.h"
17#include "effectvehicle_func.h"
18#include "effectvehicle_base.h"
19
20#include "safeguards.h"
21
22
30{
31 if (v->sprite_cache.sprite_seq.seq[0].sprite != last) {
32 v->sprite_cache.sprite_seq.seq[0].sprite++;
33 return true;
34 } else {
35 return false;
36 }
37}
38
41{
42 uint32_t r = Random();
43 v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
44 v->progress = GB(r, 16, 3);
45}
46
49{
50 if (v->progress > 0) {
51 v->progress--;
52 } else {
53 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
54 if (!IsTileType(tile, TileType::Industry)) {
55 delete v;
56 return false;
57 }
58
59 if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
60 v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
61 }
62 v->progress = 7;
64 }
65
66 return true;
67}
68
71{
72 v->sprite_cache.sprite_seq.Set(SPR_STEAM_SMOKE_0);
73 v->progress = 12;
74}
75
78{
79 bool moved = false;
80
81 v->progress++;
82
83 if ((v->progress & 7) == 0) {
84 v->z_pos++;
85 moved = true;
86 }
87
88 if ((v->progress & 0xF) == 4) {
89 if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) {
90 delete v;
91 return false;
92 }
93 moved = true;
94 }
95
96 if (moved) v->UpdatePositionAndViewport();
97
98 return true;
99}
100
103{
104 v->sprite_cache.sprite_seq.Set(SPR_DIESEL_SMOKE_0);
105 v->progress = 0;
106}
107
110{
111 v->progress++;
112
113 if ((v->progress & 3) == 0) {
114 v->z_pos++;
116 } else if ((v->progress & 7) == 1) {
117 if (!IncrementSprite(v, SPR_DIESEL_SMOKE_5)) {
118 delete v;
119 return false;
120 }
122 }
123
124 return true;
125}
126
129{
130 v->sprite_cache.sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
131 v->progress = 1;
132}
133
136{
137 if (v->progress < 2) {
138 v->progress++;
139 } else {
140 v->progress = 0;
141
142 if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) {
143 delete v;
144 return false;
145 }
147 }
148
149 return true;
150}
151
154{
155 v->sprite_cache.sprite_seq.Set(SPR_SMOKE_0);
156 v->progress = 12;
157}
158
161{
162 bool moved = false;
163
164 v->progress++;
165
166 if ((v->progress & 3) == 0) {
167 v->z_pos++;
168 moved = true;
169 }
170
171 if ((v->progress & 0xF) == 4) {
172 if (!IncrementSprite(v, SPR_SMOKE_4)) {
173 delete v;
174 return false;
175 }
176 moved = true;
177 }
178
179 if (moved) v->UpdatePositionAndViewport();
180
181 return true;
182}
183
186{
187 v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
188 v->progress = 0;
189}
190
193{
194 v->progress++;
195 if ((v->progress & 3) == 0) {
196 if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) {
197 delete v;
198 return false;
199 }
201 }
202
203 return true;
204}
205
208{
209 v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
210 v->progress = 0;
211}
212
215{
216 v->progress++;
217 if ((v->progress & 7) == 0) {
218 if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) {
219 v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
220 }
222 }
223
224 v->animation_state--;
225 if (v->animation_state == 0) {
226 delete v;
227 return false;
228 }
229
230 return true;
231}
232
235{
236 v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
237 v->progress = 0;
238}
239
242{
243 v->progress++;
244 if ((v->progress & 3) == 0) {
245 if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) {
246 delete v;
247 return false;
248 }
250 }
251
252 return true;
253}
254
257{
258 v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE);
259 v->progress = 0;
260 v->animation_state = 0;
261 v->animation_substate = 0;
262}
263
265 uint8_t direction:2;
266 uint8_t image:2;
267 uint8_t duration:3;
268};
269
270static const BulldozerMovement _bulldozer_movement[] = {
271 { 0, 0, 4 },
272 { 3, 3, 4 },
273 { 2, 2, 7 },
274 { 0, 2, 7 },
275 { 1, 1, 3 },
276 { 2, 2, 7 },
277 { 0, 2, 7 },
278 { 1, 1, 3 },
279 { 2, 2, 7 },
280 { 0, 2, 7 },
281 { 3, 3, 6 },
282 { 2, 2, 6 },
283 { 1, 1, 7 },
284 { 3, 1, 7 },
285 { 0, 0, 3 },
286 { 1, 1, 7 },
287 { 3, 1, 7 },
288 { 0, 0, 3 },
289 { 1, 1, 7 },
290 { 3, 1, 7 }
291};
292
293static const Coord2D<int8_t> _inc_by_dir[] = {
294 { -1, 0 },
295 { 0, 1 },
296 { 1, 0 },
297 { 0, -1 }
298};
299
302{
303 v->progress++;
304 if ((v->progress & 7) == 0) {
305 const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
306
307 v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
308
309 v->x_pos += _inc_by_dir[b->direction].x;
310 v->y_pos += _inc_by_dir[b->direction].y;
311
313 if (v->animation_substate >= b->duration) {
314 v->animation_substate = 0;
315 v->animation_state++;
316 if (v->animation_state == lengthof(_bulldozer_movement)) {
317 delete v;
318 return false;
319 }
320 }
322 }
323
324 return true;
325}
326
329{
330 v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
331 v->spritenum = 0;
332 v->progress = 0;
333}
334
336 int8_t x:4;
337 int8_t y:4;
338 int8_t z:4;
339 uint8_t image:4;
340};
341
342#define MK(x, y, z, i) { x, y, z, i }
343#define ME(i) { i, 4, 0, 0 }
344
345static const BubbleMovement _bubble_float_sw[] = {
346 MK(0, 0, 1, 0),
347 MK(1, 0, 1, 1),
348 MK(0, 0, 1, 0),
349 MK(1, 0, 1, 2),
350 ME(1)
351};
352
353
354static const BubbleMovement _bubble_float_ne[] = {
355 MK( 0, 0, 1, 0),
356 MK(-1, 0, 1, 1),
357 MK( 0, 0, 1, 0),
358 MK(-1, 0, 1, 2),
359 ME(1)
360};
361
362static const BubbleMovement _bubble_float_se[] = {
363 MK(0, 0, 1, 0),
364 MK(0, 1, 1, 1),
365 MK(0, 0, 1, 0),
366 MK(0, 1, 1, 2),
367 ME(1)
368};
369
370static const BubbleMovement _bubble_float_nw[] = {
371 MK(0, 0, 1, 0),
372 MK(0, -1, 1, 1),
373 MK(0, 0, 1, 0),
374 MK(0, -1, 1, 2),
375 ME(1)
376};
377
378static const BubbleMovement _bubble_burst[] = {
379 MK(0, 0, 1, 2),
380 MK(0, 0, 1, 7),
381 MK(0, 0, 1, 8),
382 MK(0, 0, 1, 9),
383 ME(0)
384};
385
386static const BubbleMovement _bubble_absorb[] = {
387 MK(0, 0, 1, 0),
388 MK(0, 0, 1, 1),
389 MK(0, 0, 1, 0),
390 MK(0, 0, 1, 2),
391 MK(0, 0, 1, 0),
392 MK(0, 0, 1, 1),
393 MK(0, 0, 1, 0),
394 MK(0, 0, 1, 2),
395 MK(0, 0, 1, 0),
396 MK(0, 0, 1, 1),
397 MK(0, 0, 1, 0),
398 MK(0, 0, 1, 2),
399 MK(0, 0, 1, 0),
400 MK(0, 0, 1, 1),
401 MK(0, 0, 1, 0),
402 MK(0, 0, 1, 2),
403 MK(0, 0, 1, 0),
404 MK(0, 0, 1, 1),
405 MK(0, 0, 1, 0),
406 MK(0, 0, 1, 2),
407 MK(0, 0, 1, 0),
408 MK(0, 0, 1, 1),
409 MK(0, 0, 1, 0),
410 MK(0, 0, 1, 2),
411 MK(0, 0, 1, 0),
412 MK(0, 0, 1, 1),
413 MK(0, 0, 1, 0),
414 MK(0, 0, 1, 2),
415 MK(0, 0, 1, 0),
416 MK(0, 0, 1, 1),
417 MK(0, 0, 1, 0),
418 MK(0, 0, 1, 2),
419 MK(0, 0, 1, 0),
420 MK(0, 0, 1, 1),
421 MK(0, 0, 1, 0),
422 MK(0, 0, 1, 2),
423 MK(0, 0, 1, 0),
424 MK(0, 0, 1, 1),
425 MK(0, 0, 1, 0),
426 MK(0, 0, 1, 2),
427 MK(0, 0, 1, 0),
428 MK(0, 0, 1, 1),
429 MK(0, 0, 1, 0),
430 MK(0, 0, 1, 2),
431 MK(0, 0, 1, 0),
432 MK(0, 0, 1, 1),
433 MK(0, 0, 1, 0),
434 MK(0, 0, 1, 2),
435 MK(0, 0, 1, 0),
436 MK(0, 0, 1, 1),
437 MK(0, 0, 1, 0),
438 MK(0, 0, 1, 2),
439 MK(0, 0, 1, 0),
440 MK(0, 0, 1, 1),
441 MK(0, 0, 1, 0),
442 MK(0, 0, 1, 2),
443 MK(0, 0, 1, 0),
444 MK(0, 0, 1, 1),
445 MK(0, 0, 1, 0),
446 MK(0, 0, 1, 2),
447 MK(0, 0, 1, 0),
448 MK(0, 0, 1, 1),
449 MK(2, 1, 3, 0),
450 MK(1, 1, 3, 1),
451 MK(2, 1, 3, 0),
452 MK(1, 1, 3, 2),
453 MK(2, 1, 3, 0),
454 MK(1, 1, 3, 1),
455 MK(2, 1, 3, 0),
456 MK(1, 0, 1, 2),
457 MK(0, 0, 1, 0),
458 MK(1, 0, 1, 1),
459 MK(0, 0, 1, 0),
460 MK(1, 0, 1, 2),
461 MK(0, 0, 1, 0),
462 MK(1, 0, 1, 1),
463 MK(0, 0, 1, 0),
464 MK(1, 0, 1, 2),
465 ME(2),
466 MK(0, 0, 0, 0xA),
467 MK(0, 0, 0, 0xB),
468 MK(0, 0, 0, 0xC),
469 MK(0, 0, 0, 0xD),
470 MK(0, 0, 0, 0xE),
471 ME(0)
472};
473#undef ME
474#undef MK
475
476static const BubbleMovement * const _bubble_movement[] = {
477 _bubble_float_sw,
478 _bubble_float_ne,
479 _bubble_float_se,
480 _bubble_float_nw,
481 _bubble_burst,
482 _bubble_absorb,
483};
484
487{
488 uint anim_state;
489
490 v->progress++;
491 if ((v->progress & 3) != 0) return true;
492
493 if (v->spritenum == 0) {
494 v->sprite_cache.sprite_seq.seq[0].sprite++;
495 if (v->sprite_cache.sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
497 return true;
498 }
499 if (v->animation_substate != 0) {
500 v->spritenum = GB(Random(), 0, 2) + 1;
501 } else {
502 v->spritenum = 6;
503 }
504 anim_state = 0;
505 } else {
506 anim_state = v->animation_state + 1;
507 }
508
509 const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][anim_state];
510
511 if (b->y == 4 && b->x == 0) {
512 delete v;
513 return false;
514 }
515
516 if (b->y == 4 && b->x == 1) {
517 if (v->z_pos > 180 || Chance16I(1, 96, Random())) {
518 v->spritenum = 5;
519 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_2F_BUBBLE_GENERATOR_FAIL, v);
520 }
521 anim_state = 0;
522 }
523
524 if (b->y == 4 && b->x == 2) {
525 TileIndex tile;
526
527 anim_state++;
528 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_31_BUBBLE_GENERATOR_SUCCESS, v);
529
530 tile = TileVirtXY(v->x_pos, v->y_pos);
531 if (IsTileType(tile, TileType::Industry) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
532 }
533
534 v->animation_state = anim_state;
535 b = &_bubble_movement[v->spritenum - 1][anim_state];
536
537 v->x_pos += b->x;
538 v->y_pos += b->y;
539 v->z_pos += b->z;
540 v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_0 + b->image);
541
543
544 return true;
545}
546
569
571static const std::array<EffectProcs, EV_END> _effect_procs = {{
572 { ChimneySmokeInit, ChimneySmokeTick, TO_INDUSTRIES }, // EV_CHIMNEY_SMOKE
573 { SteamSmokeInit, SteamSmokeTick, TO_INVALID }, // EV_STEAM_SMOKE
574 { DieselSmokeInit, DieselSmokeTick, TO_INVALID }, // EV_DIESEL_SMOKE
575 { ElectricSparkInit, ElectricSparkTick, TO_INVALID }, // EV_ELECTRIC_SPARK
576 { SmokeInit, SmokeTick, TO_INVALID }, // EV_CRASH_SMOKE
577 { ExplosionLargeInit, ExplosionLargeTick, TO_INVALID }, // EV_EXPLOSION_LARGE
578 { BreakdownSmokeInit, BreakdownSmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE
579 { ExplosionSmallInit, ExplosionSmallTick, TO_INVALID }, // EV_EXPLOSION_SMALL
580 { BulldozerInit, BulldozerTick, TO_INVALID }, // EV_BULLDOZER
581 { BubbleInit, BubbleTick, TO_INDUSTRIES }, // EV_BUBBLE
582 { SmokeInit, SmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE_AIRCRAFT
583 { SmokeInit, SmokeTick, TO_INDUSTRIES }, // EV_COPPER_MINE_SMOKE
584}};
585
595{
596 if (!Vehicle::CanAllocateItem()) return nullptr;
597
599 v->subtype = type;
600 v->x_pos = x;
601 v->y_pos = y;
602 v->z_pos = z;
603 v->tile = TileIndex{};
604 v->UpdateDeltaXY();
606
607 _effect_procs[type].init_proc(v);
608
610
611 return v;
612}
613
623{
624 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
625 int safe_y = Clamp(y, 0, Map::MaxY() * TILE_SIZE);
626 return CreateEffectVehicle(x, y, GetSlopePixelZ(safe_x, safe_y) + z, type);
627}
628
639{
640 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
641}
642
644{
645 return _effect_procs[this->subtype].tick_proc(this);
646}
647
649{
650 this->bounds = {{}, {1, 1, 1}, {}};
651}
652
658{
659 return _effect_procs[this->subtype].transparency;
660}
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
Tile animation!
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static void ElectricSparkInit(EffectVehicle *v)
Initialise the sparks of a train.
static void ExplosionLargeInit(EffectVehicle *v)
Initialise a large explosion.
static void BubbleInit(EffectVehicle *v)
Initialise the bubbles of the bubble generator industry.
static void DieselSmokeInit(EffectVehicle *v)
Initialise the smoke of a diesel engine.
static void ChimneySmokeInit(EffectVehicle *v)
Initialise the smoke of a chimney.
static bool BulldozerTick(EffectVehicle *v)
Run a single tick of a bulldozer (road works).
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
static bool DieselSmokeTick(EffectVehicle *v)
Run a single tick of the smoke of a diesel engine.
EffectVehicle * CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
Create an effect vehicle at a particular location.
static bool IncrementSprite(EffectVehicle *v, SpriteID last)
Increment the sprite unless it has reached the end of the animation.
static bool BreakdownSmokeTick(EffectVehicle *v)
Run a single tick of the smoke of a broken down vehicle.
static const std::array< EffectProcs, EV_END > _effect_procs
Per-EffectVehicleType handling.
EffectVehicle * CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular location.
static void SteamSmokeInit(EffectVehicle *v)
Initialise the smoke of a steam engine.
static bool ElectricSparkTick(EffectVehicle *v)
Run a single tick of the sparks of a train.
static void BreakdownSmokeInit(EffectVehicle *v)
Initialise the smoke of a broken down vehicle.
static bool SteamSmokeTick(EffectVehicle *v)
Run a single tick of the smoke of a steam engine.
static void ExplosionSmallInit(EffectVehicle *v)
Initialise a small explosion.
static void BulldozerInit(EffectVehicle *v)
Initialise the bulldozer (road works).
static bool ChimneySmokeTick(EffectVehicle *v)
Run a single tick of the smoke of a chimney.
static bool SmokeTick(EffectVehicle *v)
Run a single tick of some smoke.
static bool BubbleTick(EffectVehicle *v)
Run a single tick of bubbles of the bubble generator industry.
static void SmokeInit(EffectVehicle *v)
Initialise some smoke.
static bool ExplosionSmallTick(EffectVehicle *v)
Run a single tick of a small explosion.
static bool ExplosionLargeTick(EffectVehicle *v)
Run a single tick of a large explosion.
Base class for all effect vehicles.
Functions related to effect vehicles.
EffectVehicleType
Effect vehicle types.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
Accessors to map for industries.
IndustryGfx GetIndustryGfx(Tile t)
Get the industry graphics ID for the given industry tile.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Functions related to OTTD's landscape.
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:407
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Pseudo random number generator.
bool Chance16I(const uint32_t a, const uint32_t b, const uint32_t r)
Checks if a given randomize-number is below a given probability.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
#define MK(a, b)
Macro for ordinary entry of LegendAndColour.
Functions related to sound.
@ SND_31_BUBBLE_GENERATOR_SUCCESS
49 == 0x31 Industry animation: bubble generator (2b): bubble slurped
Definition sound_type.h:97
@ SND_2F_BUBBLE_GENERATOR_FAIL
47 == 0x2F Industry animation: bubble generator (2a): bubble pop
Definition sound_type.h:95
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
A coordinate with two dimensions.
bool(EffectVehicle *v) TickProc
Run the actions/perform the behaviour of an effect vehicle for a tick.
void(EffectVehicle *v) InitProc
Initialises effect vehicle for a specific type.
InitProc * init_proc
Function to initialise an effect vehicle after construction.
TickProc * tick_proc
Functions for controlling effect vehicles at each tick.
TransparencyOption transparency
Transparency option affecting the effect.
A special vehicle is one of the following:
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
TransparencyOption GetTransparencyOption() const
Determines the transparency option affecting the effect.
uint8_t animation_substate
Sub state to time the change of the graphics/behaviour.
bool Tick() override
Calls the tick handler of the vehicle.
uint16_t animation_state
State primarily used to change the graphics/behaviour.
static uint MaxY()
Gets the maximum Y coordinate within the map, including TileType::Void.
Definition map_func.h:298
static uint MaxX()
Gets the maximum X coordinate within the map, including TileType::Void.
Definition map_func.h:289
VehicleSpriteSeq sprite_seq
Vehicle appearance.
static bool CanAllocateItem(size_t n=1)
static T * Create(Targs &&... args)
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
Vehicle data structure.
int32_t z_pos
z coordinate.
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
VehStates vehstatus
Status.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
SpriteBounds bounds
Bounding box of vehicle.
uint8_t spritenum
currently displayed sprite index 0xfd == custom sprite, 0xfe == custom second head sprite 0xff == res...
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
void UpdatePositionAndViewport()
Update the position of the vehicle, and update the viewport.
Definition vehicle.cpp:1782
TileIndex tile
Current tile index.
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ Industry
Part of an industry.
Definition tile_type.h:57
TransparencyOption
Transparency option bits: which position in _transparency_opt stands for which transparency.
@ TO_INVALID
Invalid transparency option.
@ TO_INDUSTRIES
industries
@ Unclickable
Vehicle is not clickable by the user (shadow vehicles).
Functions related to vehicles.