#include "StarPlayerLuaBindings.hpp" #include "StarClientContext.hpp" #include "StarItem.hpp" #include "StarItemDatabase.hpp" #include "StarPlayer.hpp" #include "StarPlayerInventory.hpp" #include "StarPlayerTech.hpp" #include "StarPlayerLog.hpp" #include "StarQuestManager.hpp" #include "StarWarping.hpp" #include "StarStatistics.hpp" #include "StarPlayerUniverseMap.hpp" #include "StarJsonExtra.hpp" namespace Star { LuaCallbacks LuaBindings::makePlayerCallbacks(Player* player) { LuaCallbacks callbacks; callbacks.registerCallback("save", [player]() { return player->diskStore(); }); callbacks.registerCallback("load", [player](Json const& data) { auto saved = player->diskStore(); try { player->diskLoad(data); } catch (StarException const&) { player->diskLoad(saved); throw; } }); callbacks.registerCallback( "humanoidIdentity", [player]() { return player->humanoid()->identity().toJson(); }); callbacks.registerCallback("setHumanoidIdentity", [player](Json const& id) { player->setIdentity(HumanoidIdentity(id)); }); callbacks.registerCallback( "bodyDirectives", [player]() { return player->identity().bodyDirectives; }); callbacks.registerCallback("setBodyDirectives", [player](String const& str) { player->setBodyDirectives(str); }); callbacks.registerCallback( "emoteDirectives", [player]() { return player->identity().emoteDirectives; }); callbacks.registerCallback("setEmoteDirectives", [player](String const& str) { player->setEmoteDirectives(str); }); callbacks.registerCallback( "hairGroup", [player]() { return player->identity().hairGroup; }); callbacks.registerCallback("setHairGroup", [player](String const& str) { player->setHairGroup(str); }); callbacks.registerCallback( "hairType", [player]() { return player->identity().hairType; }); callbacks.registerCallback("setHairType", [player](String const& str) { player->setHairType(str); }); callbacks.registerCallback( "hairDirectives", [player]() { return player->identity().hairDirectives; }); callbacks.registerCallback("setHairDirectives", [player](String const& str) { player->setHairDirectives(str); }); callbacks.registerCallback( "facialHairGroup", [player]() { return player->identity().facialHairGroup; }); callbacks.registerCallback("setFacialHairGroup", [player](String const& str) { player->setFacialHairGroup(str); }); callbacks.registerCallback( "facialHairType", [player]() { return player->identity().facialHairType; }); callbacks.registerCallback("setFacialHairType", [player](String const& str) { player->setFacialHairType(str); }); callbacks.registerCallback( "facialHairDirectives", [player]() { return player->identity().facialHairDirectives; }); callbacks.registerCallback("setFacialHairDirectives", [player](String const& str) { player->setFacialHairDirectives(str); }); callbacks.registerCallback("hair", [player]() { HumanoidIdentity const& identity = player->identity(); return luaTupleReturn(identity.hairGroup, identity.hairType, identity.hairDirectives); }); callbacks.registerCallback("facialHair", [player]() { HumanoidIdentity const& identity = player->identity(); return luaTupleReturn(identity.facialHairGroup, identity.facialHairType, identity.facialHairDirectives); }); callbacks.registerCallback("facialMask", [player]() { HumanoidIdentity const& identity = player->identity(); return luaTupleReturn(identity.facialMaskGroup, identity.facialMaskType, identity.facialMaskDirectives); }); callbacks.registerCallback("setFacialHair", [player](Maybe const& group, Maybe const& type, Maybe const& directives) { if (group && type && directives) player->setFacialHair(*group, *type, *directives); else { if (group) player->setFacialHairGroup(*group); if (type) player->setFacialHairType(*type); if (directives) player->setFacialHairDirectives(*directives); } }); callbacks.registerCallback("setFacialMask", [player](Maybe const& group, Maybe const& type, Maybe const& directives) { if (group && type && directives) player->setFacialMask(*group, *type, *directives); else { if (group) player->setFacialMaskGroup(*group); if (type) player->setFacialMaskType(*type); if (directives) player->setFacialMaskDirectives(*directives); } }); callbacks.registerCallback("setHair", [player](Maybe const& group, Maybe const& type, Maybe const& directives) { if (group && type && directives) player->setHair(*group, *type, *directives); else { if (group) player->setHairGroup(*group); if (type) player->setHairType(*type); if (directives) player->setHairDirectives(*directives); } }); callbacks.registerCallback( "description", [player]() { return player->description(); }); callbacks.registerCallback("setDescription", [player](String const& description) { player->setDescription(description); }); callbacks.registerCallback( "name", [player]() { return player->name(); }); callbacks.registerCallback("setName", [player](String const& name) { player->setName(name); }); callbacks.registerCallback( "species", [player]() { return player->species(); }); callbacks.registerCallback("setSpecies", [player](String const& species) { player->setSpecies(species); }); callbacks.registerCallback( "imagePath", [player]() { return player->identity().imagePath; }); callbacks.registerCallback("setImagePath", [player](Maybe const& imagePath) { player->setImagePath(imagePath); }); callbacks.registerCallback( "gender", [player]() { return GenderNames.getRight(player->gender()); }); callbacks.registerCallback("setGender", [player](String const& gender) { player->setGender(GenderNames.getLeft(gender)); }); callbacks.registerCallback( "personality", [player]() { return jsonFromPersonality(player->identity().personality); }); callbacks.registerCallback("setPersonality", [player](Json const& personalityConfig) { Personality const& oldPersonality = player->identity().personality; Personality newPersonality = oldPersonality; player->setPersonality(parsePersonality(newPersonality, personalityConfig)); }); callbacks.registerCallback( "mode", [player]() { return PlayerModeNames.getRight(player->modeType()); }); callbacks.registerCallback("setMode", [player](String const& modeName) { player->setModeType(PlayerModeNames.getLeft(modeName)); }); callbacks.registerCallback( "interactRadius", [player]() { return player->interactRadius(); }); callbacks.registerCallback("setInteractRadius", [player](float radius) { player->setInteractRadius(radius); }); callbacks.registerCallback("actionBarGroup", [player]() { return luaTupleReturn(player->inventory()->customBarGroup() + 1, player->inventory()->customBarGroups()); }); callbacks.registerCallback("setActionBarGroup", [player](int group) { player->inventory()->setCustomBarGroup((group - 1) % (unsigned)player->inventory()->customBarGroups()); }); callbacks.registerCallback("selectedActionBarSlot", [player](LuaEngine& engine) -> Maybe { if (auto barLocation = player->inventory()->selectedActionBarLocation()) { if (auto index = barLocation.ptr()) return engine.luaFrom(*index + 1); else return engine.luaFrom(EssentialItemNames.getRight(barLocation.get())); } else { return {}; } }); callbacks.registerCallback("setSelectedActionBarSlot", [player](MVariant const& slot) { auto inventory = player->inventory(); if (!slot) inventory->selectActionBarLocation(SelectedActionBarLocation()); else if (auto index = slot.ptr()) { CustomBarIndex wrapped = (*index - 1) % (unsigned)inventory->customBarIndexes(); inventory->selectActionBarLocation(SelectedActionBarLocation(wrapped)); } else { EssentialItem const& item = EssentialItemNames.getLeft(slot.get()); inventory->selectActionBarLocation(SelectedActionBarLocation(item)); } }); callbacks.registerCallback("actionBarSlotLink", [player](int slot, String const& handName) { auto inventory = player->inventory(); CustomBarIndex wrapped = (slot - 1) % (unsigned)inventory->customBarIndexes(); if (handName == "primary") return inventory->customBarPrimarySlot(wrapped); else if (handName == "alt") return inventory->customBarSecondarySlot(wrapped); else throw StarException(strf("Unknown tool hand {}", handName)); }); callbacks.registerCallback("setActionBarSlotLink", [player](int slot, String const& handName, Maybe inventorySlot) { auto inventory = player->inventory(); CustomBarIndex wrapped = (slot - 1) % (unsigned)inventory->customBarIndexes(); if (inventorySlot && !inventory->slotValid(*inventorySlot)) inventorySlot.reset(); if (handName == "primary") inventory->setCustomBarPrimarySlot(wrapped, inventorySlot); else if (handName == "alt") inventory->setCustomBarSecondarySlot(wrapped, inventorySlot); else throw StarException(strf("Unknown tool hand {}", handName)); }); callbacks.registerCallback("itemBagSize", [player](String const& bagName) -> Maybe { if (auto bag = player->inventory()->bagContents(bagName)) return (unsigned)bag->size(); else return {}; }); callbacks.registerCallback("itemAllowedInBag", [player](String const& bagName, Json const& item) { auto inventory = player->inventory(); auto itemDatabase = Root::singleton().itemDatabase(); if (!inventory->bagContents(bagName)) return false; else return inventory->itemAllowedInBag(itemDatabase->item(ItemDescriptor(item)), bagName); }); callbacks.registerCallback("item", [player](InventorySlot const& slot) -> Maybe { if (!player->inventory()->slotValid(slot)) return {}; if (auto item = player->inventory()->itemsAt(slot)) return itemSafeDescriptor(item).toJson(); else return {}; }); callbacks.registerCallback("setItem", [player](InventorySlot const& slot, Json const& item) { if (!player->inventory()->slotValid(slot)) return; auto itemDatabase = Root::singleton().itemDatabase(); player->inventory()->setItem(slot, itemDatabase->item(ItemDescriptor(item))); }); callbacks.registerCallback("setDamageTeam", [player](String const& typeName, Maybe teamNumber) { player->setTeam(EntityDamageTeam(TeamTypeNames.getLeft(typeName), teamNumber.value(0))); }); callbacks.registerCallback("say", [player](String const& message) { player->addChatMessage(message); }); callbacks.registerCallback("emote", [player](String const& emote, Maybe cooldown) { player->addEmote(HumanoidEmoteNames.getLeft(emote), cooldown); }); callbacks.registerCallback("currentEmote", [player]() { auto currentEmote = player->currentEmote(); return luaTupleReturn(HumanoidEmoteNames.getRight(currentEmote.first), currentEmote.second); }); callbacks.registerCallback("aimPosition", [player]() { return player->aimPosition(); }); callbacks.registerCallback("id", [player]() { return player->entityId(); }); callbacks.registerCallback("uniqueId", [player]() { return player->uniqueId(); }); callbacks.registerCallback("isAdmin", [player]() { return player->isAdmin(); }); callbacks.registerCallback("interact", [player](String const& type, Json const& configData, Maybe const& sourceEntityId) { player->interact(InteractAction(type, sourceEntityId.value(NullEntityId), configData)); }); callbacks.registerCallback("shipUpgrades", [player]() { return player->shipUpgrades().toJson(); }); callbacks.registerCallback("upgradeShip", [player](Json const& upgrades) { player->applyShipUpgrades(upgrades); }); callbacks.registerCallback("setUniverseFlag", [player](String const& flagName) { player->clientContext()->rpcInterface()->invokeRemote("universe.setFlag", flagName); }); callbacks.registerCallback("giveBlueprint", [player](Json const& item) { player->addBlueprint(ItemDescriptor(item)); }); callbacks.registerCallback("blueprintKnown", [player](Json const& item) { return player->blueprintKnown(ItemDescriptor(item)); }); callbacks.registerCallback("availableRecipes", [player](Maybe const& filter) { auto itemDatabase = Root::singleton().itemDatabase(); auto inventory = player->inventory(); auto recipes = itemDatabase->recipesFromBagContents(inventory->availableItems(), inventory->availableCurrencies(), filter.value()); JsonArray result; result.reserve(recipes.size()); for (auto& recipe : recipes) result.append(recipe.toJson()); return result; }); callbacks.registerCallback("makeTechAvailable", [player](String const& tech) { player->techs()->makeAvailable(tech); }); callbacks.registerCallback("makeTechUnavailable", [player](String const& tech) { player->techs()->makeUnavailable(tech); }); callbacks.registerCallback("enableTech", [player](String const& tech) { player->techs()->enable(tech); }); callbacks.registerCallback("equipTech", [player](String const& tech) { player->techs()->equip(tech); }); callbacks.registerCallback("unequipTech", [player](String const& tech) { player->techs()->unequip(tech); }); callbacks.registerCallback("availableTechs", [player]() { return player->techs()->availableTechs(); }); callbacks.registerCallback("enabledTechs", [player]() { return player->techs()->enabledTechs(); }); callbacks.registerCallback("equippedTech", [player](String typeName) { return player->techs()->equippedTechs().maybe(TechTypeNames.getLeft(typeName)); }); callbacks.registerCallback("currency", [player](String const& currencyType) { return player->currency(currencyType); }); callbacks.registerCallback("addCurrency", [player](String const& currencyType, uint64_t amount) { player->inventory()->addCurrency(currencyType, amount); }); callbacks.registerCallback("consumeCurrency", [player](String const& currencyType, uint64_t amount) { return player->inventory()->consumeCurrency(currencyType, amount); }); callbacks.registerCallback("cleanupItems", [player]() { player->inventory()->cleanup(); }); callbacks.registerCallback("giveItem", [player](Json const& item) { player->giveItem(ItemDescriptor(item)); }); callbacks.registerCallback("giveEssentialItem", [player](String const& slotName, Json const& item) { auto itemDatabase = Root::singleton().itemDatabase(); player->inventory()->setEssentialItem(EssentialItemNames.getLeft(slotName), itemDatabase->item(ItemDescriptor(item))); }); callbacks.registerCallback("essentialItem", [player](String const& slotName) { return itemSafeDescriptor(player->inventory()->essentialItem(EssentialItemNames.getLeft(slotName))).toJson(); }); callbacks.registerCallback("removeEssentialItem", [player](String const& slotName) { player->inventory()->setEssentialItem(EssentialItemNames.getLeft(slotName), {}); }); callbacks.registerCallback("setEquippedItem", [player](String const& slotName, Json const& item) { auto itemDatabase = Root::singleton().itemDatabase(); auto slot = InventorySlot(EquipmentSlotNames.getLeft(slotName)); player->inventory()->setItem(slot, itemDatabase->item(ItemDescriptor(item))); }); callbacks.registerCallback("equippedItem", [player](String const& slotName) { auto slot = InventorySlot(EquipmentSlotNames.getLeft(slotName)); if (auto item = player->inventory()->itemsAt(slot)) return item->descriptor().toJson(); return Json(); }); callbacks.registerCallback("hasItem", [player](Json const& item, Maybe exactMatch) { return player->hasItem(ItemDescriptor(item), exactMatch.value(false)); }); callbacks.registerCallback("hasCountOfItem", [player](Json const& item, Maybe exactMatch) { return player->hasCountOfItem(ItemDescriptor(item), exactMatch.value(false)); }); callbacks.registerCallback("consumeItem", [player](Json const& item, Maybe consumePartial, Maybe exactMatch) { return player->takeItem(ItemDescriptor(item), consumePartial.value(false), exactMatch.value(false)).toJson(); }); callbacks.registerCallback("inventoryTags", [player]() { StringMap inventoryTags; for (auto const& item : player->inventory()->allItems()) { for (auto tag : item->itemTags()) ++inventoryTags[tag]; } return inventoryTags; }); callbacks.registerCallback("itemsWithTag", [player](String const& tag) { JsonArray items; for (auto const& item : player->inventory()->allItems()) { for (auto itemTag : item->itemTags()) { if (itemTag == tag) items.append(item->descriptor().toJson()); } } return items; }); callbacks.registerCallback("consumeTaggedItem", [player](String const& itemTag, uint64_t count) { for (auto const& item : player->inventory()->allItems()) { if (item->hasItemTag(itemTag)) { uint64_t takeCount = min(item->count(), count); player->takeItem(item->descriptor().singular().multiply(takeCount)); count -= takeCount; if (count == 0) break; } } }); callbacks.registerCallback("hasItemWithParameter", [player](String const& parameterName, Json const& parameterValue) { for (auto const& item : player->inventory()->allItems()) { if (item->instanceValue(parameterName, Json()) == parameterValue) return true; } return false; }); callbacks.registerCallback("consumeItemWithParameter", [player](String const& parameterName, Json const& parameterValue, uint64_t count) { for (auto const& item : player->inventory()->allItems()) { if (item->instanceValue(parameterName, Json()) == parameterValue) { uint64_t takeCount = min(item->count(), count); player->takeItem(item->descriptor().singular().multiply(takeCount)); count -= takeCount; if (count == 0) break; } } }); callbacks.registerCallback("getItemWithParameter", [player](String const& parameterName, Json const& parameterValue) -> Json { for (auto const& item : player->inventory()->allItems()) { if (item->instanceValue(parameterName, Json()) == parameterValue) return item->descriptor().toJson(); } return {}; }); callbacks.registerCallback("primaryHandItem", [player]() -> Maybe { if (!player->primaryHandItem()) return {}; return player->primaryHandItem()->descriptor().toJson(); }); callbacks.registerCallback("altHandItem", [player]() -> Maybe { if (!player->altHandItem()) return {}; return player->altHandItem()->descriptor().toJson(); }); callbacks.registerCallback("primaryHandItemTags", [player]() -> StringSet { if (!player->primaryHandItem()) return {}; return player->primaryHandItem()->itemTags(); }); callbacks.registerCallback("altHandItemTags", [player]() -> StringSet { if (!player->altHandItem()) return {}; return player->altHandItem()->itemTags(); }); callbacks.registerCallback("swapSlotItem", [player]() -> Maybe { if (!player->inventory()->swapSlotItem()) return {}; return player->inventory()->swapSlotItem()->descriptor().toJson(); }); callbacks.registerCallback("setSwapSlotItem", [player](Json const& item) { auto itemDatabase = Root::singleton().itemDatabase(); player->inventory()->setSwapSlotItem(itemDatabase->item(ItemDescriptor(item))); }); callbacks.registerCallback("canStartQuest", [player](Json const& quest) { return player->questManager()->canStart(QuestArcDescriptor::fromJson(quest)); }); callbacks.registerCallback("startQuest", [player](Json const& quest, Maybe const& serverUuid, Maybe const& worldId) { auto questArc = QuestArcDescriptor::fromJson(quest); auto followUp = make_shared(questArc, 0, player); if (serverUuid) followUp->setServerUuid(Uuid(*serverUuid)); if (worldId) followUp->setWorldId(parseWorldId(*worldId)); player->questManager()->offer(followUp); return followUp->questId(); }); callbacks.registerCallback("hasQuest", [player](String const& questId) { return player->questManager()->hasQuest(questId); }); callbacks.registerCallback("hasAcceptedQuest", [player](String const& questId) { return player->questManager()->hasAcceptedQuest(questId); }); callbacks.registerCallback("hasActiveQuest", [player](String const& questId) { return player->questManager()->isActive(questId); }); callbacks.registerCallback("hasCompletedQuest", [player](String const& questId) { return player->questManager()->hasCompleted(questId); }); callbacks.registerCallback("currentQuestWorld", [player]() -> Maybe { auto maybeQuest = player->questManager()->currentQuest(); if (maybeQuest) { auto quest = *maybeQuest; if (auto worldId = quest->worldId()) return printWorldId(*worldId); } return {}; }); callbacks.registerCallback("questWorlds", [player]() -> List> { List> res; auto maybeCurrentQuest = player->questManager()->currentQuest(); for (auto q : player->questManager()->listActiveQuests()) { if (auto worldId = q->worldId()) { bool isCurrentQuest = maybeCurrentQuest && maybeCurrentQuest.get()->questId() == q->questId(); res.append(pair(printWorldId(*worldId), isCurrentQuest)); } } return res; }); callbacks.registerCallback("currentQuestLocation", [player]() -> Json { auto maybeQuest = player->questManager()->currentQuest(); if (maybeQuest) { if (auto questLocation = maybeQuest.get()->location()) return JsonObject{{"system", jsonFromVec3I(questLocation->first)}, {"location", jsonFromSystemLocation(questLocation->second)}}; } return {}; }); callbacks.registerCallback("questLocations", [player]() -> List> { List> res; auto maybeCurrentQuest = player->questManager()->currentQuest(); for (auto q : player->questManager()->listActiveQuests()) { if (auto questLocation = q->location()) { bool isCurrentQuest = maybeCurrentQuest && maybeCurrentQuest.get()->questId() == q->questId(); auto locationJson = JsonObject{{"system", jsonFromVec3I(questLocation->first)}, {"location", jsonFromSystemLocation(questLocation->second)}}; res.append(pair(locationJson, isCurrentQuest)); } } return res; }); callbacks.registerCallback("enableMission", [player](String const& mission) { AiState& aiState = player->aiState(); if (!aiState.completedMissions.contains(mission)) aiState.availableMissions.add(mission); }); callbacks.registerCallback("completeMission", [player](String const& mission) { AiState& aiState = player->aiState(); aiState.availableMissions.remove(mission); aiState.completedMissions.add(mission); }); callbacks.registerCallback("hasCompletedMission", [player](String const& mission) -> bool { return player->aiState().completedMissions.contains(mission); }); callbacks.registerCallback("radioMessage", [player](Json const& messageConfig, Maybe const& delay) { player->queueRadioMessage(messageConfig, delay.value(0)); }); callbacks.registerCallback("worldId", [player]() { return printWorldId(player->clientContext()->playerWorldId()); }); callbacks.registerCallback("serverUuid", [player]() { return player->clientContext()->serverUuid().hex(); }); callbacks.registerCallback("ownShipWorldId", [player]() { return printWorldId(ClientShipWorldId(player->uuid())); }); callbacks.registerCallback("lounge", [player](EntityId entityId, Maybe anchorIndex) { return player->lounge(entityId, anchorIndex.value(0)); }); callbacks.registerCallback("isLounging", [player]() { return (bool)player->loungingIn(); }); callbacks.registerCallback("loungingIn", [player]() -> Maybe { if (auto anchorState = player->loungingIn()) return anchorState->entityId; return {}; }); callbacks.registerCallback("stopLounging", [player]() { player->stopLounging(); }); callbacks.registerCallback("playTime", [player]() { return player->log()->playTime(); }); callbacks.registerCallback("introComplete", [player]() { return player->log()->introComplete(); }); callbacks.registerCallback("setIntroComplete", [player](bool complete) { return player->log()->setIntroComplete(complete); }); callbacks.registerCallback("warp", [player](String action, Maybe animation, Maybe deploy) { player->setPendingWarp(action, animation, deploy.value(false)); }); callbacks.registerCallback("canDeploy", [player]() { return player->canDeploy(); }); callbacks.registerCallback("isDeployed", [player]() -> bool { return player->isDeployed(); }); callbacks.registerCallback("confirm", [player](Json dialogConfig) { auto pair = RpcPromise::createPair(); player->queueConfirmation(dialogConfig, pair.second); return pair.first; }); callbacks.registerCallback("playCinematic", [player](Json const& cinematic, Maybe unique) { player->setPendingCinematic(cinematic, unique.value(false)); }); callbacks.registerCallback("recordEvent", [player](String const& eventName, Json const& fields) { player->statistics()->recordEvent(eventName, fields); }); callbacks.registerCallback("worldHasOrbitBookmark", [player](Json const& coords) -> bool { CelestialCoordinate coordinate = CelestialCoordinate(coords); return player->universeMap()->worldBookmark(coordinate).isValid(); }); callbacks.registerCallback("orbitBookmarks", [player]() -> List> { return player->universeMap()->orbitBookmarks().transformed([](pair const& p) -> pair { return {p.first, p.second.toJson()}; }); }); callbacks.registerCallback("systemBookmarks", [player](Json const& coords) -> List { CelestialCoordinate coordinate = CelestialCoordinate(coords); return player->universeMap()->systemBookmarks(coordinate).transformed([](OrbitBookmark const& bookmark) { return bookmark.toJson(); }); }); callbacks.registerCallback("addOrbitBookmark", [player](Json const& system, Json const& bookmarkConfig) -> bool { CelestialCoordinate coordinate = CelestialCoordinate(system); return player->universeMap()->addOrbitBookmark(coordinate, OrbitBookmark::fromJson(bookmarkConfig)); }); callbacks.registerCallback("removeOrbitBookmark", [player](Json const& system, Json const& bookmarkConfig) -> bool { CelestialCoordinate coordinate = CelestialCoordinate(system); return player->universeMap()->removeOrbitBookmark(coordinate, OrbitBookmark::fromJson(bookmarkConfig)); }); callbacks.registerCallback("teleportBookmarks", [player]() -> List { return player->universeMap()->teleportBookmarks().transformed([](TeleportBookmark const& bookmark) -> Json { return bookmark.toJson(); }); }); callbacks.registerCallback("addTeleportBookmark", [player](Json const& bookmarkConfig) -> bool { return player->universeMap()->addTeleportBookmark(TeleportBookmark::fromJson(bookmarkConfig)); }); callbacks.registerCallback("removeTeleportBookmark", [player](Json const& bookmarkConfig) -> bool { return player->universeMap()->removeTeleportBookmark(TeleportBookmark::fromJson(bookmarkConfig)); }); callbacks.registerCallback("isMapped", [player](Json const& coords) { CelestialCoordinate coordinate = CelestialCoordinate(coords); return player->universeMap()->isMapped(coordinate); }); callbacks.registerCallback("mappedObjects", [player](Json const& coords) -> Json { CelestialCoordinate coordinate = CelestialCoordinate(coords); JsonObject json; for (auto p : player->universeMap()->mappedObjects(coordinate)) { JsonObject object = { {"typeName", p.second.typeName}, {"orbit", jsonFromMaybe(p.second.orbit, [](CelestialOrbit const& o) { return o.toJson(); })}, {"parameters", p.second.parameters} }; json.set(p.first.hex(), object); } return json; }); callbacks.registerCallback("collectables", [player](String const& collection) { return player->log()->collectables(collection); }); callbacks.registerCallback("getProperty", [player](String const& name, Maybe const& defaultValue) -> Json { return player->getGenericProperty(name, defaultValue.value(Json())); }); callbacks.registerCallback("setProperty", [player](String const& name, Json const& value) { player->setGenericProperty(name, value); }); callbacks.registerCallback("addScannedObject", [player](String const& objectName) -> bool { return player->log()->addScannedObject(objectName); }); callbacks.registerCallback("removeScannedObject", [player](String const& objectName) { player->log()->removeScannedObject(objectName); }); return callbacks; } }