3 #include <BLIB/Events/EventWaiter.hpp>
4 #include <BLIB/Scripts.hpp>
5 #include <BLIB/Util/Waiter.hpp>
21 using bl::script::Error;
22 using bl::script::Function;
23 using bl::script::PrimitiveValue;
24 using bl::script::SymbolTable;
25 using bl::script::Value;
32 typedef void (*Builtin)(system::Systems& systems, SymbolTable& table,
33 const std::vector<Value>& args, Value& result);
35 void getPlayer(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
37 void giveItem(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
39 void giveMoney(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
41 void takeItem(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
43 void takeMoney(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
45 void givePeoplemon(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
47 void takePeoplemon(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
49 void whiteout(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
51 void restorePeoplemon(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
54 void displayMessage(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
56 void promptPlayer(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
58 void rollCredits(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
60 void openStorage(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
63 void pricedItem(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
65 void sellPriceOverride(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
67 void openStore(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
70 void getNpc(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
72 void getTrainer(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
74 void loadCharacter(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
76 void spawnCharacter(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
79 void spawnGenericEntity(system::Systems& systems, SymbolTable& table,
80 const std::vector<Value>& args, Value& result);
81 void spawnAnimation(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
83 void triggerEntityAnimation(system::Systems& systems, SymbolTable& table,
84 const std::vector<Value>& args, Value& result);
86 void moveEntity(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
88 void rotateEntity(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
90 void faceEntity(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
92 void faceDirection(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
94 void removeEntity(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
96 void entityToPosition(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
98 void entityInteract(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
100 void setEntityLock(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
102 void resetEntityLock(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
105 void makeTime(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
107 void getClock(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
109 void waitUntilTime(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
111 void runAtClockTime(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
114 void addSaveEntry(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
116 void getSaveEntry(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
118 void checkConvFlag(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
120 void setConvFlag(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
123 void loadMap(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
125 void setAmbientLight(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
127 void createLight(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
129 void updateLight(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
131 void removeLight(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
133 void visitTown(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
136 void clearWeather(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
138 void makeRain(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
140 void makeSnow(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
142 void makeSunny(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
144 void makeSandstorm(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
146 void makeFog(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
148 void makeRandomRain(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
150 void makeRandomSnow(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
152 void makeRandomDesert(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
154 void makeRandomWeather(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
156 void getCurrentWeather(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
159 Value bind(system::Systems& systems, Builtin func) {
160 return {Function([&systems, func](SymbolTable& table,
161 const std::vector<Value>& args,
162 Value& result) { (*func)(systems, table, args, result); })};
168 #define BUILTIN(function) table.set(#function, bind(systems, &function))
206 BUILTIN(triggerEntityAnimation);
243 coord.setProperty(
"x", {pos.position.x});
244 coord.setProperty(
"y", {pos.position.y});
245 value.setProperty(
"tiles", coord);
248 value.setProperty(
"pixels", coord);
249 value.setProperty(
"level", {pos.level});
250 value.setProperty(
"direction", {bl::tmap::directionToString(pos.direction)});
257 const bl::tmap::Position* pos = systems.
engine().ecs().getComponent<bl::tmap::Position>(e);
259 BL_LOG_WARN <<
"Entity " << e <<
" has no position";
263 void getPlayer(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value& player) {
264 player = systems.player().player();
265 player.setProperty(
"name", {systems.player().state().name});
266 player.setProperty(
"gender",
268 player.setProperty(
"money", {systems.player().state().monei});
269 player.setProperty(
"position", makePosition(systems, systems.player().player()));
271 std::vector<player::Bag::Item> items;
272 systems.player().state().bag.getAll(items);
273 player.setProperty(
"bag", {bl::script::ArrayValue{}});
274 auto& bag = player.getProperty(
"bag",
false).deref().value().getAsArray();
275 bag.reserve(items.size());
276 for (
const player::Bag::Item& item : items) {
277 bag.emplace_back(item.id);
278 bag.back().setProperty(
"id", {item.id});
280 bag.back().setProperty(
"qty", {item.qty});
284 void giveItem(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
286 Value::validateArgs<PrimitiveValue::TInteger,
287 PrimitiveValue::TInteger,
288 PrimitiveValue::TBool,
289 PrimitiveValue::TBool>(
"giveItem", args);
291 const unsigned int rawId =
static_cast<unsigned int>(args[0].value().getAsInt());
294 const long qty = args[1].value().getAsInt();
296 systems.player().state().bag.addItem(item, qty);
297 if (args[2].value().getAsBool()) {
298 bl::util::Waiter waiter;
300 if (args[3].value().getAsBool())
301 unlock = [&waiter](
const std::string&) { waiter.unblock(); };
303 const std::string msg = qty > 1 ? (
"Received " + std::to_string(qty) +
" " +
306 systems.hud().displayMessage(msg, unlock);
307 if (args[3].value().getAsBool()) table.waitOn(waiter);
310 else { BL_LOG_WARN <<
"qty must be a positive integer"; }
312 else { BL_LOG_WARN <<
"Unknown item id: " << rawId; }
315 void giveMoney(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
317 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TBool, PrimitiveValue::TBool>(
320 const long money = args[0].value().getAsInt();
322 systems.player().state().monei += money;
323 if (args[1].value().getAsBool()) {
324 bl::util::Waiter waiter;
326 if (args[2].value().getAsBool())
327 unlock = [&waiter](
const std::string&) { waiter.unblock(); };
329 const std::string msg =
"Received " + std::to_string(money) +
" monies";
330 systems.hud().displayMessage(msg, unlock);
331 if (args[2].value().getAsBool()) table.waitOn(waiter);
334 else { BL_LOG_WARN <<
"qty must be a positive integer"; }
337 void takeItem(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
339 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger, PrimitiveValue::TBool>(
342 const unsigned int rawId =
static_cast<unsigned int>(args[0].value().getAsInt());
345 const long qty = args[1].value().getAsInt();
347 if (args[2].value().getAsBool()) {
348 bl::util::Waiter waiter;
355 const std::string msg =
359 systems.hud().promptUser(msg, {
"Yes",
"No"}, unlock);
360 table.waitOn(waiter);
361 if (choice ==
"No") { result =
false; }
363 else { result = systems.player().state().bag.removeItem(item, qty); }
366 BL_LOG_WARN <<
"qty must be a positive integer";
371 BL_LOG_WARN <<
"Unknown item id: " << rawId;
376 void takeMoney(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
378 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TBool>(
"takeMoney", args);
380 const long qty = args[0].value().getAsInt();
382 if (args[1].value().getAsBool()) {
383 bl::util::Waiter waiter;
390 const std::string msg =
"Give " + std::to_string(qty) +
" monies?";
391 systems.hud().promptUser(msg, {
"Yes",
"No"}, unlock);
392 table.waitOn(waiter);
393 if (choice ==
"No") { result =
false; }
395 if (systems.player().state().monei >= qty) {
396 systems.player().state().monei -= qty;
399 else { result =
false; }
402 BL_LOG_WARN <<
"qty must be a positive integer";
407 void givePeoplemon(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
409 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"givePeoplemon", args);
413 BL_LOG_ERROR <<
"Bad peoplemon id: " << args[0].value().getAsInt();
418 if (systems.player().state().peoplemon.size() < 6) {
419 systems.player().state().peoplemon.emplace_back(
id, args[1].value().getAsInt());
423 systems.player().state().storage.add(
424 {id,
static_cast<unsigned int>(args[1].value().getAsInt())});
427 systems.player().state().peopledex.registerSighting(
428 id, systems.world().activeMap().getLocationName(systems.player().position()));
433 void takePeoplemon(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
435 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"takePeoplemon", args);
439 BL_LOG_ERROR <<
"Bad peoplemon id: " << args[0].value().getAsInt();
444 const unsigned int level = args[1].value().getAsInt();
445 for (
auto it = systems.player().state().peoplemon.begin();
446 it != systems.player().state().peoplemon.end();
448 if (it->id() ==
id && it->currentLevel() >= level) {
450 systems.player().state().peoplemon.erase(it);
458 void whiteout(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value& result) {
459 systems.player().whiteout();
463 void restorePeoplemon(system::Systems& systems, SymbolTable&,
const std::vector<Value>&,
465 systems.player().state().healPeoplemon();
469 void displayMessage(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
471 Value::validateArgs<PrimitiveValue::TString, PrimitiveValue::TBool>(
"displayMessage", args);
473 bl::util::Waiter waiter;
475 if (args[1].value().getAsBool()) unlock = [&waiter](
const std::string&) { waiter.unblock(); };
477 systems.hud().displayMessage(args[0].value().getAsString(), unlock);
478 if (args[1].value().getAsBool()) table.waitOn(waiter);
481 void promptPlayer(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
483 Value::validateArgs<PrimitiveValue::TString, PrimitiveValue::TArray>(
"promptPlayer", args);
485 const auto& rawChoices = args[1].value().getAsArray();
486 std::vector<std::string> choices;
487 choices.reserve(rawChoices.size());
488 for (
const Value& val : rawChoices) {
489 if (val.value().getType() != PrimitiveValue::TString) {
490 throw Error(
"All choices in promptPlayer() must be String type");
492 choices.emplace_back(val.value().getAsString());
495 bl::util::Waiter waiter;
497 const auto cb = [&waiter, &choice](
const std::string& c) {
501 systems.hud().promptUser(args[0].value().getAsString(), choices, cb);
502 table.waitOn(waiter);
507 void rollCredits(system::Systems&, SymbolTable&,
const std::vector<Value>&, Value& result) {
512 void openStorage(system::Systems&, SymbolTable&,
const std::vector<Value>& args, Value&) {
513 Value::validateArgs<PrimitiveValue::TBool>(
"openStorage", args);
515 const bool block = args[0].value().getAsBool();
516 bl::event::Dispatcher::dispatch<event::StorageSystemOpened>({});
518 bl::event::EventWaiter<event::StorageSystemClosed> waiter;
523 void pricedItem(system::Systems&, SymbolTable&,
const std::vector<Value>& args, Value& result) {
524 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"pricedItem", args);
526 result.setProperty(
"price", args[1]);
529 void sellPriceOverride(system::Systems&, SymbolTable&,
const std::vector<Value>& args,
531 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"sellPriceOverride",
534 result.setProperty(
"price", args[1]);
537 void openStore(system::Systems&, SymbolTable&,
const std::vector<Value>& args, Value&) {
538 Value::validateArgs<PrimitiveValue::TArray, PrimitiveValue::TArray, PrimitiveValue::TBool>(
541 std::vector<std::pair<item::Id, int>> items;
542 const bool block = args[2].value().getAsBool();
543 const auto& argItems = args[0].value().getAsArray();
544 items.reserve(argItems.size());
545 for (
const auto& item : argItems) {
546 if (item.value().getType() != PrimitiveValue::TInteger) {
547 throw Error(
"openStore: Item list must be an array of integer ids");
551 throw Error(
"openStore: Invalid item id: " + std::to_string(item.value().getAsInt()));
553 const auto it = item.allProperties().find(
"price");
554 const auto getPrice = [it]() ->
int {
555 const auto& pv = it->second.deref().value();
556 if (pv.getType() != PrimitiveValue::TInteger) {
557 throw Error(
"openStore: Item prices must be integer values");
559 return pv.getAsInt();
562 items.emplace_back(
id, price);
565 std::vector<std::pair<item::Id, int>> sellPrices;
566 const auto& sellPriceArg = args[1].value().getAsArray();
567 sellPrices.reserve(sellPriceArg.size());
568 for (
const auto& price : sellPriceArg) {
569 if (price.value().getType() != PrimitiveValue::TInteger) {
570 throw Error(
"openStore: Item list must be an array of integer ids");
574 throw Error(
"openStore: Invalid item id: " + std::to_string(price.value().getAsInt()));
576 const auto it = price.allProperties().find(
"price");
577 const auto getPrice = [it]() ->
int {
578 const auto& pv = it->second.deref().value();
579 if (pv.getType() != PrimitiveValue::TInteger) {
580 throw Error(
"openStore: Item prices must be integer values");
582 return pv.getAsInt();
585 sellPrices.emplace_back(
id, sp);
588 bl::event::Dispatcher::dispatch<event::StoreOpened>({items, sellPrices});
590 bl::event::EventWaiter<event::StoreClosed> waiter;
595 void getNpc(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value& npc) {
596 Value::validateArgs<PrimitiveValue::TString>(
"getNpc", args);
598 const std::string name = args[0].value().getAsString();
600 const auto visitor = [&name, &npc, &systems, &found](bl::ecs::Entity ent,
601 component::NPC& component) {
603 bl::tmap::Position* pos = systems.engine().ecs().getComponent<bl::tmap::Position>(ent);
606 if (component.name() == name) {
608 npc.setProperty(
"name", {name});
609 npc.setProperty(
"talkedTo", {systems.interaction().npcTalkedTo(name)});
610 npc.setProperty(
"defeated", {
false});
616 systems.engine().ecs().getAllComponents<component::NPC>().forEach(visitor);
617 if (!found) { npc =
false; }
620 void loadCharacter(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
622 Value::validateArgs<PrimitiveValue::TInteger>(
"loadCharacter", args);
624 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
625 if (systems.engine().ecs().entityExists(entity)) {
626 const bl::tmap::Position* pos =
627 systems.engine().ecs().getComponent<bl::tmap::Position>(entity);
632 const component::NPC* npc = systems.engine().ecs().getComponent<component::NPC>(entity);
634 character.setProperty(
"name", {npc->name()});
635 character.setProperty(
"talkedTo", {systems.interaction().npcTalkedTo(npc->name())});
636 character.setProperty(
"defeated", {
false});
640 const component::Trainer* trainer =
641 systems.engine().ecs().getComponent<component::Trainer>(entity);
643 character.setProperty(
"name", {trainer->name()});
644 character.setProperty(
"talkedTo",
645 {systems.interaction().trainerTalkedto(trainer->name())});
646 character.setProperty(
"defeated", {systems.trainers().trainerDefeated(*trainer)});
655 void spawnCharacter(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
657 Value::validateArgs<PrimitiveValue::TString,
658 PrimitiveValue::TInteger,
659 PrimitiveValue::TInteger,
660 PrimitiveValue::TInteger,
661 PrimitiveValue::TString>(
"spawnCharacter", args);
663 const map::CharacterSpawn spawn(
664 bl::tmap::Position(args[1].value().getAsInt(),
665 {
static_cast<int>(args[2].value().getAsInt()),
666 static_cast<int>(args[3].value().getAsInt())},
667 bl::tmap::directionFromString(args[4].value().getAsString())),
668 args[0].value().getAsString());
669 result = systems.entity().spawnCharacter(spawn, systems.world().activeMap());
672 void getTrainer(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
674 Value::validateArgs<PrimitiveValue::TString>(
"getTrainer", args);
676 const std::string name = args[0].value().getAsString();
679 const auto visitor = [&systems, &found, &name, &trainer](bl::ecs::Entity ent,
680 component::Trainer& tc) {
682 bl::tmap::Position* pos = systems.engine().ecs().getComponent<bl::tmap::Position>(ent);
684 if (tc.name() == name) {
686 trainer.setProperty(
"name", {name});
687 trainer.setProperty(
"talkedTo", {systems.interaction().trainerTalkedto(name)});
688 trainer.setProperty(
"defeated", {systems.trainers().trainerDefeated(tc)});
694 systems.engine().ecs().getAllComponents<component::Trainer>().forEach(visitor);
695 if (!found) { trainer =
false; }
698 void moveEntity(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
700 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TString, PrimitiveValue::TBool>(
703 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
704 const bl::tmap::Direction dir = bl::tmap::directionFromString(args[1].value().getAsString());
705 const bool block = args[2].value().getAsBool();
706 const bool result = systems.movement().moveEntity(entity, dir,
false);
708 if (block && result) {
709 const component::Movable* move =
710 systems.engine().ecs().getComponent<component::Movable>(entity);
715 while (move->moving()) {
716 sf::sleep(sf::milliseconds(10));
719 BL_LOG_WARN <<
"Blocking on moveEntity for entity " << entity
720 <<
" taking too long (" << t <<
"s), continuing";
730 void rotateEntity(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
731 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TString>(
"rotateEntity", args);
733 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
734 const bl::tmap::Direction dir = bl::tmap::directionFromString(args[1].value().getAsString());
735 bl::tmap::Position* pos = systems.engine().ecs().getComponent<bl::tmap::Position>(entity);
736 if (pos && pos->direction != dir) pos->direction = dir;
739 void faceEntity(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
740 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"faceEntity", args);
742 const bl::ecs::Entity toSpin =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
743 const bl::ecs::Entity toFace =
static_cast<bl::ecs::Entity
>(args[1].value().getAsInt());
744 bl::tmap::Position* sp = systems.engine().ecs().getComponent<bl::tmap::Position>(toSpin);
745 bl::tmap::Position* fp = systems.engine().ecs().getComponent<bl::tmap::Position>(toSpin);
748 const bl::tmap::Direction d = bl::tmap::Position::facePosition(*sp, *fp);
749 if (sp->direction != d) { systems.movement().moveEntity(toSpin, d,
false); }
751 else { BL_LOG_ERROR <<
"Invalid entities. Tried to make " << toSpin <<
" face " << toFace; }
754 void faceDirection(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
756 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"faceDirection", args);
758 const bl::ecs::Entity toSpin =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
759 const bl::ecs::Entity toFace =
static_cast<bl::ecs::Entity
>(args[1].value().getAsInt());
760 bl::tmap::Position* sp = systems.engine().ecs().getComponent<bl::tmap::Position>(toSpin);
761 bl::tmap::Position* fp = systems.engine().ecs().getComponent<bl::tmap::Position>(toSpin);
764 const bl::tmap::Direction d = bl::tmap::Position::facePosition(*sp, *fp);
765 result = bl::tmap::directionToString(d);
768 BL_LOG_ERROR <<
"Invalid entities. Tried to get direction from " << toSpin <<
" to "
774 void removeEntity(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
776 Value::validateArgs<PrimitiveValue::TInteger>(
"removeEntity", args);
778 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
779 if (entity != systems.player().player()) {
780 const bool result = systems.engine().ecs().entityExists(entity);
781 systems.engine().ecs().destroyEntity(entity);
784 else { res =
false; }
787 void entityToPosition(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
789 Value::validateArgs<PrimitiveValue::TInteger,
790 PrimitiveValue::TInteger,
791 PrimitiveValue::TInteger,
792 PrimitiveValue::TInteger,
793 PrimitiveValue::TString,
794 PrimitiveValue::TBool>(
"entityToPosition", args);
796 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
797 const std::uint8_t level = args[1].value().getAsInt();
798 const glm::i32vec2 destTiles(args[2].value().getAsInt(), args[3].value().getAsInt());
799 const bl::tmap::Direction dir = bl::tmap::directionFromString(args[4].value().getAsString());
800 const bool block = args[5].value().getAsBool();
801 const bl::tmap::Position dest(level, destTiles, dir);
802 BL_LOG_INFO <<
"Moving entity " << entity <<
" to " << dest;
804 if (!systems.ai().moveToPosition(entity, dest)) {
806 BL_LOG_ERROR <<
"Failed to path find entity " << entity <<
"to " << dest;
813 bl::event::EventWaiter<event::PathFindCompleted> eventWaiter;
815 while (!bl::util::Waiter::allUnblocked()) {
816 std::optional<event::PathFindCompleted> pathEvent = eventWaiter.wait();
817 if (pathEvent.has_value() && pathEvent.value().entity == entity) {
818 result = pathEvent.value().success;
825 void entityInteract(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
827 Value::validateArgs<PrimitiveValue::TInteger>(
"entityInteract", args);
829 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
830 result = systems.interaction().interact(entity);
833 void setEntityLock(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
834 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TBool>(
"setEntityLock", args);
836 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
837 const bool lock = args[1].value().getAsBool();
838 systems.controllable().setEntityLocked(entity, lock,
true);
841 void resetEntityLock(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
843 Value::validateArgs<PrimitiveValue::TInteger>(
"resetEntityLock", args);
845 const bl::ecs::Entity entity =
static_cast<bl::ecs::Entity
>(args[0].value().getAsInt());
846 systems.controllable().resetEntityLock(entity);
849 void spawnAnimation(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
851 Value::validateArgs<PrimitiveValue::TInteger,
852 PrimitiveValue::TInteger,
853 PrimitiveValue::TInteger,
854 PrimitiveValue::TNumeric,
855 PrimitiveValue::TNumeric,
856 PrimitiveValue::TString>(
"spawnAnimation", args);
858 const std::uint8_t level = args[0].value().getAsInt();
859 glm::vec2 pos{args[1].value().getNumAsFloat(), args[2].value().getNumAsFloat()};
860 glm::vec2 offset{args[3].value().getNumAsFloat(), args[4].value().getNumAsFloat()};
861 result = systems.entity().spawnAnimation(level,
864 args[3].value().getAsString(),
865 systems.world().activeMap());
868 void spawnGenericEntity(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
870 Value::validateArgs<PrimitiveValue::TInteger,
871 PrimitiveValue::TInteger,
872 PrimitiveValue::TInteger,
873 PrimitiveValue::TString,
874 PrimitiveValue::TBool>(
"spawnGenericEntity", args);
876 const std::uint8_t level = args[0].value().getAsInt();
877 const glm::i32vec2 pos{
static_cast<int>(args[1].value().getAsInt()),
878 static_cast<int>(args[2].value().getAsInt())};
879 result = systems.entity().spawnGeneric(level,
881 args[4].value().getAsBool(),
882 args[3].value().getAsString(),
883 systems.world().activeMap());
886 void triggerEntityAnimation(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
888 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TBool, PrimitiveValue::TBool>(
889 "triggerEntityAnimation", args);
891 const bl::ecs::Entity e = args[0].value().getAsInt();
892 component::Renderable* r = systems.engine().ecs().getComponent<component::Renderable>(e);
895 BL_LOG_WARN <<
"Could not trigger animation for invalid entity: " << e;
900 const bool loop = args[1].value().getAsBool();
901 r->triggerAnim(loop);
902 if (args[2].value().getAsBool() && !loop) { sf::sleep(sf::seconds(r->animLength())); }
906 void getClock(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value& clock) {
907 const system::Clock::Time now = systems.clock().now();
908 clock = now.hour * 60 + now.minute;
909 clock.setProperty(
"minute", {now.minute});
910 clock.setProperty(
"hour", {now.hour});
911 clock.setProperty(
"day", {now.day});
914 class ClockTrigger :
public bl::event::Listener<event::TimeChange> {
916 using Callback = std::function<void()>;
917 ClockTrigger(Callback cb,
const system::Clock::Time& time)
921 virtual void observe(
const event::TimeChange& event)
override {
922 if (event.newTime.hour == time.hour && event.newTime.minute == time.minute) cb();
926 const system::Clock::Time time;
930 system::Clock::Time parseTime(
const Value& val) {
931 system::Clock::Time t(12, 0, 0);
932 const PrimitiveValue& h = val.getProperty(
"hour",
false).deref().value();
933 t.hour =
static_cast<unsigned int>(h.getNumAsInt());
934 const PrimitiveValue& m = val.getProperty(
"minute",
false).deref().value();
935 t.minute =
static_cast<unsigned int>(m.getNumAsInt());
939 void makeTime(system::Systems&, SymbolTable&,
const std::vector<Value>& args, Value& time) {
940 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger>(
"makeTime", args);
941 if (args[0].value().getAsInt() < 0 || args[0].value().getAsInt() > 23)
942 throw Error(
"hour must be in range [0, 23]");
943 if (args[1].value().getAsInt() < 0 || args[1].value().getAsInt() > 59)
944 throw Error(
"minute must be in range [0, 59]");
946 const unsigned int hour =
static_cast<unsigned int>(args[0].value().getAsInt());
947 const unsigned int minute =
static_cast<unsigned int>(args[1].value().getAsInt());
948 time = hour * 60 + minute;
949 time.setProperty(
"hour", {hour});
950 time.setProperty(
"minute", {minute});
953 void waitUntilTime(system::Systems& systems, SymbolTable& table,
const std::vector<Value>& args,
955 Value::validateArgs<PrimitiveValue::TAny, PrimitiveValue::TBool>(
"waitUntilTime", args);
957 const system::Clock::Time now = systems.clock().now();
958 const system::Clock::Time then = parseTime(args[0]);
959 const unsigned int nts = now.hour * 60 + now.minute;
960 const unsigned int ets = then.hour * 60 + then.minute;
961 if (nts < ets || (nts != ets && args[1].value().getAsBool())) {
962 bl::util::Waiter waiter;
963 const auto unlock = [&waiter]() { waiter.unblock(); };
964 ClockTrigger trigger(unlock, then);
965 bl::event::ListenerGuard<event::TimeChange> guard(&trigger);
967 table.waitOn(waiter);
971 void runAtClockTime(system::Systems&, SymbolTable& table,
const std::vector<Value>& args, Value&) {
972 Value::validateArgs<PrimitiveValue::TAny, PrimitiveValue::TInteger>(
"runAtClockTime", args);
974 const std::string source = args[0].value().getAsString();
975 const system::Clock::Time time = parseTime(args[1]);
978 const std::string program =
"waitUntilTime(makeTime(" + std::to_string(time.hour) +
", " +
979 std::to_string(time.minute) +
"), true);\nrun(\"" + source +
982 bl::script::Script script(program, table);
983 if (!script.valid())
throw Error(
"Syntax error in script passed to runAtClockTime()");
984 script.runBackground(table.manager());
987 void addSaveEntry(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
989 Value::validateArgs<PrimitiveValue::TString, EntryTypes>(
"addSaveEntry", args);
990 systems.scripts().setEntry(args[0].value().getAsString(), args[1].value());
994 void getSaveEntry(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
996 Value::validateArgs<PrimitiveValue::TString>(
"getSaveEntry", args);
997 const Value* val = systems.scripts().getEntry(args[0].value().getAsString());
998 result = val ? *val :
false;
1001 void loadMap(system::Systems&, SymbolTable&,
const std::vector<Value>& args, Value&) {
1002 Value::validateArgs<PrimitiveValue::TString, PrimitiveValue::TInteger>(
"loadMap", args);
1003 bl::event::Dispatcher::dispatch<event::SwitchMapTriggered>(
1004 {args[0].value().getAsString(),
static_cast<int>(args[1].value().getAsInt())});
1007 void checkConvFlag(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
1009 Value::validateArgs<PrimitiveValue::TString>(
"checkConvFlag", args);
1010 result = systems.interaction().flagSet(args[0].value().getAsString());
1013 void setConvFlag(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1014 Value::validateArgs<PrimitiveValue::TString>(
"setConvFlag", args);
1015 systems.interaction().setFlag(args[0].value().getAsString());
1018 void setAmbientLight(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
1020 Value::validateArgs<PrimitiveValue::TInteger, PrimitiveValue::TInteger, PrimitiveValue::TBool>(
1021 "setAmbientLight", args);
1022 if (args[0].value().getAsInt() < 0) {
throw Error(
"Light level must be positive"); }
1023 if (args[0].value().getAsInt() > 255) {
throw Error(
"Light level must be under 255"); }
1025 const std::uint8_t low =
static_cast<std::uint8_t
>(args[0].value().getAsInt());
1026 const std::uint8_t high =
static_cast<std::uint8_t
>(args[1].value().getAsInt());
1027 const bool sunlight = args[2].value().getAsBool();
1028 systems.world().activeMap().lightingSystem().setAmbientLevel(low, high);
1029 systems.world().activeMap().lightingSystem().adjustForSunlight(sunlight);
1032 void createLight(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args,
1034 Value::validateArgs<PrimitiveValue::TNumeric,
1035 PrimitiveValue::TNumeric,
1036 PrimitiveValue::TNumeric>(
"createLight", args);
1038 const float x = args[0].value().getNumAsFloat();
1039 const float y = args[1].value().getNumAsFloat();
1040 const float r = args[2].value().getNumAsFloat();
1042 if (x < 0.f || x >= systems.world().activeMap().sizePixels().x) {
1043 throw Error(
"Light x coordinate is out of range: " + std::to_string(x));
1045 if (y < 0.f || y >= systems.world().activeMap().sizePixels().y) {
1046 throw Error(
"Light y coordinate is out of range: " + std::to_string(y));
1048 if (r < 0.f) {
throw Error(
"Light radius must be positive"); }
1050 const bl::rc::lgt::Light2D handle =
1051 systems.world().activeMap().getSceneLighting().addLight({x, y}, r);
1053 result = handle.getId();
1056 void updateLight(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1057 Value::validateArgs<PrimitiveValue::TInteger,
1058 PrimitiveValue::TNumeric,
1059 PrimitiveValue::TNumeric,
1060 PrimitiveValue::TNumeric>(
"updateLight", args);
1062 const std::uint32_t
id =
static_cast<std::uint32_t
>(args[0].value().getAsInt());
1063 const float x = args[1].value().getNumAsFloat();
1064 const float y = args[2].value().getNumAsFloat();
1065 const float r = args[3].value().getNumAsFloat();
1067 if (x < 0.f || x >= systems.world().activeMap().sizePixels().x) {
1068 throw Error(
"Light x coordinate is out of range: " + std::to_string(x));
1070 if (y < 0.f || y >= systems.world().activeMap().sizePixels().y) {
1071 throw Error(
"Light y coordinate is out of range: " + std::to_string(y));
1073 if (r < 0.f) {
throw Error(
"Light radius must be positive"); }
1075 bl::rc::lgt::Light2D light = systems.world().activeMap().getSceneLighting().getLight(
id);
1076 if (!light.isValid()) {
1077 BL_LOG_ERROR <<
"Cannot update invalid light: " << id;
1080 light.setPosition({x, y});
1084 void removeLight(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1085 Value::validateArgs<PrimitiveValue::TInteger>(
"removeLight", args);
1086 const std::uint32_t
id =
static_cast<std::uint32_t
>(args[0].value().getAsInt());
1087 bl::rc::lgt::Light2D light = systems.world().activeMap().getSceneLighting().getLight(
id);
1088 if (!light.isValid()) {
1089 BL_LOG_ERROR <<
"Cannot remove invalid light: " << id;
1092 light.removeFromScene();
1095 void visitTown(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1096 Value::validateArgs<PrimitiveValue::TString>(
"visitTown", args);
1097 systems.player().state().visitedTowns.emplace(args[0].value().getAsString());
1100 void clearWeather(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1104 void makeRain(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1105 Value::validateArgs<PrimitiveValue::TBool, PrimitiveValue::TBool>(
"makeRain", args);
1108 if (args[0].value().getAsBool()) {
1109 if (args[1].value().getAsBool())
1115 if (args[1].value().getAsBool())
1121 systems.world().activeMap().weatherSystem().set(t);
1124 void makeSnow(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1125 Value::validateArgs<PrimitiveValue::TBool, PrimitiveValue::TBool>(
"makeSnow", args);
1128 if (args[0].value().getAsBool()) {
1129 if (args[1].value().getAsBool())
1135 if (args[1].value().getAsBool())
1141 systems.world().activeMap().weatherSystem().set(t);
1144 void makeSunny(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1148 void makeSandstorm(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1152 void makeFog(system::Systems& systems, SymbolTable&,
const std::vector<Value>& args, Value&) {
1153 Value::validateArgs<PrimitiveValue::TBool>(
"makeFog", args);
1157 systems.world().activeMap().weatherSystem().set(t);
1160 void makeRandomRain(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1164 void makeRandomSnow(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1168 void makeRandomDesert(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1172 void makeRandomWeather(system::Systems& systems, SymbolTable&,
const std::vector<Value>&, Value&) {
1176 void getCurrentWeather(system::Systems& systems, SymbolTable&,
const std::vector<Value>&,
1180 switch (systems.world().activeMap().weatherSystem().getType()) {
1182 case T::LightRainThunder:
1184 case T::HardRainThunder:
1188 case T::LightSnowThunder:
1190 case T::HardSnowThunder:
1201 result =
"sandstorm";
#define BUILTIN(function)
Type
The type classification of an item. This is used to determine when an item may be used and how to use...
Id
Represents an item in its simplist form.
Core classes and functionality for both the editor and game.
static const std::string & getName(item::Id item)
Returns the name of the given item.
static int getValue(item::Id item)
Returns the value of the given item.
static Id cast(unsigned int id)
Helper function to cast a raw id to an item Id.
@ HardSnowThunder
Hard snow with thunder.
@ Sunny
A very sunny day with pulsating light.
@ ThinFog
Thin fog covers the area.
@ SandStorm
A sandstorm ravages you.
@ HardRain
Hard rain with no thunder.
@ HardRainThunder
Hard rain with thunder.
@ LightRainThunder
Light rain with thunder.
@ None
No weather will occur.
@ WaterRandom
Periodically triggers one of LightRain, HardRain, LightRainThunder, HardRainThunder.
@ LightSnowThunder
Light snow with thunder.
@ LightRain
Light rain with no thunder.
@ SnowRandom
Periodically triggers one of LightSnow, HardSnow, LightSnowThunder, HardSnowThunder.
@ ThickFog
Thick fog obscures everything.
@ LightSnow
Light snow with no thunder.
@ DesertRandom
Periodically triggers one of Sunny, Sandstorm.
@ HardSnow
Hard snow with no thunder.
@ AllRandom
All types of weather may occur over time.
static Id cast(unsigned int id)
Casts an integer to a validated id. Returns Unknown if the id is invalid.
static int PixelsPerTile()
static float CharacterMoveSpeed()
static bl::script::Value makePosition(const bl::tmap::Position &pos)
Helper function for other script functions to make script position values from the position component...
static void addDefaults(bl::script::SymbolTable &table, system::Systems &systems)
Adds the universal built-in functions to the given symbol table.
std::function< void(const std::string &value)> Callback
Signature for HUD callbacks. Used for both messages completing and choices made.
Owns all primary systems and a reference to the engine.
const bl::engine::Engine & engine() const
Const accessor for the Engine.