Files
starruler-linux/scripts/shared/dialogue.as
T
2018-07-17 14:15:37 +02:00

311 lines
5.8 KiB
ActionScript 3

#section server
import void addDialogue(Dialogue@ diag) from "scenario";
#section all
class Dialogue : Serializable {
string title;
string text;
string proceedText;
Sprite icon;
array<Objective@> objectives;
//pass is called at the start of the page, even if skipping/loading through it
DialogueAction@ pass;
//complete is called when the page is first completed or skipped, not when loading.
DialogueAction@ complete;
//start is called when the page is trying to appear
DialogueAction@ start;
Dialogue() {
_add();
}
Dialogue(const string& title, const string& text, const Sprite& icon = Sprite()) {
this.title = title;
this.text = text;
this.icon = icon;
_add();
}
Dialogue(const string& ident) {
this.title = localize("#"+ident+"_TITLE");
this.text = localize("#"+ident+"_TEXT");
addObjectives(ident);
_add();
}
Dialogue& proceedWith(const string& text) {
proceedText = localize(text);
return this;
}
Dialogue& onPass(DialogueAction@ act) {
@pass = act;
return this;
}
Dialogue& onComplete(DialogueAction@ act) {
@complete = act;
return this;
}
Dialogue& onStart(DialogueAction@ act) {
@start = act;
return this;
}
Dialogue& get_newObjective() {
objectives.insertLast(Objective(empty=true));
return this;
}
Dialogue& addObjectives(const string& ident) {
for(uint i = 1; true; ++i) {
string title = localize("#"+ident+"_ACT"+i+"_TITLE");
if(title[0] == '#')
break;
objectives.insertLast(Objective(ident+"_ACT"+i));
}
return this;
}
Dialogue& checker(uint index, ObjectiveCheck@ check, bool skippable = false) {
if(index > objectives.length) {
error("Invalid objective "+index+" on "+title);
return this;
}
@objectives[index-1].check = check;
objectives[index-1].skippable = skippable;
return this;
}
Dialogue& objectiveKeybind(uint index, uint keybind) {
if(index > objectives.length) {
error("Invalid objective "+index+" on "+title);
return this;
}
auto@ obj = objectives[index-1];
int key = keybinds::Global.getCurrentKey(Keybind(keybind), 0);
string keyname = getKeyDisplayName(key);
obj.text = format(obj.text, keyname);
return this;
}
void _add() {
#section server
addDialogue(this);
#section all
}
void write(Message& msg) {
msg << title << text << proceedText;
msg << getSpriteDesc(icon);
msg.writeSmall(objectives.length);
for(uint i = 0, cnt = objectives.length; i < cnt; ++i)
msg << objectives[i];
}
void read(Message& msg) {
msg >> title >> text >> proceedText;
string desc;
msg >> desc;
icon = getSprite(desc);
uint cnt = msg.readSmall();
objectives.length = cnt;
for(uint i = 0, cnt = objectives.length; i < cnt; ++i) {
if(objectives[i] !is null) {
msg >> objectives[i];
}
else {
Objective obj;
msg >> obj;
@objectives[i] = obj;
}
}
}
};
class DialogueAction {
void call() {
}
bool check() {
return true;
}
};
class GUIAction : DialogueAction {
string id;
GUIAction(const string& id) {
this.id = id;
}
void call() {
#section server
guiDialogueAction(CURRENT_PLAYER, id);
#section all
}
};
void DialogueRemoteAction(string id) {
auto@ cls = getClass(id);
if(cls is null)
return;
Lock lck(actMtx);
actions.insertLast(cast<DialogueAction>(cls.create()));
}
Mutex actMtx;
array<DialogueAction@> actions;
array<GuiObjectiveCheck@> guiCheckers;
array<GUIChecker@> remoteCheckers;
void tick(double time) {
Lock lck(actMtx);
for(uint i = 0, cnt = actions.length; i < cnt; ++i)
actions[i].call();
actions.length = 0;
#section gui
for(uint i = 0, cnt = guiCheckers.length; i < cnt; ++i) {
if(guiCheckers[i].check()) {
srvObjectiveComplete(guiCheckers[i].id);
guiCheckers[i].end();
guiCheckers.removeAt(i);
--i; --cnt;
}
}
#section all
}
void RemoteObjectiveStart(string id, Object@ obj) {
auto@ cls = getClass(id);
if(cls is null)
return;
Lock lck(actMtx);
auto@ chk = cast<GuiObjectiveCheck>(cls.create());
chk.id = id;
@chk.obj = obj;
chk.start();
guiCheckers.insertLast(chk);
}
void RemoteObjectiveEnd(string id) {
Lock lck(actMtx);
for(uint i = 0, cnt = guiCheckers.length; i < cnt; ++i) {
if(guiCheckers[i].id == id) {
guiCheckers[i].end();
guiCheckers.removeAt(i);
--i; --cnt;
}
}
}
void RemoteObjectiveComplete(string id) {
Lock lck(actMtx);
for(uint i = 0, cnt = remoteCheckers.length; i < cnt; ++i) {
if(remoteCheckers[i].id == id)
remoteCheckers[i].completed = true;
}
}
int nextObjectiveId = 0;
class Objective : Serializable {
int id;
string title;
string text;
Sprite icon;
bool skippable = true;
ObjectiveCheck@ check;
Objective() {
id = -1;
}
Objective(bool empty) {
if(!empty)
error("Empty objectives must be empty");
id = nextObjectiveId++;
}
Objective(const string& ident) {
id = nextObjectiveId++;
this.title = localize("#"+ident+"_TITLE");
this.text = localize("#"+ident+"_TEXT");
}
void write(Message& msg) {
msg << id;
msg << title << text;
msg << getSpriteDesc(icon);
msg << skippable;
}
void read(Message& msg) {
msg >> id;
msg >> title >> text;
string desc;
msg >> desc;
icon = getSprite(desc);
msg >> skippable;
}
};
class ObjectiveCheck {
bool start() {
return true;
}
bool check() {
return false;
}
void end() {
}
};
class GuiObjectiveCheck : ObjectiveCheck {
string id;
Object@ obj;
};
class GUIChecker : ObjectiveCheck {
string id;
bool completed;
Object@ obj;
GUIChecker(const string& id, Object@ obj = null) {
this.id = id;
@this.obj = obj;
}
#section server
bool start() {
guiObjectiveStart(CURRENT_PLAYER, id, obj);
completed = false;
{
Lock lck(actMtx);
remoteCheckers.insertLast(this);
}
return true;
}
bool check() {
return completed;
}
void end() {
guiObjectiveEnd(CURRENT_PLAYER, id);
{
Lock lck(actMtx);
remoteCheckers.remove(this);
}
}
#section all
};