3#include <Tempest/Painter>
15using namespace Tempest;
17bool DialogMenu::Pipe::output(
Npc &npc, std::string_view text) {
18 return owner.aiOutput(npc,text);
21bool DialogMenu::Pipe::outputSvm(
Npc &npc, std::string_view text) {
22 return owner.aiOutput(npc,text);
25bool DialogMenu::Pipe::outputOv(
Npc &npc, std::string_view text) {
26 return owner.aiOutput(npc,text);
29bool DialogMenu::Pipe::printScr(
Npc& npc,
int time, std::string_view msg,
int x,
int y, std::string_view font) {
30 return owner.aiPrintScr(npc,time,msg,x,y,font);
33bool DialogMenu::Pipe::close() {
34 return owner.aiClose();
37bool DialogMenu::Pipe::isFinished() {
38 return !owner.isNpcInDialog(
nullptr);
42 :trade(trade), pipe(*this) {
46 setCursorShape(CursorShape::Hidden);
47 setFocusPolicy(NoFocus);
64 if(state==State::PreStart) {
68 onStart(*this->pl,*this->other);
72 if(remPrint>0 || choiceAnimTime>0 || current.time>0 || pscreen.size()>0)
76 for(
size_t i=1;i<MAX_PRINT;++i)
77 printMsg[i-1u]=printMsg[i];
78 printMsg[MAX_PRINT-1]=PScreen();
85 if(choiceAnimTime+dt>ANIM_TIME)
86 choiceAnimTime = ANIM_TIME;
else
90 choiceAnimTime = 0;
else
96 if(dlgTrade && !haveToWaitOutput()) {
99 else if(choice.size()==0 && state!=State::Idle && !haveToWaitOutput()) {
106 for(
size_t i=0;i<pscreen.size();){
109 pscreen.erase(pscreen.begin()+
int(i));
119void DialogMenu::drawTextMultiline(Painter &p,
int x,
int y,
int w,
int h, std::string_view txt,
bool isPl) {
120 auto sz = processTextMultiline(
nullptr,0,0,w,h,txt,isPl);
121 y+=std::max(0, (h-sz.h)/2);
122 processTextMultiline(&p,x,y,w,h,txt,isPl);
125Size DialogMenu::processTextMultiline(Painter* p,
int x,
int y,
int w,
int h, std::string_view txt,
bool isPl) {
127 const int pdd = int(10*scale);
131 if(!isPl && other!=
nullptr) {
132 y += fnt.pixelSize();
134 auto sz = fnt.textSize(w,txt.data());
136 fnt.drawText(*p,x+(w-sz.w)/2,y,txt.data());
138 ret.w = std::max(ret.w,sz.w);
145 ret.h += fnt.pixelSize();
147 fnt.drawText(*p,x+pdd, y,
148 w-2*pdd, h, txt, Tempest::AlignHCenter);
150 auto sz = fnt.textSize(w,txt);
151 ret.w = std::max(ret.w,sz.w);
158 for(
auto& i:printMsg)
164 assert(state==State::Idle);
170 return pl!=
nullptr && other==pl && pl->
interactive()!=
nullptr;
184 float l = p0.length();
202 state = State::PreStart;
206 if(state==State::Idle)
208 return npc==pl || npc==other || npc==
nullptr;
211bool DialogMenu::aiOutput(
Npc &npc, std::string_view msg) {
212 if(&npc!=pl && &npc!=other){
213 Log::e(
"unexpected aiOutput call: ",msg.data());
230 current.time = current.msgTime + (dlgAnimation ? ANIM_TIME*2 : 0);
232 curentIsPl = (pl==&npc);
240bool DialogMenu::aiPrintScr(
Npc& npc,
int time, std::string_view msg,
int x,
int y, std::string_view font) {
248bool DialogMenu::aiClose() {
261 return (state!=State::Idle) || current.time>0;
265 return (current.time>0 || choice.size()>0);
268bool DialogMenu::onStart(
Npc &p,
Npc &ot) {
270 state = State::Active;
275 if(choice.size()>0 && choice[0].title.size()==0){
289 e.time = uint32_t(time*1000)+1000;
292 pscreen.emplace(pscreen.begin(),std::move(e));
297 for(
size_t i=0;i<MAX_PRINT;++i){
298 auto& sc = printMsg[i];
302 auto& fnt = *sc.font;
303 auto sz = fnt.textSize(sc.txt);
304 int x = (w()-sz.w)/2;
305 fnt.drawText(p, x, offsetY +
int(i*2+1)*sz.h, sc.txt);
314 for(
size_t i=1;i<MAX_PRINT;++i)
315 printMsg[i-1u]=printMsg[i];
318 PScreen& e=printMsg[MAX_PRINT-1];
327void DialogMenu::onDoneText() {
330 if(choice.size()==0){
339void DialogMenu::close() {
341 auto prevNpc = other;
350 currentSnd = SoundEffect();
353 if(prevPl!=
nullptr && prevPl==prevNpc){
354 auto i = prevPl->interactive();
356 prevPl->setInteraction(
nullptr);
359 prevPl->stopDlgAnim();
361 if(prevNpc && prevNpc!=prevPl){
362 prevNpc->stopDlgAnim();
366bool DialogMenu::haveToWaitOutput()
const {
376bool DialogMenu::haveToShowSubtitles(
bool isPl)
const {
377 return showSubtitles && (showSubtitlesPlayer || !isPl);
380void DialogMenu::startTrade() {
381 if(pl!=
nullptr && other!=
nullptr)
382 trade.
trade(*pl,*other);
386void DialogMenu::skipPhrase() {
392 currentSnd = SoundEffect();
413 const uint64_t da = dlgAnimation ? ANIM_TIME : 0;
414 const int dw = std::min(w(),
int(600*scale));
415 const int dh = int(100*scale);
418 if(ambient!=
nullptr) {
422 if(current.time>current.msgTime+da) {
423 float k = 1.f-float(current.time-current.msgTime-da)/float(da);
424 dlgW = int(
float(dlgW)*k);
425 dlgH = int(
float(dlgH)*k);
427 else if(current.time<da) {
428 float k = float(current.time)/float(da);
429 dlgW = int(
float(dlgW)*k);
430 dlgH = int(
float(dlgH)*k);
433 p.setBrush(*ambient);
434 p.drawRect((w()-dlgW)/2, 20+(dh-dlgH)/2, dlgW, dlgH,
435 0,0,ambient->w(),ambient->h());
438 if(current.time>da && current.time<current.msgTime+da)
439 drawTextMultiline(p,(w()-dw)/2,20,dw,dh,current.txt,curentIsPl);
444 for(
size_t i=0;i<pscreen.size();++i){
445 auto& sc = pscreen[i];
449 auto sz = fnt.textSize(sc.txt);
450 auto area = this->size();
462 y = ((area.h-sz.h)*y)/100+sz.h;
464 fnt.drawText(p, x, y, sc.txt);
473 const int padd = int(20*scale);
474 const int dw = std::min(w(),
int(600*scale));
478 for(
size_t i=0;i<choice.size();++i){
480 Size choiceTextSize = fnt.textSize(dw-padd, choice[i].title);
481 dh += choiceTextSize.h;
484 const int y = h()-dh-int(20*scale);
486 if(tex!=
nullptr && (choiceAnimTime>0 ||
isActive)) {
487 float k = dlgAnimation ? (float(choiceAnimTime)/float(ANIM_TIME)) : 1.f;
488 int dlgW = int(
float(dw)*k);
489 int dlgH = int(
float(dh)*k);
493 p.drawRect((w()-dlgW)/2,y+(dh-dlgH)/2,dlgW,dlgH,
494 0,0,tex->w(),tex->h());
497 if(choiceAnimTime<ANIM_TIME && dlgAnimation)
503 int textHeightOffset = padd;
505 for(
size_t i=0;i<choice.size();++i){
512 Size choiceTextSize = fnt.
textSize(dw-padd, choice[i].title);
513 fnt.
drawText(p,x+padd,y+padd+textHeightOffset,dw-padd,0,choice[i].title,AlignLeft);
514 textHeightOffset += choiceTextSize.h;
523 if(current.time>0 || haveToWaitOutput()){
527 if(dlgSel<choice.size()) {
528 onEntry(choice[dlgSel]);
529 choiceAnimTime = dlgAnimation ? ANIM_TIME : 0;
539 if(event.button==MouseEvent::ButtonLeft) {
542 if(event.button==MouseEvent::ButtonRight) {
557 dlgSel = (dlgSel+choice.size())%std::max<size_t>(choice.size(),1);
567 if(e.key==Event::K_Return){
571 if(e.key==Event::K_W || e.key==Event::K_S || e.key==Event::K_Up || e.key==Event::K_Down || e.key==Event::K_ESCAPE){
572 if(e.key==Event::K_W || e.key==Event::K_Up){
575 if(e.key==Event::K_S || e.key==Event::K_Down){
578 if(e.key==Event::K_ESCAPE){
581 dlgSel = (dlgSel+choice.size())%std::max<size_t>(choice.size(),1);
589 if(state==State::Idle && current.time>0){
void setPosition(const Tempest::Vec3 &pos)
void setSpin(const Tempest::PointF &p)
void setDialogDistance(float d)
void dialogExec(const GameScript::DlgChoice &dlg, Npc &player, Npc &npc)
Tempest::Signal< void()> onSettingsChanged
Tempest::Signal< void(std::string_view, int, int, int, const GthFont &)> onPrintScreen
static float interfaceScale(const Tempest::Widget *w)
auto updateDialog(const GameScript::DlgChoice &dlg, Npc &player, Npc &npc) -> std::vector< GameScript::DlgChoice >
static int settingsGetI(std::string_view sec, std::string_view name)
uint32_t messageTime(std::string_view id) const
std::string_view messageByName(std::string_view id) const
void drawText(Tempest::Painter &p, int x, int y, int w, int h, std::string_view txt, Tempest::AlignFlag align, int firstLine=0) const
auto textSize(const std::string_view txt) const -> Tempest::Size
auto cameraBone(bool isFirstPerson=false) const -> Tempest::Vec3
auto dialogChoices(Npc &player, const std::vector< uint32_t > &except, bool includeImp) -> std::vector< GameScript::DlgChoice >
auto interactive() const -> Interactive *
std::string_view displayName() const
void setAiOutputBarrier(uint64_t dt, bool overlay)
static const GthFont & font(const float scale)
static const GthFont & dialogFont(const float scale)
static const Tempest::Texture2d * loadTexture(std::string_view name, bool forceMips=false)
static Tempest::Sound loadSoundBuffer(std::string_view name)