Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
OpeningMovies.cpp
Go to the documentation of this file.
1#include "OpeningMovies.hpp"
2
5
6#include <algorithm>
7#include <array>
8#include <utility>
9
11{
12 namespace
13 {
14 constexpr auto TrademarkId = data::packDataId(
16 constexpr std::uint16_t MoviePriority = 1100;
17 constexpr std::array MovieNames{"HLogo", "ALogo", "MIntro"};
18
19 std::optional<std::string> resolveMovie(const data::ResourcePaths& paths,
20 std::string_view base, bool use3DBoard)
21 {
22 const auto find = [&](std::string_view extension) -> std::optional<std::string> {
23 const std::string name = std::string(base) + std::string(extension);
24 if (paths.resolve(name)) return name;
25 const std::string inAviDirectory = "AVI/" + name;
26 if (paths.resolve(inAviDirectory)) return inAviDirectory;
27 return std::nullopt;
28 };
29 if (use3DBoard)
30 if (auto bink = find(".bik")) return bink;
31 return find(".avi");
32 }
33 }
34
35 void Controller::stopOwnedNodes(engine::SequencePlayback& playback)
36 {
37 if (trademark_ && playback.runtime().inspect(trademark_))
38 (void)playback.runtime().stop(trademark_);
39 if (movie_ && playback.runtime().inspect(movie_))
40 (void)playback.runtime().stop(movie_);
41 if (movieDataId_ != data::EmptyDataId)
42 (void)playback.freeRuntimeSequence(movieDataId_);
43 trademark_ = movie_ = 0;
44 movieDataId_ = data::EmptyDataId;
45 }
46
48 {
49 stopOwnedNodes(playback);
50 phase_ = Phase::Idle;
51 nextMovie_ = 0;
52 selectionRequested_ = false;
53 movieFile_.clear();
54 error_.clear();
55 }
56
57 void Controller::finish(engine::SequencePlayback& playback)
58 {
59 stopOwnedNodes(playback);
60 phase_ = Phase::Finished;
61 selectionRequested_ = true;
62 movieFile_.clear();
63 }
64
66 {
67 if (!active()) return;
68 error_ = std::move(error);
69 finish(playback);
70 }
71
72 void Controller::begin(std::uint64_t tick, bool startedByLobby, bool use3DBoard,
74 {
76 use3DBoard_ = use3DBoard;
78 {
79 finish(playback);
80 return;
81 }
82 phase_ = Phase::Trademark;
83 trademarkStarted_ = tick;
84 auto program = sequence::SequenceProgram::load(playback.resources(), TrademarkId);
85 if (!program)
86 {
87 decoderFailed("opening trademark: " + program.error().detail, playback);
88 return;
89 }
90 auto node = playback.runtime().start(*program, 0);
91 if (!node)
92 {
93 decoderFailed("opening trademark: " + node.error().detail, playback);
94 return;
95 }
96 trademark_ = *node;
97 }
98
99 void Controller::startNextMovie(engine::SequencePlayback& playback)
100 {
101 if (nextMovie_ == MovieNames.size())
102 {
103 finish(playback);
104 return;
105 }
106 const auto resources = playback.resources();
107 auto file = resources
108 ? resolveMovie(resources->paths(), MovieNames[nextMovie_], use3DBoard_)
109 : std::nullopt;
110 if (!file)
111 {
112 decoderFailed(std::string("opening movie cannot be found: ") +
113 MovieNames[nextMovie_], playback);
114 return;
115 }
116 const bool bink = file->ends_with(".bik");
117 // Retail's AVI path uses the central 400x300 rectangle at 32-bit depth.
118 // Bink requests double intrinsic size, centered and clamped within the
119 // 800x600 viewport. The bridge computes its real destination from the
120 // decoded metadata before publishing the first frame.
121 data::Sequence2DBoundingBoxAttribute bounds{};
122 bounds.left = bink ? 0 : 200;
123 bounds.top = bink ? 0 : 150;
124 bounds.right = bink ? 800 : 600;
125 bounds.bottom = bink ? 600 : 450;
126 data::SequenceVideoData options{};
127 options.drawSolid = true;
128 options.alphaLevel = 255;
129 options.enableVideo = options.enableAudio = true;
130 options.drawDirectlyToScreen = true;
131 auto runtimeId = playback.createVideoObject(
132 *file, options, bounds, bink);
133 if (!runtimeId)
134 {
136 return;
137 }
138 const auto collecting = playback.collectCommands();
139 if (!collecting)
140 {
141 (void)playback.freeRuntimeSequence(*runtimeId);
143 return;
144 }
145
146 const auto started = playback.start(*runtimeId, MoviePriority);
147 const auto ending = started
148 ? playback.setEndingAction(*runtimeId, MoviePriority, 1)
149 : std::expected<void, std::string>{
150 std::unexpected(started.error())};
151 const auto executed = ending
152 ? playback.executeCommands()
153 : std::expected<int, std::string>{
154 std::unexpected(ending.error())};
155
156 if (!executed)
157 {
158 while (playback.commands().nestingLevel() > 0)
159 (void)playback.executeCommands();
160 (void)playback.freeRuntimeSequence(*runtimeId);
162 return;
163 }
164
165 const auto nodes = playback.runtime().matching(
166 *runtimeId, MoviePriority, false);
167 if (nodes.empty())
168 {
169 (void)playback.freeRuntimeSequence(*runtimeId);
171 "opening movie start command produced no runtime node",
172 playback);
173 return;
174 }
175
176 movieDataId_ = *runtimeId;
177 movie_ = nodes.front();
178 movieFile_ = std::move(*file);
179 ++nextMovie_;
180 }
181
182 void Controller::update(std::uint64_t tick,
183 std::span<const video::SequencePlaybackState> videoStates,
185 {
186 if (phase_ == Phase::Trademark)
187 {
188 // Strict inequality matches screenStartTime + 60 * 9 < tick.
189 if (tick >= trademarkStarted_ && tick - trademarkStarted_ > 540)
190 skip(playback);
191 return;
192 }
193 if (phase_ != Phase::Movies) return;
194 if (movie_)
195 {
196 const auto state = std::find_if(videoStates.begin(), videoStates.end(),
197 [&](const auto& value) { return value.node == movie_; });
198 const bool ended = state != videoStates.end() && state->status.ended;
199 const bool live = playback.runtime().inspect(movie_).has_value();
200 // A decoded Stop EOF can already have removed its runtime node in
201 // the preceding update. Its published EOF is still authoritative.
202 if (!ended && !live)
203 {
204 decoderFailed("opening movie sequence disappeared before decoded EOF", playback);
205 return;
206 }
207 if (!ended) return;
208 if (live) (void)playback.runtime().stop(movie_);
209 if (movieDataId_ != data::EmptyDataId)
210 (void)playback.freeRuntimeSequence(movieDataId_);
211 movie_ = 0;
212 movieDataId_ = data::EmptyDataId;
213 }
214 startNextMovie(playback);
215 }
216
218 {
219 if (phase_ == Phase::Trademark)
220 {
221 stopOwnedNodes(playback);
222 phase_ = Phase::Movies;
223 }
224 else if (phase_ == Phase::Movies) finish(playback);
225 }
226
228 { return std::exchange(selectionRequested_, false); }
229
231 { return std::exchange(error_, {}); }
232}
const char * name
unsigned state
unsigned index
std::string error
std::uint16_t value
Definition Timers.cpp:16
void skip(engine::SequencePlayback &playback)
void begin(std::uint64_t tick, bool startedByLobby, bool use3DBoard, engine::SequencePlayback &playback)
void decoderFailed(std::string error, engine::SequencePlayback &playback)
void update(std::uint64_t tick, std::span< const video::SequencePlaybackState > videoStates, engine::SequencePlayback &playback)
void reset(engine::SequencePlayback &playback)
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > load(const data::DataSource &source, data::DataId id, std::size_t offset=0, DescriptionLimits limits={})
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39