Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/game/features.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ core.features = {
get_modnames_load_order = true,
set_camera_resettable = true,
hud_hideable_field = true,
hud_animate = true,
compress_raw_deflate = true,
get_all_craft_recipes_fuel = true,
}
Expand Down
50 changes: 50 additions & 0 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9329,6 +9329,14 @@ You **must not** mix names and track numbers to refer to the same animation.
element.
* `stat` supports the same keys as in the hud definition table except for
`"type"` (or the deprecated `"hud_elem_type"`).
* `hud_animate(id, animation definition)`: animate properties of a HUD element by keyframes.
* `id` is the HUD element ID returned by `hud_add`.
* See [HUD animation definition](#hud-animation-definition) for the format.
* Animations are applied client-side, server sends the definition
to the client which applies them locally.
* Multiple properties can be animated simultaneously in one anim def, but only one anim def can exist per element at a time. Calling this function on an element that's already animating will replace the existing animation set.
* Pass an empty table `{}` to cancel any animation on an element
* While an animation is active, it overwrites the animated properties each frame. Any `hud_change` calls on those properties will have no visible effect until the animation finishes or is cancelled.
* `hud_get(id)`: gets the HUD element definition structure of the specified ID
* `hud_get_all()`:
* Returns a table in the form `{ [id] = HUD definition, [id] = ... }`.
Expand Down Expand Up @@ -12068,6 +12076,48 @@ Used by `ObjectRef:hud_add`. Returned by `ObjectRef:hud_get`.
style = 0, -- integer [u32]

hideable = true, -- bool

opacity = 1,-- float
-- value between 0 and 1, where 0 is invisible, and 1 is fully opaque. Default 1
}
```

HUD animation definition
-------------------------

Used by `ObjectRef:hud_animate`.

Each field in the table corresponds to an animatable HUD element property.
Multiple properties can be animated at the same time by listing them in the same animation definition table. Any property not listed will be untouched by the animation.

```lua
{
pos_x = { -- pos_x is used as example, each available property has the same definition table
keyframes = { {0.1, 0.5}, {0.9, 0.5}, {0.1} },
-- List of {value, duration} pairs.
-- Duration is the time in seconds to transition FROM this keyframe TO the next one.
-- Last keyframe's duration is the time to transition back to the
-- first keyframe when looping. If not looping, or upon finishing finite loops,
-- the animation holds at the last keyframe's value

easing = "linear",
-- method of Interpolation to use between keyframes.
-- "linear" (default), "easein", "easeout", "easeinout"
-- these use quadratic easing functions under the hood

loop = 0,
-- Number of times to repeat the animation.
-- 0 = play once (default), -1 = loop forever, N = repeat N times
},

-- Animatable properties - multiple can be specified in one table to animate them simultaneously
-- Each property is animated independently, with its own elapsed time and loop count,
-- So you can specify different properties with their different durations and loop counts, and they will simply drift out of sync.
-- pos_x, pos_y : HUD element position
-- scale_x, scale_y : HUD element scale, a multiplier
-- offset_x, offset_y : HUD element offset, in pixels
-- size_x, size_y : HUD element size, in pixels
-- opacity : HUD element opacity, clamped 0 to 1
}
```

Expand Down
1 change: 1 addition & 0 deletions src/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void handleCommand_HudChange(NetworkPacket* pkt);
void handleCommand_HudSetFlags(NetworkPacket* pkt);
void handleCommand_HudSetParam(NetworkPacket* pkt);
void handleCommand_HudAnimate(NetworkPacket* pkt);
void handleCommand_HudSetSky(NetworkPacket* pkt);
void handleCommand_HudSetSun(NetworkPacket* pkt);
void handleCommand_HudSetMoon(NetworkPacket* pkt);
Expand Down
9 changes: 9 additions & 0 deletions src/client/clientevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum ClientEventType : u8
CE_OVERRIDE_DAY_NIGHT_RATIO,
CE_CLOUD_PARAMS,
CE_UPDATE_CAMERA,
CE_HUDANIMATE,
CLIENTEVENT_MAX,
};

Expand All @@ -53,6 +54,7 @@ struct ClientEventHudAdd
v2f size;
s16 z_index;
bool hideable;
f32 opacity;
};

struct ClientEventHudChange
Expand All @@ -66,6 +68,12 @@ struct ClientEventHudChange
v2s32 v2s32data;
};

struct ClientEventHudAnimate
{
u32 server_id;
HudElementAnimations animations;
};

struct ClientEvent
{
// TODO: should get rid of this ctor
Expand Down Expand Up @@ -108,6 +116,7 @@ struct ClientEvent
u32 id;
} hudrm;
ClientEventHudChange *hudchange;
ClientEventHudAnimate *hudanimate;
SkyboxParams *set_sky;
struct
{
Expand Down
53 changes: 53 additions & 0 deletions src/client/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
#include "client/sound/sound_openal.h"
#endif

const static float PENDING_HUD_ANIM_TIMEOUT_SEC = 10.0f;

typedef s32 SamplerLayer_t;


Expand Down Expand Up @@ -2184,6 +2186,7 @@ const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
{&Game::handleClientEvent_OverrideDayNightRatio},
{&Game::handleClientEvent_CloudParams},
{&Game::handleClientEvent_UpdateCamera},
{&Game::handleClientEvent_HudAnimate},
};

void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam)
Expand Down Expand Up @@ -2300,8 +2303,20 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
e->text2 = event->hudadd->text2;
e->style = event->hudadd->style;
e->hideable = event->hudadd->hideable;
e->opacity = event->hudadd->opacity;
m_hud_server_to_client[server_id] = player->hud.add(std::move(e));

// check for pending aims that may have arrived out of order
auto pending = m_pending_hud_animations.find(server_id);
if (pending != m_pending_hud_animations.end()) {
HudElement *elem = player->hud.get(m_hud_server_to_client[server_id]);
if (pending->second.animations.properties.empty())
elem->animations.reset();
else
elem->animations = std::move(pending->second.animations);
m_pending_hud_animations.erase(pending);
}

delete event->hudadd;
}

Expand All @@ -2313,6 +2328,7 @@ void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *ca
if (i != m_hud_server_to_client.end()) {
player->hud.remove(i->second);
m_hud_server_to_client.erase(i);
m_pending_hud_animations.erase(event->hudrm.id);
}

}
Expand Down Expand Up @@ -2369,6 +2385,8 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca

CASE_SET(HUD_STAT_HIDEABLE, hideable, data);

CASE_SET(HUD_STAT_OPACITY, opacity, v2fdata.X);

case HudElementStat_END:
break;
}
Expand All @@ -2378,6 +2396,27 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca
delete event->hudchange;
}

void Game::handleClientEvent_HudAnimate(ClientEvent *event, CameraOrientation *cam)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();

auto i = m_hud_server_to_client.find(event->hudanimate->server_id);
if (i != m_hud_server_to_client.end()) {
HudElement *e = player->hud.get(i->second);
if (e) {
if (event->hudanimate->animations.properties.empty())
e->animations.reset();
else
e->animations = std::move(event->hudanimate->animations);
}
} else {
m_pending_hud_animations[event->hudanimate->server_id] =
{std::move(event->hudanimate->animations), 0.0f};
}

delete event->hudanimate;
}

void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
{
sky->setVisible(false);
Expand Down Expand Up @@ -3467,6 +3506,20 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
*/
client->getParticleManager()->step(dtime);

/*
Update rendering engine
*/
m_rendering_engine->step(dtime);

// cleanup any too-old pending anims to avoid keeping them forever
for (auto it = m_pending_hud_animations.begin(); it != m_pending_hud_animations.end();) {
it->second.age += dtime;
if (it->second.age > PENDING_HUD_ANIM_TIMEOUT_SEC)
it = m_pending_hud_animations.erase(it);
else
++it;
}

/*
Damage camera tilt
*/
Expand Down
7 changes: 7 additions & 0 deletions src/client/game_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class Game {
CameraOrientation *cam);
void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
void handleClientEvent_UpdateCamera(ClientEvent *event, CameraOrientation *cam);
void handleClientEvent_HudAnimate(ClientEvent *event, CameraOrientation *cam);

void updateChat(f32 dtime);

Expand Down Expand Up @@ -339,6 +340,12 @@ class Game {

// Map server hud ids to client hud ids
std::unordered_map<u32, u32> m_hud_server_to_client;
// list of pending anims that may have arrived out of order
struct PendingHudAnimation {
HudElementAnimations animations;
f32 age = 0.0f;
};
std::unordered_map<u32, PendingHudAnimation> m_pending_hud_animations;

GameRunData runData;
Flags m_flags;
Expand Down
15 changes: 10 additions & 5 deletions src/client/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset, bool only_unhidable)
u8 alpha = (num >> 24) & 0xFF;
if (alpha == 0)
alpha = 0xFF; // Backwards compatibility
alpha = rangelim((u32)(alpha * e->opacity), 0, 255);

video::SColor color = video::SColor(alpha,
(num >> 16) & 0xFF,
Expand Down Expand Up @@ -440,7 +441,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset, bool only_unhidable)
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
e->number, e->item, offs, e->size);
e->number, e->item, offs, e->size, e->opacity);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
Expand All @@ -454,7 +455,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset, bool only_unhidable)
break;

pos += v2s32(e->offset.X, e->offset.Y);
video::SColor color(255, (e->number >> 16) & 0xFF,
u8 wp_alpha = rangelim((u32)(255 * e->opacity), 0, 255);
video::SColor color(wp_alpha, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
Expand Down Expand Up @@ -490,7 +492,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset, bool only_unhidable)
if (!texture)
continue;

const video::SColor color(255, 255, 255, 255);
u8 img_alpha = rangelim((u32)(e->opacity * 255.0f), 0, 255);
const video::SColor color(img_alpha, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
core::dimension2di imgsize(texture->getOriginalSize());
v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
Expand Down Expand Up @@ -659,9 +662,11 @@ void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,

void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
const std::string &texture, const std::string &bgtexture,
s32 count, s32 maxcount, v2s32 offset, v2f size)
s32 count, s32 maxcount, v2s32 offset, v2f size,
f32 opacity)
{
const video::SColor color(255, 255, 255, 255);
u8 a = rangelim((u32)(255 * opacity), 0, 255);
const video::SColor color(a, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};

video::ITexture *stat_texture = tsrc->getTexture(texture);
Expand Down
3 changes: 2 additions & 1 deletion src/client/hud.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ class Hud
bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos);
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
const std::string &texture, const std::string& bgtexture,
s32 count, s32 maxcount, v2s32 offset, v2f size = v2f());
s32 count, s32 maxcount, v2s32 offset, v2f size = v2f(),
f32 opacity = 1.0f);

void drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment,
s32 inv_offset, InventoryList *mainlist, u16 selectitem,
Expand Down
Loading
Loading