mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-06 14:27:16 +00:00
Feature: Multiple rotating views on title screen
This commit is contained in:
parent
710b758b81
commit
6bd3106681
@ -11,12 +11,14 @@
|
||||
#include "error.h"
|
||||
#include "gui.h"
|
||||
#include "window_gui.h"
|
||||
#include "window_func.h"
|
||||
#include "textbuf_gui.h"
|
||||
#include "network/network.h"
|
||||
#include "genworld.h"
|
||||
#include "network/network_gui.h"
|
||||
#include "network/network_content.h"
|
||||
#include "landscape_type.h"
|
||||
#include "landscape.h"
|
||||
#include "strings_func.h"
|
||||
#include "fios.h"
|
||||
#include "ai/ai_gui.hpp"
|
||||
@ -25,6 +27,10 @@
|
||||
#include "language.h"
|
||||
#include "rev.h"
|
||||
#include "highscore.h"
|
||||
#include "signs_base.h"
|
||||
#include "viewport_func.h"
|
||||
#include "vehicle_base.h"
|
||||
#include <regex>
|
||||
|
||||
#include "widgets/intro_widget.h"
|
||||
|
||||
@ -33,13 +39,203 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
/**
|
||||
* A viewport command for the main menu background (intro game).
|
||||
*/
|
||||
struct IntroGameViewportCommand {
|
||||
/** Horizontal alignment value. */
|
||||
enum AlignmentH : byte {
|
||||
LEFT,
|
||||
CENTRE,
|
||||
RIGHT,
|
||||
};
|
||||
/** Vertical alignment value. */
|
||||
enum AlignmentV : byte {
|
||||
TOP,
|
||||
MIDDLE,
|
||||
BOTTOM,
|
||||
};
|
||||
|
||||
int command_index = 0; ///< Sequence number of the command (order they are performed in).
|
||||
Point position{ 0, 0 }; ///< Calculated world coordinate to position viewport top-left at.
|
||||
VehicleID vehicle = INVALID_VEHICLE; ///< Vehicle to follow, or INVALID_VEHICLE if not following a vehicle.
|
||||
uint delay = 0; ///< Delay until next command.
|
||||
int zoom_adjust = 0; ///< Adjustment to zoom level from base zoom level.
|
||||
bool pan_to_next = false; ///< If true, do a smooth pan from this position to the next.
|
||||
AlignmentH align_h = CENTRE; ///< Horizontal alignment.
|
||||
AlignmentV align_v = MIDDLE; ///< Vertical alignment.
|
||||
|
||||
/**
|
||||
* Calculate effective position.
|
||||
* This will update the position field if a vehicle is followed.
|
||||
* @param vp Viewport to calculate position for.
|
||||
* @return Calculated position in the viewport.
|
||||
*/
|
||||
Point PositionForViewport(const Viewport *vp)
|
||||
{
|
||||
if (this->vehicle != INVALID_VEHICLE) {
|
||||
const Vehicle *v = Vehicle::Get(this->vehicle);
|
||||
this->position = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
|
||||
}
|
||||
|
||||
Point p;
|
||||
switch (this->align_h) {
|
||||
case LEFT: p.x = this->position.x; break;
|
||||
case CENTRE: p.x = this->position.x - vp->virtual_width / 2; break;
|
||||
case RIGHT: p.x = this->position.x - vp->virtual_width; break;
|
||||
}
|
||||
switch (this->align_v) {
|
||||
case TOP: p.y = this->position.y; break;
|
||||
case MIDDLE: p.y = this->position.y - vp->virtual_height / 2; break;
|
||||
case BOTTOM: p.y = this->position.y - vp->virtual_height; break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SelectGameWindow : public Window {
|
||||
/** Vector of viewport commands parsed. */
|
||||
std::vector<IntroGameViewportCommand> intro_viewport_commands;
|
||||
/** Index of currently active viewport command. */
|
||||
size_t cur_viewport_command_index;
|
||||
/** Time spent (milliseconds) on current viewport command. */
|
||||
uint cur_viewport_command_time;
|
||||
|
||||
/**
|
||||
* Find and parse all viewport command signs.
|
||||
* Fills the intro_viewport_commands vector and deletes parsed signs from the world.
|
||||
*/
|
||||
void ReadIntroGameViewportCommands()
|
||||
{
|
||||
intro_viewport_commands.clear();
|
||||
|
||||
/* Regular expression matching the commands: T, spaces, integer, spaces, flags, spaces, integer */
|
||||
const char *sign_langauge = "^T\\s*([0-9]+)\\s*([-+A-Z0-9]+)\\s*([0-9]+)";
|
||||
std::regex re(sign_langauge, std::regex_constants::icase);
|
||||
|
||||
/* List of signs successfully parsed to delete afterwards. */
|
||||
std::vector<SignID> signs_to_delete;
|
||||
|
||||
for (const Sign *sign : Sign::Iterate()) {
|
||||
std::smatch match;
|
||||
if (std::regex_search(sign->name, match, re)) {
|
||||
IntroGameViewportCommand vc;
|
||||
/* Sequence index from the first matching group. */
|
||||
vc.command_index = std::stoi(match[1].str());
|
||||
/* Sign coordinates for positioning. */
|
||||
vc.position = RemapCoords(sign->x, sign->y, sign->z);
|
||||
/* Delay from the third matching group. */
|
||||
vc.delay = std::stoi(match[3].str()) * 1000; // milliseconds
|
||||
|
||||
/* Parse flags from second matching group. */
|
||||
enum IdType {
|
||||
ID_NONE, ID_VEHICLE
|
||||
} id_type = ID_NONE;
|
||||
for (char c : match[2].str()) {
|
||||
if (isdigit(c)) {
|
||||
if (id_type == ID_VEHICLE) {
|
||||
vc.vehicle = vc.vehicle * 10 + (c - '0');
|
||||
}
|
||||
} else {
|
||||
id_type = ID_NONE;
|
||||
switch (toupper(c)) {
|
||||
case '-': vc.zoom_adjust = +1; break;
|
||||
case '+': vc.zoom_adjust = -1; break;
|
||||
case 'T': vc.align_v = IntroGameViewportCommand::TOP; break;
|
||||
case 'M': vc.align_v = IntroGameViewportCommand::MIDDLE; break;
|
||||
case 'B': vc.align_v = IntroGameViewportCommand::BOTTOM; break;
|
||||
case 'L': vc.align_h = IntroGameViewportCommand::LEFT; break;
|
||||
case 'C': vc.align_h = IntroGameViewportCommand::CENTRE; break;
|
||||
case 'R': vc.align_h = IntroGameViewportCommand::RIGHT; break;
|
||||
case 'P': vc.pan_to_next = true; break;
|
||||
case 'V': id_type = ID_VEHICLE; vc.vehicle = 0; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Successfully parsed, store. */
|
||||
intro_viewport_commands.push_back(vc);
|
||||
signs_to_delete.push_back(sign->index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort the commands by sequence index. */
|
||||
std::sort(intro_viewport_commands.begin(), intro_viewport_commands.end(), [](const IntroGameViewportCommand &a, const IntroGameViewportCommand &b) { return a.command_index < b.command_index; });
|
||||
|
||||
/* Delete all the consumed signs, from last ID to first ID. */
|
||||
std::sort(signs_to_delete.begin(), signs_to_delete.end(), [](SignID a, SignID b) { return a > b; });
|
||||
for (SignID sign_id : signs_to_delete) {
|
||||
delete Sign::Get(sign_id);
|
||||
}
|
||||
}
|
||||
|
||||
SelectGameWindow(WindowDesc *desc) : Window(desc)
|
||||
{
|
||||
this->CreateNestedTree();
|
||||
this->FinishInitNested(0);
|
||||
this->OnInvalidateData();
|
||||
|
||||
this->ReadIntroGameViewportCommands();
|
||||
|
||||
this->cur_viewport_command_index = (size_t)-1;
|
||||
this->cur_viewport_command_time = 0;
|
||||
}
|
||||
|
||||
void OnRealtimeTick(uint delta_ms) override
|
||||
{
|
||||
/* Move the main game viewport according to intro viewport commands. */
|
||||
|
||||
if (intro_viewport_commands.empty()) return;
|
||||
|
||||
/* Determine whether to move to the next command or stay at current. */
|
||||
bool changed_command = false;
|
||||
if (this->cur_viewport_command_index >= intro_viewport_commands.size()) {
|
||||
/* Reached last, rotate back to start of the list. */
|
||||
this->cur_viewport_command_index = 0;
|
||||
changed_command = true;
|
||||
} else {
|
||||
/* Check if current command has elapsed and switch to next. */
|
||||
this->cur_viewport_command_time += delta_ms;
|
||||
if (this->cur_viewport_command_time >= intro_viewport_commands[this->cur_viewport_command_index].delay) {
|
||||
this->cur_viewport_command_index = (this->cur_viewport_command_index + 1) % intro_viewport_commands.size();
|
||||
this->cur_viewport_command_time = 0;
|
||||
changed_command = true;
|
||||
}
|
||||
}
|
||||
|
||||
IntroGameViewportCommand &vc = intro_viewport_commands[this->cur_viewport_command_index];
|
||||
Window *mw = FindWindowByClass(WC_MAIN_WINDOW);
|
||||
Viewport *vp = mw->viewport;
|
||||
|
||||
/* Early exit if the current command hasn't elapsed and isn't animated. */
|
||||
if (!changed_command && !vc.pan_to_next && vc.vehicle == INVALID_VEHICLE) return;
|
||||
|
||||
/* Reset the zoom level. */
|
||||
if (changed_command) FixTitleGameZoom(vc.zoom_adjust);
|
||||
|
||||
/* Calculate current command position (updates followed vehicle coordinates). */
|
||||
Point pos = vc.PositionForViewport(vp);
|
||||
|
||||
/* Calculate panning (linear interpolation between current and next command position). */
|
||||
if (vc.pan_to_next) {
|
||||
size_t next_command_index = (this->cur_viewport_command_index + 1) % intro_viewport_commands.size();
|
||||
IntroGameViewportCommand &nvc = intro_viewport_commands[next_command_index];
|
||||
Point pos2 = nvc.PositionForViewport(vp);
|
||||
const double t = this->cur_viewport_command_time / (double)vc.delay;
|
||||
pos.x = pos.x + (int)(t * (pos2.x - pos.x));
|
||||
pos.y = pos.y + (int)(t * (pos2.y - pos.y));
|
||||
}
|
||||
|
||||
/* Update the viewport position. */
|
||||
mw->viewport->dest_scrollpos_x = mw->viewport->scrollpos_x = pos.x;
|
||||
mw->viewport->dest_scrollpos_y = mw->viewport->scrollpos_y = pos.y;
|
||||
UpdateViewportPosition(mw);
|
||||
mw->SetDirty(); // Required during panning, otherwise logo graphics disappears
|
||||
|
||||
/* If there is only one command, we just executed it and don't need to do any more */
|
||||
if (intro_viewport_commands.size() == 1 && vc.vehicle == INVALID_VEHICLE) intro_viewport_commands.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,12 +152,24 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w)
|
||||
}
|
||||
}
|
||||
|
||||
void FixTitleGameZoom()
|
||||
void FixTitleGameZoom(int zoom_adjust)
|
||||
{
|
||||
if (_game_mode != GM_MENU) return;
|
||||
|
||||
Viewport *vp = FindWindowByClass(WC_MAIN_WINDOW)->viewport;
|
||||
|
||||
/* Adjust the zoom in/out.
|
||||
* Can't simply add, since operator+ is not defined on the ZoomLevel type. */
|
||||
vp->zoom = _gui_zoom;
|
||||
while (zoom_adjust < 0 && vp->zoom != ZOOM_LVL_MIN) {
|
||||
vp->zoom--;
|
||||
zoom_adjust++;
|
||||
}
|
||||
while (zoom_adjust > 0 && vp->zoom != ZOOM_LVL_MAX) {
|
||||
vp->zoom++;
|
||||
zoom_adjust--;
|
||||
}
|
||||
|
||||
vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
|
||||
vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
||||
bool DoZoomInOutWindow(ZoomStateChange how, Window *w);
|
||||
void ZoomInOrOutToCursorWindow(bool in, Window * w);
|
||||
Point GetTileZoomCenterWindow(bool in, Window * w);
|
||||
void FixTitleGameZoom();
|
||||
void FixTitleGameZoom(int zoom_adjust = 0);
|
||||
void HandleZoomMessage(Window *w, const Viewport *vp, byte widget_zoom_in, byte widget_zoom_out);
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user