336 lines
6.6 KiB
C++
336 lines
6.6 KiB
C++
#include "Engine.h"
|
|
#include "PowderToySDL.h"
|
|
#include "Window.h"
|
|
#include "common/platform/Platform.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "gui/dialogues/ConfirmPrompt.h"
|
|
#include <cmath>
|
|
#include <cstring>
|
|
|
|
using namespace ui;
|
|
|
|
Engine::Engine():
|
|
FpsLimit(60.0f),
|
|
drawingFrequencyLimit(0),
|
|
Scale(1),
|
|
Fullscreen(false),
|
|
FrameIndex(0),
|
|
altFullscreen(false),
|
|
resizable(false),
|
|
state_(NULL),
|
|
windowTargetPosition(0, 0),
|
|
break_(false),
|
|
FastQuit(1),
|
|
lastTick(0),
|
|
mouseb_(0),
|
|
mousex_(0),
|
|
mousey_(0),
|
|
mousexp_(0),
|
|
mouseyp_(0)
|
|
{
|
|
SetFps(FpsLimit); // populate dt with whatever that makes any sort of sense
|
|
}
|
|
|
|
Engine::~Engine()
|
|
{
|
|
delete state_;
|
|
//Dispose of any Windows.
|
|
while (!windows.empty())
|
|
{
|
|
delete windows.top();
|
|
windows.pop();
|
|
}
|
|
}
|
|
|
|
void Engine::Begin()
|
|
{
|
|
//engine is now ready
|
|
running_ = true;
|
|
}
|
|
|
|
void Engine::Break()
|
|
{
|
|
break_ = true;
|
|
}
|
|
|
|
void Engine::UnBreak()
|
|
{
|
|
break_ = false;
|
|
}
|
|
|
|
void Engine::Exit()
|
|
{
|
|
onClose();
|
|
running_ = false;
|
|
}
|
|
|
|
void Engine::ConfirmExit()
|
|
{
|
|
new ConfirmPrompt("You are about to quit", "Are you sure you want to exit the game?", { [] {
|
|
ui::Engine::Ref().Exit();
|
|
} });
|
|
}
|
|
|
|
void Engine::ShowWindow(Window * window)
|
|
{
|
|
if (state_)
|
|
ignoreEvents = true;
|
|
if(window->Position.X==-1)
|
|
{
|
|
window->Position.X = (g->Size().X - window->Size.X) / 2;
|
|
}
|
|
if(window->Position.Y==-1)
|
|
{
|
|
window->Position.Y = (g->Size().Y - window->Size.Y) / 2;
|
|
}
|
|
/*if(window->Position.Y > 0)
|
|
{
|
|
windowTargetPosition = window->Position;
|
|
window->Position = Point(windowTargetPosition.X, height_);
|
|
}*/
|
|
if(state_)
|
|
{
|
|
frozenGraphics.emplace(FrozenGraphics{0, std::make_unique<pixel []>(g->Size().X * g->Size().Y)});
|
|
std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozenGraphics.top().screen.get());
|
|
|
|
windows.push(state_);
|
|
mousePositions.push(ui::Point(mousex_, mousey_));
|
|
}
|
|
if(state_)
|
|
state_->DoBlur();
|
|
|
|
state_ = window;
|
|
|
|
}
|
|
|
|
int Engine::CloseWindow()
|
|
{
|
|
if(!windows.empty())
|
|
{
|
|
frozenGraphics.pop();
|
|
state_ = windows.top();
|
|
windows.pop();
|
|
|
|
if(state_)
|
|
state_->DoFocus();
|
|
|
|
ui::Point mouseState = mousePositions.top();
|
|
mousePositions.pop();
|
|
if(state_)
|
|
{
|
|
mousexp_ = mouseState.X;
|
|
mouseyp_ = mouseState.Y;
|
|
state_->DoMouseMove(mousex_, mousey_, mousex_ - mousexp_, mousey_ - mouseyp_);
|
|
mousexp_ = mousex_;
|
|
mouseyp_ = mousey_;
|
|
}
|
|
ignoreEvents = true;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
state_ = NULL;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*void Engine::SetState(State * state)
|
|
{
|
|
if(state_) //queue if currently in a state
|
|
statequeued_ = state;
|
|
else
|
|
{
|
|
state_ = state;
|
|
if(state_)
|
|
state_->DoInitialized();
|
|
}
|
|
}*/
|
|
|
|
|
|
void Engine::Tick()
|
|
{
|
|
if(state_ != NULL)
|
|
state_->DoTick(dt);
|
|
|
|
|
|
lastTick = Platform::GetTime();
|
|
|
|
ignoreEvents = false;
|
|
/*if(statequeued_ != NULL)
|
|
{
|
|
if(state_ != NULL)
|
|
{
|
|
state_->DoExit();
|
|
delete state_;
|
|
state_ = NULL;
|
|
}
|
|
state_ = statequeued_;
|
|
statequeued_ = NULL;
|
|
|
|
if(state_ != NULL)
|
|
state_->DoInitialized();
|
|
}*/
|
|
}
|
|
|
|
void Engine::Draw()
|
|
{
|
|
if (!frozenGraphics.empty() && !(state_ && RectSized(state_->Position, state_->Size) == g->Size().OriginRect()))
|
|
{
|
|
auto &frozen = frozenGraphics.top();
|
|
std::copy_n(frozen.screen.get(), g->Size().X * g->Size().Y, g->Data());
|
|
if (frozen.fadeTicks <= maxFadeTicks)
|
|
{
|
|
// from 0x00 at 0 to about 0x54 at 20
|
|
uint8_t alpha = (1 - std::pow(0.98, frozen.fadeTicks)) * 0xFF;
|
|
g->BlendFilledRect(g->Size().OriginRect(), 0x000000_rgb .WithAlpha(alpha));
|
|
}
|
|
// If this is the last frame in the fade, save what the faded image looks like
|
|
if (frozen.fadeTicks == maxFadeTicks)
|
|
std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozen.screen.get());
|
|
if (frozen.fadeTicks <= maxFadeTicks)
|
|
frozen.fadeTicks++;
|
|
}
|
|
else
|
|
{
|
|
g->Clear();
|
|
}
|
|
if(state_)
|
|
state_->DoDraw();
|
|
|
|
g->Finalise();
|
|
FrameIndex++;
|
|
FrameIndex %= 7200;
|
|
}
|
|
|
|
void Engine::SetFps(float fps)
|
|
{
|
|
this->fps = fps;
|
|
if(FpsLimit > 2.0f)
|
|
this->dt = 60/fps;
|
|
else
|
|
this->dt = 1.0f;
|
|
}
|
|
|
|
void Engine::onKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
|
{
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoKeyPress(key, scan, repeat, shift, ctrl, alt);
|
|
}
|
|
|
|
void Engine::onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
|
{
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoKeyRelease(key, scan, repeat, shift, ctrl, alt);
|
|
}
|
|
|
|
void Engine::onTextInput(String text)
|
|
{
|
|
if (textInput)
|
|
{
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoTextInput(text);
|
|
}
|
|
}
|
|
|
|
void Engine::onTextEditing(String text, int start)
|
|
{
|
|
if (textInput)
|
|
{
|
|
// * SDL sends the candidate string in packets of some arbitrary size,
|
|
// leaving it up to the user to assemble these packets into the
|
|
// complete candidate string. The start parameter tells us which
|
|
// portion of the candidate string the current packet spans.
|
|
// * Sadly, there's no documented way to tell the first or last packet
|
|
// apart from the rest. While there's also no documented guarantee
|
|
// that the packets come in order and that there are no gaps or
|
|
// overlaps between them, the implementation on the SDL side seems to
|
|
// ensure this. So what we do is just append whatever packet we get
|
|
// to a buffer, which we reset every time a "first-y looking" packet
|
|
// arrives. We also forward a textediting event on every packet,
|
|
// which is redundant, but should be okay, as textediting events are
|
|
// not supposed to have an effect on the actual text being edited.
|
|
if (start == 0)
|
|
{
|
|
textEditingBuf.clear();
|
|
}
|
|
textEditingBuf.append(text);
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoTextEditing(textEditingBuf);
|
|
}
|
|
}
|
|
|
|
void Engine::onMouseClick(int x, int y, unsigned button)
|
|
{
|
|
mouseb_ |= button;
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoMouseDown(x, y, button);
|
|
}
|
|
|
|
void Engine::onMouseUnclick(int x, int y, unsigned button)
|
|
{
|
|
mouseb_ &= ~button;
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoMouseUp(x, y, button);
|
|
}
|
|
|
|
void Engine::initialMouse(int x, int y)
|
|
{
|
|
mousexp_ = x;
|
|
mouseyp_ = y;
|
|
}
|
|
|
|
void Engine::onMouseMove(int x, int y)
|
|
{
|
|
mousex_ = x;
|
|
mousey_ = y;
|
|
if (state_ && !ignoreEvents)
|
|
{
|
|
state_->DoMouseMove(x, y, mousex_ - mousexp_, mousey_ - mouseyp_);
|
|
}
|
|
mousexp_ = x;
|
|
mouseyp_ = y;
|
|
}
|
|
|
|
void Engine::onMouseWheel(int x, int y, int delta)
|
|
{
|
|
if (state_ && !ignoreEvents)
|
|
state_->DoMouseWheel(x, y, delta);
|
|
}
|
|
|
|
void Engine::onClose()
|
|
{
|
|
if (state_)
|
|
state_->DoExit();
|
|
}
|
|
|
|
void Engine::onFileDrop(ByteString filename)
|
|
{
|
|
if (state_)
|
|
state_->DoFileDrop(filename);
|
|
}
|
|
|
|
void Engine::StartTextInput()
|
|
{
|
|
if (textInput)
|
|
{
|
|
return;
|
|
}
|
|
textInput = true;
|
|
::StartTextInput();
|
|
}
|
|
|
|
void Engine::StopTextInput()
|
|
{
|
|
if (!textInput)
|
|
{
|
|
return;
|
|
}
|
|
::StopTextInput();
|
|
textInput = false;
|
|
}
|
|
|
|
void Engine::TextInputRect(Point position, Point size)
|
|
{
|
|
::SetTextInputRect(position.X * Scale, position.Y * Scale, size.X * Scale, size.Y * Scale);
|
|
}
|