forked from townforge/townforge
game: tutorials are now loaded from json
This commit is contained in:
parent
a2b226d602
commit
8c26c89c84
11
GameData/Tutorial/tutorials.json
Normal file
11
GameData/Tutorial/tutorials.json
Normal file
@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"trigger": "main-panel",
|
||||
"title": "Main panel",
|
||||
"pages": [
|
||||
{
|
||||
"text": "The main panel can be unrolled by clicking on the arrow, or double clicking on the title bar.\n\nYou can move it by dragging the title bar and resize it by dragging the bottom right corner.\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -421,6 +421,7 @@ private:
|
||||
void SetupUI();
|
||||
void SetupWallet();
|
||||
void SetupDaemon();
|
||||
void LoadTutorials();
|
||||
void UnsetFocus();
|
||||
void RebuildMap(bool save = false);
|
||||
void SelectNextMaterial(bool next);
|
||||
@ -793,6 +794,8 @@ void CryptoCityUrho3D::Setup(void)
|
||||
String resources = "CoreData;Data;GameData;";
|
||||
if (epee::file_io_utils::is_file_exist((config_dir + "/ExtraMusic.pak").CString()))
|
||||
resources += "ExtraMusic;";
|
||||
if (epee::file_io_utils::is_file_exist((config_dir + "/Tutorial.pak").CString()))
|
||||
resources += "Tutorial;";
|
||||
engineParameters_[EP_RESOURCE_PATHS] = resources;
|
||||
}
|
||||
|
||||
@ -863,6 +866,8 @@ void CryptoCityUrho3D::Start()
|
||||
|
||||
SetupDaemon();
|
||||
|
||||
LoadTutorials();
|
||||
|
||||
// setup wallet
|
||||
SetupWallet();
|
||||
}
|
||||
@ -886,6 +891,13 @@ void CryptoCityUrho3D::SetupDaemon()
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoCityUrho3D::LoadTutorials()
|
||||
{
|
||||
auto* cache = GetSubsystem<ResourceCache>();
|
||||
SharedPtr<JSONFile> tutorials(cache->GetResource<JSONFile>("Tutorial/tutorials.json", false));
|
||||
load_tutorials(tutorials);
|
||||
}
|
||||
|
||||
void CryptoCityUrho3D::SetupWallet()
|
||||
{
|
||||
const auto args = Urho3D::GetArguments();
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <Urho3D/Urho3D.h>
|
||||
#include <Urho3D/Resource/JSONFile.h>
|
||||
#include <tb/tb_editfield.h>
|
||||
#include <tb/tb_toggle_container.h>
|
||||
#include <tb/image/tb_image_widget.h>
|
||||
@ -9,21 +10,23 @@
|
||||
using namespace Urho3D;
|
||||
using namespace tb;
|
||||
|
||||
struct TutorialData
|
||||
{
|
||||
TBStr title;
|
||||
std::vector<std::pair<TBStr, TBStr>> pages;
|
||||
};
|
||||
std::map<TBStr, TutorialData> tutorial_data;
|
||||
|
||||
class TutorialWindow: public UITBWindow
|
||||
{
|
||||
public:
|
||||
TutorialWindow(Urho3D::Context *ctx, const GameState *game = nullptr, const std::vector<std::pair<const char*, const char*>> &data = {});
|
||||
TutorialWindow(Urho3D::Context *ctx, const GameState *game = nullptr, const std::vector<const char*> &text = {});
|
||||
TutorialWindow(Urho3D::Context *ctx, const GameState *game = nullptr, const char *text = "");
|
||||
TutorialWindow(Urho3D::Context *ctx, const GameState *game = nullptr, const TutorialData &td = {});
|
||||
|
||||
void HandleEnableTutorial(StringHash eventType, VariantMap& eventData);
|
||||
void HandleClose(StringHash eventType, VariantMap& eventData);
|
||||
void HandleTBMessage(StringHash eventType, VariantMap& eventData);
|
||||
void SetPage(uint32_t page);
|
||||
|
||||
private:
|
||||
static std::vector<std::pair<const char*, const char*>> make_data(const std::vector<const char*> &text);
|
||||
|
||||
public:
|
||||
const GameState *game;
|
||||
std::vector<std::pair<TBStr, TBStr>> pages;
|
||||
@ -33,40 +36,25 @@ public:
|
||||
tb::TBButton *prev, *next;
|
||||
};
|
||||
|
||||
TutorialWindow::TutorialWindow(Urho3D::Context *ctx, const GameState *game, const std::vector<std::pair<const char*, const char*>> &text):
|
||||
TutorialWindow::TutorialWindow(Urho3D::Context *ctx, const GameState *game, const TutorialData &td):
|
||||
UITBWindow(ctx, "cc/tutorial-basic.tb.txt"),
|
||||
game(game)
|
||||
{
|
||||
for (const auto &s: text)
|
||||
pages.push_back(std::make_pair(s.first, s.second));
|
||||
for (const auto &e: td.pages)
|
||||
pages.push_back(e);
|
||||
|
||||
GetWidgetByIDAndType<TBToggleContainer>(TBIDC("prev-next-container"))->SetValue(pages.size() > 1);
|
||||
|
||||
TBStr title = "Tutorial - ";
|
||||
title.Append(td.title);
|
||||
SetText(std::move(title));
|
||||
|
||||
SetPage(0);
|
||||
|
||||
SubscribeToEvent(this, E_TB_WIDGET_EVENT, URHO3D_HANDLER(TutorialWindow, HandleTBMessage));
|
||||
SubscribeToEvent(this, E_TB_WINDOW_CLOSED, URHO3D_HANDLER(TutorialWindow, HandleClose));
|
||||
}
|
||||
|
||||
TutorialWindow::TutorialWindow(Urho3D::Context *ctx, const GameState *game, const std::vector<const char*> &text):
|
||||
TutorialWindow(ctx, game, make_data(text))
|
||||
{
|
||||
}
|
||||
|
||||
TutorialWindow::TutorialWindow(Urho3D::Context *ctx, const GameState *game, const char *text):
|
||||
TutorialWindow(ctx, game, {1, std::make_pair("", text)})
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::pair<const char*, const char*>> TutorialWindow::make_data(const std::vector<const char*> &text)
|
||||
{
|
||||
std::vector<std::pair<const char*, const char*>> v;
|
||||
v.reserve(text.size());
|
||||
for (const char *s: text)
|
||||
v.push_back(std::make_pair("", s));
|
||||
return v;
|
||||
}
|
||||
|
||||
void TutorialWindow::SetPage(unsigned page)
|
||||
{
|
||||
this->page = page;
|
||||
@ -132,69 +120,62 @@ void TutorialWindow::HandleClose(StringHash eventType, VariantMap& eventData)
|
||||
Close();
|
||||
}
|
||||
|
||||
static UITBWindow * display_main_panel_tutorial(Context *ctx, const GameState *game)
|
||||
void load_tutorials(const SharedPtr<JSONFile> &tutorials)
|
||||
{
|
||||
return new TutorialWindow(ctx, game,
|
||||
"The main panel can be unrolled by clicking on the arrow, or double clicking on the title bar.\n"
|
||||
"\n"
|
||||
"You can move it by dragging the title bar and resize it by dragging the bottom right corner.\n"
|
||||
);
|
||||
}
|
||||
|
||||
static UITBWindow * display_player_tutorial(Context *ctx, const GameState *game)
|
||||
{
|
||||
return new TutorialWindow(ctx, game,
|
||||
"The player section contains player and wallet information. If you have a wallet loaded, you can see your in-game and out of game balances, "
|
||||
"as well as move gold in and out of the game at will.\n"
|
||||
"\n"
|
||||
"The ? button near your player name will open the player info screen.\n"
|
||||
);
|
||||
}
|
||||
|
||||
static UITBWindow * display_node_tutorial(Context *ctx, const GameState *game)
|
||||
{
|
||||
return new TutorialWindow(ctx, game, {
|
||||
"The node keeps up with other Townforge nodes on the network. You can ask your node to mine, but long term mining is best done "
|
||||
"while the game is not running, since the game also takes substantial CPU time and memory\n"
|
||||
});
|
||||
}
|
||||
|
||||
static UITBWindow * display_calendar_tutorial(Context *ctx, const GameState *game)
|
||||
{
|
||||
return new TutorialWindow(ctx, game,
|
||||
"Every in-game year lasts a week in real time.\n"
|
||||
"\n"
|
||||
"Townforge has seasons: spring, summer, autumn and winter, each divided in three months: early, mid and late.\n"
|
||||
"For simplicity, every month has 30 days. Sowing is done in spring and harvest in summer.\n"
|
||||
"\n"
|
||||
"A game tick happens four times a day. Game ticks process world changes, such as player income and taxes, building consumption and production, "
|
||||
"awarding of new badges, building decay, special events, and more.\n"
|
||||
);
|
||||
}
|
||||
|
||||
static UITBWindow * display_commands_tutorial(Context *ctx, const GameState *game)
|
||||
{
|
||||
return new TutorialWindow(ctx, game,
|
||||
"The commands section lists a lot of what you can do in Townforge.\n"
|
||||
"\n"
|
||||
"All these commands will open a new screen, except Buy land, which will try to buy the land you have selected in the 3D world.\n"
|
||||
"\n"
|
||||
"To select some land, click on the landscape and drag the mouse while keeping the left button pressed. Then click Buy land to buy it.\n"
|
||||
"The minimum size of a plot of land is 8x8 squares, and the maximum is 256x256 squares.\n"
|
||||
"Different building types will need different plot sizes though, check the manual for details.\n"
|
||||
"\n"
|
||||
"As with every action with Townforge, the plot of land will be bought only after a block is mined which includes your purchase.\n"
|
||||
);
|
||||
if (!tutorials)
|
||||
return;
|
||||
const JSONValue &root = tutorials->GetRoot();
|
||||
if (!root.IsArray())
|
||||
{
|
||||
printf("tutorials: root is not an array\n");
|
||||
return;
|
||||
}
|
||||
const JSONArray &array = root.GetArray();
|
||||
for (unsigned i = 0; i < array.Size(); ++i)
|
||||
{
|
||||
if (!array[i].Get("trigger").IsString() || !array[i].Get("title").IsString() || !array[i].Get("pages").IsArray())
|
||||
{
|
||||
printf("tutorials: type error at index %u\n", i);
|
||||
continue;
|
||||
}
|
||||
const TBStr trigger = array[i].Get("trigger").GetString().CString();
|
||||
const auto it = tutorial_data.find(trigger);
|
||||
if (it != tutorial_data.end())
|
||||
{
|
||||
printf("Duplicate tutorial trigger: %s\n", trigger.CStr());
|
||||
continue;
|
||||
}
|
||||
TutorialData td;
|
||||
td.title = array[i].Get("title").GetString().CString();
|
||||
const JSONArray &pages = array[i].Get("pages").GetArray();
|
||||
for (unsigned j = 0; j < pages.Size(); ++j)
|
||||
{
|
||||
std::pair<TBStr, TBStr> page;
|
||||
if (pages[j].Get("image").IsString())
|
||||
page.first = pages[j].Get("image").GetString().CString();
|
||||
if (pages[j].Get("text").IsString())
|
||||
page.second = pages[j].Get("text").GetString().CString();
|
||||
td.pages.push_back(std::move(page));
|
||||
}
|
||||
tutorial_data[trigger] = std::move(td);
|
||||
}
|
||||
}
|
||||
|
||||
UITBWindow *display_tutorial(Context *ctx, const GameState *game, const char *tag)
|
||||
{
|
||||
#define TUTORIAL(t, f) if (!strcmp(tag, t)) { return f(ctx, game); ; }
|
||||
TUTORIAL("main-panel", display_main_panel_tutorial);
|
||||
TUTORIAL("calendar-section", display_calendar_tutorial);
|
||||
TUTORIAL("player-section", display_player_tutorial);
|
||||
TUTORIAL("node-section", display_node_tutorial);
|
||||
TUTORIAL("commands-section", display_commands_tutorial);
|
||||
#undef TUTORIAL
|
||||
return NULL;
|
||||
const auto i = tutorial_data.find(tag);
|
||||
if (i == tutorial_data.end())
|
||||
{
|
||||
printf("Tutorial not found: %s\n", tag);
|
||||
return NULL;
|
||||
}
|
||||
return new TutorialWindow(ctx, game, i->second);
|
||||
}
|
||||
|
||||
std::set<TBStr> get_tutorial_list()
|
||||
{
|
||||
std::set<TBStr> tutorials;
|
||||
for (const auto &e: tutorial_data)
|
||||
tutorials.insert(e.second.title);
|
||||
return tutorials;
|
||||
}
|
||||
|
@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <Urho3D/Container/Ptr.h>
|
||||
|
||||
namespace Urho3D
|
||||
{
|
||||
class Context;
|
||||
class JSONFile;
|
||||
}
|
||||
|
||||
namespace TB
|
||||
{
|
||||
class TBStr;
|
||||
}
|
||||
|
||||
class GameState;
|
||||
@ -10,4 +19,6 @@ class UITBWindow;
|
||||
|
||||
URHO3D_EVENT(E_TUTORIAL_CLOSED, TutorialClosed) { }
|
||||
|
||||
void load_tutorials(const Urho3D::SharedPtr<Urho3D::JSONFile> &tutorials);
|
||||
UITBWindow *display_tutorial(Urho3D::Context *ctx, const GameState *game, const char *tag);
|
||||
std::set<tb::TBStr> get_tutorial_list();
|
||||
|
Loading…
Reference in New Issue
Block a user