Adds InputWindowElement.
This commit is contained in:
parent
11279acb02
commit
a07c61675e
@ -148,6 +148,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\Font.cc" />
|
||||
<ClCompile Include="src\Game.cc" />
|
||||
<ClCompile Include="src\HiraganaMatcher.cc" />
|
||||
<ClCompile Include="src\Lua.cc" />
|
||||
<ClCompile Include="src\main.cc" />
|
||||
<ClCompile Include="src\PlayScene.cc" />
|
||||
@ -155,6 +156,8 @@
|
||||
<ClCompile Include="src\Win32Console.cc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\HiraganaMatcher.h" />
|
||||
<ClInclude Include="src\iInputMatcher.h" />
|
||||
<ClInclude Include="src\common.h" />
|
||||
<ClInclude Include="src\ElementStore.h" />
|
||||
<ClInclude Include="src\Font.h" />
|
||||
@ -166,6 +169,7 @@
|
||||
<ClInclude Include="src\iElementFactory.h" />
|
||||
<ClInclude Include="src\iLogger.h" />
|
||||
<ClInclude Include="src\iClock.h" />
|
||||
<ClInclude Include="src\InputWindowElementFactory.h" />
|
||||
<ClInclude Include="src\iScene.h" />
|
||||
<ClInclude Include="src\iWritable.h" />
|
||||
<ClInclude Include="src\Logger.h" />
|
||||
@ -181,6 +185,7 @@
|
||||
<ClInclude Include="src\StackAllocator.h" />
|
||||
<ClInclude Include="src\SystemClock.h" />
|
||||
<ClInclude Include="src\Text.h" />
|
||||
<ClInclude Include="src\InputWindowElement.h" />
|
||||
<ClInclude Include="src\Texture.h" />
|
||||
<ClInclude Include="src\TextureElement.h" />
|
||||
<ClInclude Include="src\TickingClock.h" />
|
||||
|
@ -39,6 +39,9 @@
|
||||
<ClCompile Include="src\PlayScene.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\HiraganaMatcher.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\iConsole.h">
|
||||
@ -155,6 +158,18 @@
|
||||
<ClInclude Include="thirdparty\utf8.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\InputWindowElement.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\iInputMatcher.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\HiraganaMatcher.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\InputWindowElementFactory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="thirdparty\lua5.1.lib" />
|
||||
|
@ -53,6 +53,7 @@ class ElementStore {
|
||||
for (auto& eptr : performing_) {
|
||||
iElement* e = eptr.get();
|
||||
if (e->period.end <= now) {
|
||||
e->Finalize();
|
||||
eptr = nullptr;
|
||||
} else {
|
||||
e->Update(frame, e->period.Normalize(now));
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "iDrawable.h"
|
||||
@ -46,6 +47,8 @@ class Frame : public iDrawable, public iWritable {
|
||||
}
|
||||
}
|
||||
|
||||
std::string input;
|
||||
|
||||
private:
|
||||
std::vector<const iDrawable*> draw_;
|
||||
std::vector<const iWritable*> write_;
|
||||
|
@ -36,10 +36,12 @@ class Game : public iDrawable, public iWritable {
|
||||
|
||||
Game(Param&& p);
|
||||
|
||||
void Update() {
|
||||
void Update(const std::string& input) {
|
||||
clock_.Tick();
|
||||
|
||||
frame_.Clear();
|
||||
frame_.input = input;
|
||||
|
||||
UniqPtr<iScene> next = scene_->Update(frame_);
|
||||
if (next) {
|
||||
scene_ = std::move(next);
|
||||
|
@ -30,7 +30,7 @@ class GlyphElementFactory : public iElementFactory {
|
||||
const intmax_t size = std::get<double>(param.custom[2]);
|
||||
|
||||
auto& font = FindOrCreateFont(name);
|
||||
auto tex = std::move(font.RenderGlyphs(ConvertUtf8ToUtf16(text), size)); /* TODO */
|
||||
auto tex = std::move(font.RenderGlyphs(ConvertStrToWstr(text), size)); /* TODO */
|
||||
|
||||
return alloc_->MakeUniq<iElement, TextureElement>(
|
||||
param.period, std::move(tex), std::move(param.driver));
|
||||
|
99
src/HiraganaMatcher.cc
Normal file
99
src/HiraganaMatcher.cc
Normal file
@ -0,0 +1,99 @@
|
||||
#include "HiraganaMatcher.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
static const std::vector<std::pair<std::wstring, std::wstring>> kPatterns = {
|
||||
{ L"ka", L"か", },
|
||||
{ L"ki", L"き", },
|
||||
{ L"ku", L"く", },
|
||||
{ L"ke", L"け", },
|
||||
{ L"ko", L"こ", },
|
||||
{ L"sa", L"さ", },
|
||||
{ L"si", L"し", },
|
||||
{ L"su", L"す", },
|
||||
{ L"se", L"せ", },
|
||||
{ L"so", L"そ", },
|
||||
{ L"ta", L"た", },
|
||||
{ L"ti", L"ち", },
|
||||
{ L"tu", L"つ", },
|
||||
{ L"te", L"て", },
|
||||
{ L"to", L"と", },
|
||||
{ L"na", L"な", },
|
||||
{ L"ni", L"に", },
|
||||
{ L"nu", L"ぬ", },
|
||||
{ L"ne", L"ね", },
|
||||
{ L"no", L"の", },
|
||||
{ L"a", L"あ", },
|
||||
{ L"i", L"い", },
|
||||
{ L"u", L"う", },
|
||||
{ L"e", L"え", },
|
||||
{ L"o", L"お", },
|
||||
};
|
||||
|
||||
|
||||
bool gj::HiraganaMatcher::Input(wchar_t c) {
|
||||
assert(!done());
|
||||
|
||||
const std::wstring remain = state_.text.substr(state_.match);
|
||||
const std::wstring newbuf = buffer_ + c;
|
||||
|
||||
const size_t n = newbuf.size();
|
||||
|
||||
size_t last_match = 0;
|
||||
bool accept = false;
|
||||
for (size_t i = 0; i < kPatterns.size(); ++i) {
|
||||
const auto& pattern = kPatterns[i];
|
||||
if (pattern.first.substr(0, n) == newbuf) {
|
||||
const size_t pn = pattern.second.size();
|
||||
if (remain.substr(0, pn) == pattern.second) {
|
||||
accept = true;
|
||||
last_match = i;
|
||||
}
|
||||
if (pattern.first.size() == n) {
|
||||
if (!accept) return false;
|
||||
state_.match += pn;
|
||||
buffer_ = L"";
|
||||
UpdateExpects(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
buffer_ = newbuf;
|
||||
UpdateExpects(last_match);
|
||||
}
|
||||
return accept;
|
||||
}
|
||||
|
||||
void gj::HiraganaMatcher::UpdateExpects(size_t last_match) {
|
||||
std::wstring text = state_.text.substr(state_.match);
|
||||
|
||||
expects_ = L"";
|
||||
if (text.empty()) return;
|
||||
|
||||
if (last_match < SIZE_MAX && buffer_.size()) {
|
||||
expects_ = kPatterns[last_match].first.substr(buffer_.size());
|
||||
text = text.substr(kPatterns[last_match].second.size());
|
||||
}
|
||||
|
||||
while (text.size()) {
|
||||
bool match = false;
|
||||
for (const auto& pattern : kPatterns) {
|
||||
const size_t n = pattern.second.size();
|
||||
if (text.size() < n) continue;
|
||||
|
||||
if (text.substr(0, n) == pattern.second) {
|
||||
expects_ += pattern.first;
|
||||
text = text.substr(n);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) Abort("hiragana janai");
|
||||
}
|
||||
}
|
44
src/HiraganaMatcher.h
Normal file
44
src/HiraganaMatcher.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "iInputMatcher.h"
|
||||
|
||||
namespace gj {
|
||||
|
||||
|
||||
class HiraganaMatcher : public iInputMatcher {
|
||||
public:
|
||||
HiraganaMatcher(HiraganaMatcher&&) = default;
|
||||
HiraganaMatcher(const HiraganaMatcher&) = default;
|
||||
|
||||
HiraganaMatcher& operator=(HiraganaMatcher&&) = default;
|
||||
HiraganaMatcher& operator=(const HiraganaMatcher&) = default;
|
||||
|
||||
HiraganaMatcher(const std::wstring& text) : state_{text, 0} {
|
||||
UpdateExpects();
|
||||
}
|
||||
|
||||
bool Input(wchar_t c) override;
|
||||
|
||||
const std::wstring& expects() const override {
|
||||
return expects_;
|
||||
}
|
||||
bool done() const override {
|
||||
return state_.text.size() <= state_.match;
|
||||
}
|
||||
const State& state() const override {
|
||||
return state_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring buffer_;
|
||||
std::wstring expects_;
|
||||
|
||||
State state_;
|
||||
|
||||
void UpdateExpects(size_t last_match = SIZE_MAX);
|
||||
};
|
||||
|
||||
|
||||
}
|
73
src/InputWindowElement.h
Normal file
73
src/InputWindowElement.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "common.h"
|
||||
#include "iElement.h"
|
||||
#include "iElementDriver.h"
|
||||
#include "iInputMatcher.h"
|
||||
#include "Text.h"
|
||||
|
||||
namespace gj {
|
||||
|
||||
|
||||
class InputWindowElement : public iElement {
|
||||
public:
|
||||
struct Param {
|
||||
UniqPtr<iInputMatcher> matcher;
|
||||
UniqPtr<iElementDriver> driver;
|
||||
|
||||
Period period;
|
||||
|
||||
std::wstring text;
|
||||
};
|
||||
|
||||
InputWindowElement() = delete;
|
||||
InputWindowElement(InputWindowElement&&) = delete;
|
||||
InputWindowElement(const InputWindowElement&) = delete;
|
||||
|
||||
InputWindowElement& operator=(InputWindowElement&&) = delete;
|
||||
InputWindowElement& operator=(const InputWindowElement&) = delete;
|
||||
|
||||
InputWindowElement(Param&& p) :
|
||||
iElement(p.period),
|
||||
matcher_(std::move(p.matcher)), drv_(std::move(p.driver)),
|
||||
text_(p.text), guide_(matcher_->expects()) {
|
||||
param_["posX"] = 0;
|
||||
param_["posY"] = 0;
|
||||
}
|
||||
|
||||
void Update(Frame& frame, double t) override {
|
||||
for (auto c : frame.input) {
|
||||
if (matcher_->done()) break;
|
||||
if (matcher_->Input(c)) {
|
||||
guide_ = Text(matcher_->expects());
|
||||
}
|
||||
}
|
||||
drv_->Update(param_, t);
|
||||
|
||||
const auto posX = std::get<intmax_t>(param_["posX"]);
|
||||
const auto posY = std::get<intmax_t>(param_["posY"]);
|
||||
|
||||
text_.SetPosition(posX, posY);
|
||||
guide_.SetPosition(posX, posY+1);
|
||||
frame.Add(&text_);
|
||||
frame.Add(&guide_);
|
||||
}
|
||||
|
||||
void Finalize() override {
|
||||
/* TODO score calculation */
|
||||
}
|
||||
|
||||
private:
|
||||
UniqPtr<iInputMatcher> matcher_;
|
||||
UniqPtr<iElementDriver> drv_;
|
||||
|
||||
Text text_;
|
||||
Text guide_;
|
||||
|
||||
iElementDriver::Param param_;
|
||||
};
|
||||
|
||||
|
||||
}
|
45
src/InputWindowElementFactory.h
Normal file
45
src/InputWindowElementFactory.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "HiraganaMatcher.h"
|
||||
#include "iAllocator.h"
|
||||
#include "iClock.h"
|
||||
#include "iElementFactory.h"
|
||||
#include "InputWindowElement.h"
|
||||
|
||||
|
||||
namespace gj {
|
||||
|
||||
|
||||
class InputWindowElementFactory : public iElementFactory {
|
||||
public:
|
||||
InputWindowElementFactory(InputWindowElementFactory&&) = delete;
|
||||
InputWindowElementFactory(const InputWindowElementFactory&) = delete;
|
||||
|
||||
InputWindowElementFactory& operator=(InputWindowElementFactory&&) = delete;
|
||||
InputWindowElementFactory& operator=(const InputWindowElementFactory&) = delete;
|
||||
|
||||
InputWindowElementFactory(iAllocator* alloc) : alloc_(alloc) {
|
||||
}
|
||||
|
||||
UniqPtr<iElement> Create(Param&& param) override {
|
||||
if (param.custom.size() != 2) return nullptr;
|
||||
|
||||
const std::string text = std::get<std::string>(param.custom[0]);
|
||||
const std::string kana = std::get<std::string>(param.custom[1]);
|
||||
|
||||
InputWindowElement::Param p;
|
||||
p.period = param.period;
|
||||
p.driver = std::move(param.driver);
|
||||
p.matcher = alloc_->MakeUniq<iInputMatcher, HiraganaMatcher>(ConvertStrToWstr(kana));
|
||||
p.text = ConvertStrToWstr(text);
|
||||
|
||||
return alloc_->MakeUniq<iElement, InputWindowElement>(std::move(p));
|
||||
}
|
||||
|
||||
private:
|
||||
iAllocator* alloc_;
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -1,15 +1,18 @@
|
||||
#include "PlayScene.h"
|
||||
|
||||
#include "GlyphElementFactory.h"
|
||||
#include "InputWindowElementFactory.h"
|
||||
|
||||
|
||||
gj::PlayScene::PlayScene(Param&& p) :
|
||||
alloc_(p.alloc), logger_(p.logger), w_(p.w), h_(p.h),
|
||||
clock_(p.clock), store_(&clock_, 256) {
|
||||
GlyphElementFactory glyph(alloc_);
|
||||
GlyphElementFactory glyph(alloc_);
|
||||
InputWindowElementFactory inputWin(alloc_);
|
||||
|
||||
Lua::FactoryMap map;
|
||||
map["Glyph"] = &glyph;
|
||||
map["Glyph"] = &glyph;
|
||||
map["InputWin"] = &inputWin;
|
||||
|
||||
lua_ = alloc_->MakeUniq<Lua>(
|
||||
alloc_, &store_, map, "res/score/" + p.score + ".lua");
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
frame.Add(&tex_);
|
||||
}
|
||||
|
||||
void Finalize() override {
|
||||
}
|
||||
|
||||
private:
|
||||
Texture tex_;
|
||||
UniqPtr<iElementDriver> drv_;
|
||||
|
@ -47,6 +47,35 @@ void gj::Win32Console::main() {
|
||||
constexpr CONSOLE_CURSOR_INFO cursor{ 1, FALSE };
|
||||
SetConsoleCursorInfo(screen_, &cursor);
|
||||
|
||||
/* read input */
|
||||
{ /* critical section */
|
||||
std::lock_guard<std::mutex> _(mtx_);
|
||||
|
||||
INPUT_RECORD input[256];
|
||||
DWORD input_n;
|
||||
PeekConsoleInput(buffer_, input, 256, &input_n);
|
||||
if (input_n) {
|
||||
/* Peek* can retrieve the input but doesn't remove it from the buffer
|
||||
* so calls Read* to clear. */
|
||||
ReadConsoleInput(buffer_, input, 256, &input_n);
|
||||
}
|
||||
for (DWORD i = 0; i < input_n; ++i) {
|
||||
const auto& rec = input[i];
|
||||
switch (rec.EventType) {
|
||||
case KEY_EVENT:
|
||||
if (rec.Event.KeyEvent.bKeyDown) {
|
||||
input_ += rec.Event.KeyEvent.uChar.AsciiChar;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* write output */
|
||||
CHAR_INFO* c = chars_.get();
|
||||
{ /* critical section */
|
||||
std::lock_guard<std::mutex> _(mtx_);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
@ -10,6 +11,7 @@
|
||||
#include <windows.h>
|
||||
#undef NOMINMAX
|
||||
|
||||
#include "common.h"
|
||||
#include "iConsole.h"
|
||||
|
||||
|
||||
@ -32,10 +34,13 @@ class Win32Console : public iConsole {
|
||||
tb_main_(alloc, w, h), tb_sub_(alloc, w, h),
|
||||
chars_(std::make_unique<CHAR_INFO[]>(static_cast<uint64_t>(w)*h)),
|
||||
win_(GetConsoleWindow()) {
|
||||
_ASSERT(win_);
|
||||
if (!win_) gj::Abort("GetConsoleWindow returned nullptr");
|
||||
|
||||
screen_ = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
_ASSERT(screen_ != INVALID_HANDLE_VALUE);
|
||||
buffer_ = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (screen_ == INVALID_HANDLE_VALUE || buffer_ == INVALID_HANDLE_VALUE) {
|
||||
gj::Abort("GetStdHandle returned nullptr");
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFOEX size;
|
||||
size.cbSize = sizeof(size);
|
||||
@ -83,6 +88,13 @@ class Win32Console : public iConsole {
|
||||
std::swap(tb_main_, tb_sub_);
|
||||
}
|
||||
|
||||
std::string TakeInput() override {
|
||||
std::lock_guard<std::mutex> _(mtx_);
|
||||
std::string ret = std::move(input_);
|
||||
input_ = "";
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t width() const override {
|
||||
return w_;
|
||||
}
|
||||
@ -107,8 +119,11 @@ class Win32Console : public iConsole {
|
||||
std::unique_ptr<CHAR_INFO[]> chars_;
|
||||
|
||||
HANDLE screen_ = INVALID_HANDLE_VALUE;
|
||||
HANDLE buffer_ = INVALID_HANDLE_VALUE;
|
||||
HWND win_ = nullptr;
|
||||
|
||||
std::string input_;
|
||||
|
||||
void main();
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,7 @@ using mat3 = ::linalg::mat<double, 3, 3>;
|
||||
using vec3 = ::linalg::vec<double, 3>;
|
||||
|
||||
|
||||
static inline std::wstring ConvertUtf8ToUtf16(const std::string& str) {
|
||||
static inline std::wstring ConvertStrToWstr(const std::string& str) {
|
||||
std::wstring ret;
|
||||
|
||||
const void* c = str.c_str();
|
||||
@ -33,7 +33,7 @@ static inline std::wstring ConvertUtf8ToUtf16(const std::string& str) {
|
||||
|
||||
[[noreturn]]
|
||||
static inline void Abort(const std::string& msg) {
|
||||
MessageBox(NULL, ConvertUtf8ToUtf16(msg).c_str(), L"PROGRAM ABORTED", MB_OK);
|
||||
MessageBox(NULL, ConvertStrToWstr(msg).c_str(), L"PROGRAM ABORTED", MB_OK);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ class iConsole {
|
||||
virtual Textbuffer& TakeTextbuffer() = 0;
|
||||
virtual void SwapTextbuffer() = 0;
|
||||
|
||||
virtual std::string TakeInput() = 0;
|
||||
|
||||
virtual uint32_t width() const = 0;
|
||||
virtual uint32_t height() const = 0;
|
||||
};
|
||||
|
@ -23,6 +23,8 @@ class iElement {
|
||||
|
||||
virtual void Update(Frame& frame, double t) = 0;
|
||||
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
/* Interfaces had better not have a variable but this is for optimization. */
|
||||
const Period period;
|
||||
};
|
||||
|
35
src/iInputMatcher.h
Normal file
35
src/iInputMatcher.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace gj {
|
||||
|
||||
|
||||
class iInputMatcher {
|
||||
public:
|
||||
struct State {
|
||||
std::wstring text;
|
||||
size_t match;
|
||||
};
|
||||
|
||||
iInputMatcher(iInputMatcher&&) = default;
|
||||
iInputMatcher(const iInputMatcher&) = default;
|
||||
|
||||
iInputMatcher& operator=(iInputMatcher&&) = default;
|
||||
iInputMatcher& operator=(const iInputMatcher&) = default;
|
||||
|
||||
iInputMatcher() = default;
|
||||
virtual ~iInputMatcher() = default;
|
||||
|
||||
virtual bool Input(wchar_t c) = 0;
|
||||
|
||||
virtual const std::wstring& expects() const = 0;
|
||||
|
||||
virtual bool done() const = 0;
|
||||
|
||||
virtual const State& state() const = 0;
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -32,7 +32,7 @@ int main() {
|
||||
param.h = kHeight;
|
||||
gj::Game game(std::move(param));
|
||||
while (true) {
|
||||
game.Update();
|
||||
game.Update(console.TakeInput());
|
||||
{
|
||||
auto& fb = console.TakeColorbuffer();
|
||||
fb.Clear();
|
||||
|
Reference in New Issue
Block a user