You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

264 lines
6.5 KiB

#include <algorithm>
#include <memory>
#include <vector>
#include "controls/menu_controls.h"
#include "all.h"
#include "display.h"
#include "DiabloUI/diabloui.h"
#include "DiabloUI/credits_lines.h"
#include "DiabloUI/support_lines.h"
#include "DiabloUI/art.h"
#include "DiabloUI/art_draw.h"
#include "DiabloUI/fonts.h"
namespace dvl {
namespace {
const SDL_Rect VIEWPORT = { 0, 114, 640, 251 };
const int SHADOW_OFFSET_X = 2;
const int SHADOW_OFFSET_Y = 2;
const int LINE_H = 22;
char const *const *text;
std::size_t textLines;
// The maximum number of visible lines is the number of whole lines
// (VIEWPORT.h / LINE_H) rounded up, plus one extra line for when
// a line is leaving the screen while another one is entering.
#define MAX_VISIBLE_LINES ((VIEWPORT.h - 1) / LINE_H + 2)
struct CachedLine {
CachedLine()
{
m_index = 0;
m_surface = NULL;
palette_version = pal_surface_palette_version;
}
CachedLine(std::size_t index, SDL_Surface *surface)
{
m_index = index;
m_surface = surface;
palette_version = pal_surface_palette_version;
}
std::size_t m_index;
SDL_Surface *m_surface;
unsigned int palette_version;
};
SDL_Surface *RenderText(const char *text, SDL_Color color)
{
if (text[0] == '\0')
return NULL;
SDL_Surface *result = TTF_RenderUTF8_Solid(font, text, color);
if (result == NULL)
SDL_Log(TTF_GetError());
return result;
}
CachedLine PrepareLine(std::size_t index)
{
const char *contents = text[index];
while (contents[0] == '\t')
++contents;
const SDL_Color shadow_color = { 0, 0, 0, 0 };
SDL_Surface *text = RenderText(contents, shadow_color);
// Precompose shadow and text:
SDL_Surface *surface = NULL;
if (text != NULL) {
// Set up the target surface to have 3 colors: mask, text, and shadow.
surface = SDL_CreateRGBSurfaceWithFormat(0, text->w + SHADOW_OFFSET_X, text->h + SHADOW_OFFSET_Y, 8, SDL_PIXELFORMAT_INDEX8);
const SDL_Color mask_color = { 0, 255, 0, 0 }; // Any color different from both shadow and text
const SDL_Color &text_color = palette->colors[224];
SDL_Color colors[3] = { mask_color, text_color, shadow_color };
if (SDLC_SetSurfaceColors(surface, colors, 0, 3) <= -1)
SDL_Log(SDL_GetError());
SDLC_SetColorKey(surface, 0);
// Blit the shadow first:
SDL_Rect shadow_rect = { SHADOW_OFFSET_X, SHADOW_OFFSET_Y, 0, 0 };
if (SDL_BlitSurface(text, NULL, surface, &shadow_rect) <= -1)
ErrSdl();
// Change the text surface color and blit again:
SDL_Color text_colors[2] = { mask_color, text_color };
if (SDLC_SetSurfaceColors(text, text_colors, 0, 2) <= -1)
ErrSdl();
SDLC_SetColorKey(text, 0);
if (SDL_BlitSurface(text, NULL, surface, NULL) <= -1)
ErrSdl();
SDL_Surface *surface_ptr = surface;
ScaleSurfaceToOutput(&surface_ptr);
surface = surface_ptr;
}
SDL_FreeSurface(text);
return CachedLine(index, surface);
}
class CreditsRenderer {
public:
CreditsRenderer()
{
LoadTtfFont();
ticks_begin_ = SDL_GetTicks();
prev_offset_y_ = 0;
finished_ = false;
}
~CreditsRenderer()
{
ArtBackgroundWidescreen.Unload();
ArtBackground.Unload();
UnloadTtfFont();
for (size_t x = 0; x < lines_.size(); x++) {
if (lines_[x].m_surface)
SDL_FreeSurface(lines_[x].m_surface);
}
}
void Render();
bool Finished() const
{
return finished_;
}
private:
std::vector<CachedLine> lines_;
bool finished_;
Uint32 ticks_begin_;
int prev_offset_y_;
};
void CreditsRenderer::Render()
{
const int offset_y = -VIEWPORT.h + (SDL_GetTicks() - ticks_begin_) / 40;
if (offset_y == prev_offset_y_)
return;
prev_offset_y_ = offset_y;
SDL_FillRect(GetOutputSurface(), NULL, 0x000000);
DrawArt(PANEL_LEFT - 320, UI_OFFSET_Y, &ArtBackgroundWidescreen);
DrawArt(PANEL_LEFT, UI_OFFSET_Y, &ArtBackground);
if (font == NULL)
return;
const std::size_t lines_begin = std::max(offset_y / LINE_H, 0);
const std::size_t lines_end = std::min(lines_begin + MAX_VISIBLE_LINES, textLines);
if (lines_begin >= lines_end) {
if (lines_end == textLines)
finished_ = true;
return;
}
while (lines_end > lines_.size())
lines_.push_back(PrepareLine(lines_.size()));
SDL_Rect viewport = VIEWPORT;
viewport.x += PANEL_LEFT;
viewport.y += UI_OFFSET_Y;
ScaleOutputRect(&viewport);
SDL_SetClipRect(GetOutputSurface(), &viewport);
// We use unscaled coordinates for calculation throughout.
Sint16 dest_y = UI_OFFSET_Y + VIEWPORT.y - (offset_y - lines_begin * LINE_H);
for (std::size_t i = lines_begin; i < lines_end; ++i, dest_y += LINE_H) {
CachedLine &line = lines_[i];
if (line.m_surface == NULL)
continue;
// Still fading in: the cached line was drawn with a different fade level.
if (line.palette_version != pal_surface_palette_version) {
SDL_FreeSurface(line.m_surface);
line = PrepareLine(line.m_index);
}
Sint16 dest_x = PANEL_LEFT + VIEWPORT.x + 31;
int j = 0;
while (text[line.m_index][j++] == '\t')
dest_x += 40;
SDL_Rect dst_rect = { dest_x, dest_y, 0, 0 };
ScaleOutputRect(&dst_rect);
dst_rect.w = line.m_surface->w;
dst_rect.h = line.m_surface->h;
if (SDL_BlitSurface(line.m_surface, NULL, GetOutputSurface(), &dst_rect) < 0)
ErrSdl();
}
SDL_SetClipRect(GetOutputSurface(), NULL);
}
BOOL TextDialog()
{
CreditsRenderer credits_renderer;
bool endMenu = false;
SDL_Event event;
do {
credits_renderer.Render();
UiFadeIn();
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_MOUSEBUTTONDOWN:
endMenu = true;
break;
default:
switch (GetMenuAction(event)) {
case MenuAction_BACK:
case MenuAction_SELECT:
endMenu = true;
break;
default:
break;
}
}
UiHandleEvents(&event);
}
} while (!endMenu && !credits_renderer.Finished());
return true;
}
} // namespace
BOOL UiCreditsDialog()
{
text = CREDITS_LINES;
textLines = CREDITS_LINES_SIZE;
LoadArt("ui_art\\creditsw.pcx", &ArtBackgroundWidescreen);
LoadBackgroundArt("ui_art\\credits.pcx");
return TextDialog();
}
BOOL UiSupportDialog()
{
text = SUPPORT_LINES;
textLines = SUPPORT_LINES_SIZE;
if (gbIsHellfire) {
LoadArt("ui_art\\supportw.pcx", &ArtBackgroundWidescreen);
LoadBackgroundArt("ui_art\\support.pcx");
} else {
LoadArt("ui_art\\creditsw.pcx", &ArtBackgroundWidescreen);
LoadBackgroundArt("ui_art\\credits.pcx");
}
return TextDialog();
}
} // namespace dvl