summaryrefslogtreecommitdiffhomepage
path: root/src/scenery.cpp
blob: a424351f16e99fed628ffc07823265179e608bd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "scenery.hpp"
#include "anim-atlas.hpp"
#include "chunk.hpp"
#include "compat/assert.hpp"
#include "world.hpp"
#include <algorithm>

namespace floormat {

scenery_proto::scenery_proto()
{
    type = entity_type::scenery;
}

scenery_proto& scenery_proto::operator=(const scenery_proto&) = default;
scenery_proto::scenery_proto(const scenery_proto&) = default;
scenery_proto::~scenery_proto() noexcept = default;
scenery_proto::operator bool() const { return atlas != nullptr; }

bool scenery::can_activate(size_t) const
{
    return atlas && interactive;
}

bool scenery::update(size_t, float dt)
{
    auto& s = *this;
    if (!s.active)
        return false;

    switch (s.sc_type)
    {
    default:
    case scenery_type::none:
    case scenery_type::generic:
        return false;
    case scenery_type::door:
        fm_assert(atlas);
        auto& anim = *atlas;
        const auto hz = uint8_t(atlas->info().fps);
        const auto nframes = (int)anim.info().nframes;
        fm_debug_assert(anim.info().fps > 0 && anim.info().fps <= 0xff);

        auto delta_ = int(s.delta) + int(65535u * dt);
        delta_ = std::min(65535, delta_);
        const auto frame_time = int(1.f/hz * 65535);
        const auto n = (uint8_t)std::clamp(delta_ / frame_time, 0, 255);
        s.delta = (uint16_t)std::clamp(delta_ - frame_time*n, 0, 65535);
        fm_debug_assert(s.delta >= 0);
        const int8_t dir = s.closing ? 1 : -1;
        const int fr = s.frame + dir*n;
        s.active = fr > 0 && fr < nframes-1;
        pass_mode p;
        if (fr <= 0)
            p = pass_mode::pass;
        else if (fr >= nframes-1)
            p = pass_mode::blocked;
        else
            p = pass_mode::see_through;
        set_bbox(offset, bbox_offset, bbox_size, p);
        s.frame = (uint16_t)std::clamp(fr, 0, nframes-1);
        if (!s.active)
            s.delta = s.closing = 0;
        return true;
    }
}

bool scenery::activate(size_t)
{
    auto& s = *this;
    if (s.active)
        return false;

    switch (s.sc_type)
    {
    default:
    case scenery_type::none:
    case scenery_type::generic:
        break;
    case scenery_type::door:
        fm_assert(s.frame == 0 || s.frame == atlas->info().nframes-1);
        s.closing = s.frame == 0;
        s.frame += s.closing ? 1 : -1;
        s.active = true;
        return true;
    }
    return false;
}

bool scenery_proto::operator==(const entity_proto& e0) const
{
    if (type != e0.type)
        return false;

    if (!entity_proto::operator==(e0))
        return false;

    const auto& s0 = static_cast<const scenery_proto&>(e0);
    return sc_type == s0.sc_type && active == s0.active &&
           closing == s0.closing && interactive == s0.interactive;
}

scenery::operator scenery_proto() const
{
    scenery_proto ret;
    static_cast<entity_proto&>(ret) = entity::operator entity_proto();
    ret.sc_type = sc_type;
    ret.active = active;
    ret.closing = closing;
    ret.interactive = interactive;
    return ret;
}

scenery::scenery(object_id id, struct chunk& c, entity_type type_, const scenery_proto& proto) :
    entity{id, c, type_, proto}, sc_type{proto.sc_type}, active{proto.active},
    closing{proto.closing}, interactive{proto.interactive}
{
    fm_assert(type == proto.type);
}

} // namespace floormat