2023-06-20 04:33:09 +00:00
|
|
|
#include "StarWorldLuaBindings.hpp"
|
|
|
|
#include "StarJsonExtra.hpp"
|
|
|
|
#include "StarWorld.hpp"
|
|
|
|
#include "StarBlocksAlongLine.hpp"
|
|
|
|
#include "StarSky.hpp"
|
|
|
|
#include "StarPlayer.hpp"
|
|
|
|
#include "StarPlayerInventory.hpp"
|
|
|
|
#include "StarMonster.hpp"
|
|
|
|
#include "StarNpc.hpp"
|
|
|
|
#include "StarStagehand.hpp"
|
|
|
|
#include "StarLoungeableObject.hpp"
|
|
|
|
#include "StarProjectileDatabase.hpp"
|
|
|
|
#include "StarProjectile.hpp"
|
|
|
|
#include "StarRoot.hpp"
|
|
|
|
#include "StarWorldServer.hpp"
|
|
|
|
#include "StarWorldClient.hpp"
|
|
|
|
#include "StarWorldTemplate.hpp"
|
|
|
|
#include "StarWorldParameters.hpp"
|
|
|
|
#include "StarItemDrop.hpp"
|
|
|
|
#include "StarMaterialDatabase.hpp"
|
|
|
|
#include "StarLogging.hpp"
|
|
|
|
#include "StarObjectDatabase.hpp"
|
|
|
|
#include "StarItemDatabase.hpp"
|
|
|
|
#include "StarItem.hpp"
|
|
|
|
#include "StarTreasure.hpp"
|
|
|
|
#include "StarContainerObject.hpp"
|
|
|
|
#include "StarFarmableObject.hpp"
|
|
|
|
#include "StarImageMetadataDatabase.hpp"
|
|
|
|
#include "StarLuaGameConverters.hpp"
|
|
|
|
#include "StarVehicleDatabase.hpp"
|
|
|
|
#include "StarUtilityLuaBindings.hpp"
|
|
|
|
#include "StarUniverseSettings.hpp"
|
|
|
|
#include "StarBiome.hpp"
|
|
|
|
|
|
|
|
namespace Star {
|
|
|
|
namespace LuaBindings {
|
|
|
|
|
|
|
|
enum class EntityBoundMode { MetaBoundBox, CollisionArea, Position };
|
|
|
|
|
|
|
|
EnumMap<EntityBoundMode> const EntityBoundModeNames = {
|
|
|
|
{EntityBoundMode::MetaBoundBox, "MetaBoundBox"},
|
|
|
|
{EntityBoundMode::CollisionArea, "CollisionArea"},
|
|
|
|
{EntityBoundMode::Position, "Position"}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename EntityT>
|
|
|
|
using Selector = function<bool(shared_ptr<EntityT> const&)>;
|
|
|
|
|
|
|
|
template <typename EntityT>
|
|
|
|
LuaTable entityQueryImpl(World* world, LuaEngine& engine, LuaTable const& options, Selector<EntityT> selector) {
|
|
|
|
Maybe<EntityId> withoutEntityId = options.get<Maybe<EntityId>>("withoutEntityId");
|
|
|
|
Maybe<Set<EntityType>> includedTypes;
|
|
|
|
if (auto types = options.get<Maybe<LuaTable>>("includedTypes")) {
|
|
|
|
includedTypes = Set<EntityType>();
|
|
|
|
types->iterate([&includedTypes](LuaValue const&, LuaString const& type) {
|
|
|
|
if (type == "mobile") {
|
|
|
|
includedTypes->add(EntityType::Player);
|
|
|
|
includedTypes->add(EntityType::Monster);
|
|
|
|
includedTypes->add(EntityType::Npc);
|
|
|
|
includedTypes->add(EntityType::Projectile);
|
|
|
|
includedTypes->add(EntityType::ItemDrop);
|
|
|
|
includedTypes->add(EntityType::Vehicle);
|
|
|
|
} else if (type == "creature") {
|
|
|
|
includedTypes->add(EntityType::Player);
|
|
|
|
includedTypes->add(EntityType::Monster);
|
|
|
|
includedTypes->add(EntityType::Npc);
|
|
|
|
} else {
|
|
|
|
includedTypes->add(EntityTypeNames.getLeft(type.ptr()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> callScript = options.get<Maybe<String>>("callScript");
|
|
|
|
List<LuaValue> callScriptArgs = options.get<Maybe<List<LuaValue>>>("callScriptArgs").value();
|
|
|
|
LuaValue callScriptResult = options.get<Maybe<LuaValue>>("callScriptResult").value(LuaBoolean(true));
|
|
|
|
|
|
|
|
Maybe<Line2F> lineQuery = options.get<Maybe<Line2F>>("line");
|
|
|
|
Maybe<PolyF> polyQuery = options.get<Maybe<PolyF>>("poly");
|
|
|
|
Maybe<RectF> rectQuery = options.get<Maybe<RectF>>("rect");
|
|
|
|
Maybe<pair<Vec2F, float>> radiusQuery;
|
|
|
|
if (auto radius = options.get<Maybe<float>>("radius"))
|
|
|
|
radiusQuery = make_pair(options.get<Vec2F>("center"), *radius);
|
|
|
|
|
|
|
|
EntityBoundMode boundMode = EntityBoundModeNames.getLeft(options.get<Maybe<String>>("boundMode").value("CollisionArea"));
|
|
|
|
Maybe<LuaString> order = options.get<Maybe<LuaString>>("order");
|
|
|
|
|
|
|
|
auto geometry = world->geometry();
|
|
|
|
|
|
|
|
auto innerSelector = [=](shared_ptr<EntityT> const& entity) -> bool {
|
|
|
|
if (selector && !selector(entity))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (includedTypes && !includedTypes->contains(entity->entityType()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (withoutEntityId && entity->entityId() == *withoutEntityId)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (callScript) {
|
|
|
|
auto scriptedEntity = as<ScriptedEntity>(entity);
|
|
|
|
if (!scriptedEntity || !scriptedEntity->isMaster())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto res = scriptedEntity->callScript(*callScript, luaUnpack(callScriptArgs));
|
|
|
|
if (!res || *res != callScriptResult)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto position = entity->position();
|
|
|
|
if (boundMode == EntityBoundMode::MetaBoundBox) {
|
|
|
|
// If using MetaBoundBox, the regular line / box query methods already
|
|
|
|
// enforce collision with MetaBoundBox
|
|
|
|
if (radiusQuery)
|
|
|
|
return geometry.rectIntersectsCircle(
|
|
|
|
entity->metaBoundBox().translated(position), radiusQuery->first, radiusQuery->second);
|
|
|
|
} else if (boundMode == EntityBoundMode::CollisionArea) {
|
|
|
|
// Collision area queries either query based on the collision area if
|
|
|
|
// that's given, or as a fallback the regular bound box.
|
|
|
|
auto collisionArea = entity->collisionArea();
|
|
|
|
if (collisionArea.isNull())
|
|
|
|
collisionArea = entity->metaBoundBox();
|
|
|
|
collisionArea.translate(position);
|
|
|
|
|
|
|
|
if (lineQuery)
|
|
|
|
return geometry.lineIntersectsRect(*lineQuery, collisionArea);
|
|
|
|
if (polyQuery)
|
|
|
|
return geometry.polyIntersectsPoly(*polyQuery, PolyF(collisionArea));
|
|
|
|
if (rectQuery)
|
|
|
|
return geometry.rectIntersectsRect(*rectQuery, collisionArea);
|
|
|
|
if (radiusQuery)
|
|
|
|
return geometry.rectIntersectsCircle(collisionArea, radiusQuery->first, radiusQuery->second);
|
|
|
|
} else if (boundMode == EntityBoundMode::Position) {
|
|
|
|
if (lineQuery)
|
|
|
|
return geometry.lineIntersectsRect(*lineQuery, RectF(position, position));
|
|
|
|
if (polyQuery)
|
|
|
|
return geometry.polyContains(*polyQuery, position);
|
|
|
|
if (rectQuery)
|
|
|
|
return geometry.rectContains(*rectQuery, position);
|
|
|
|
if (radiusQuery)
|
|
|
|
return geometry.diff(radiusQuery->first, position).magnitude() <= radiusQuery->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
List<shared_ptr<EntityT>> entities;
|
|
|
|
|
|
|
|
if (lineQuery) {
|
|
|
|
entities = world->lineQuery<EntityT>(lineQuery->min(), lineQuery->max(), innerSelector);
|
|
|
|
} else if (polyQuery) {
|
|
|
|
entities = world->query<EntityT>(polyQuery->boundBox(), innerSelector);
|
|
|
|
} else if (rectQuery) {
|
|
|
|
entities = world->query<EntityT>(*rectQuery, innerSelector);
|
|
|
|
} else if (radiusQuery) {
|
|
|
|
RectF region(radiusQuery->first - Vec2F::filled(radiusQuery->second),
|
|
|
|
radiusQuery->first + Vec2F::filled(radiusQuery->second));
|
|
|
|
entities = world->query<EntityT>(region, innerSelector);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (order) {
|
|
|
|
if (*order == "nearest") {
|
|
|
|
Vec2F nearestPosition;
|
|
|
|
if (lineQuery)
|
|
|
|
nearestPosition = lineQuery->min();
|
|
|
|
else if (polyQuery)
|
|
|
|
nearestPosition = polyQuery->center();
|
|
|
|
else if (rectQuery)
|
|
|
|
nearestPosition = rectQuery->center();
|
|
|
|
else if (radiusQuery)
|
|
|
|
nearestPosition = radiusQuery->first;
|
|
|
|
sortByComputedValue(entities,
|
|
|
|
[world, nearestPosition](shared_ptr<EntityT> const& entity) {
|
|
|
|
return world->geometry().diff(entity->position(), nearestPosition).magnitude();
|
|
|
|
});
|
|
|
|
} else if (*order == "random") {
|
|
|
|
Random::shuffle(entities);
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported query order {}", order->ptr()));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable entityIds = engine.createTable();
|
|
|
|
int entityIdsIndex = 1;
|
|
|
|
for (auto entity : entities)
|
|
|
|
entityIds.set(entityIdsIndex++, entity->entityId());
|
|
|
|
|
|
|
|
return entityIds;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename EntityT>
|
|
|
|
LuaTable entityQuery(World* world,
|
|
|
|
LuaEngine& engine,
|
|
|
|
Vec2F const& pos1,
|
|
|
|
LuaValue const& pos2,
|
|
|
|
Maybe<LuaTable> options,
|
|
|
|
Selector<EntityT> selector = {}) {
|
|
|
|
if (!options)
|
|
|
|
options = engine.createTable();
|
|
|
|
|
|
|
|
if (auto radius = engine.luaMaybeTo<float>(pos2)) {
|
|
|
|
Vec2F center = pos1;
|
|
|
|
options->set("center", center);
|
|
|
|
options->set("radius", *radius);
|
|
|
|
return entityQueryImpl<EntityT>(world, engine, *options, selector);
|
|
|
|
} else {
|
|
|
|
RectF rect(pos1, engine.luaTo<Vec2F>(pos2));
|
|
|
|
options->set("rect", rect);
|
|
|
|
return entityQueryImpl<EntityT>(world, engine, *options, selector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename EntityT>
|
|
|
|
LuaTable entityLineQuery(World* world,
|
|
|
|
LuaEngine& engine,
|
|
|
|
Vec2F const& point1,
|
|
|
|
Vec2F const& point2,
|
|
|
|
Maybe<LuaTable> options,
|
|
|
|
Selector<EntityT> selector = {}) {
|
|
|
|
Line2F line(point1, point2);
|
|
|
|
|
|
|
|
if (!options)
|
|
|
|
options = engine.createTable();
|
|
|
|
|
|
|
|
options->set("line", line);
|
|
|
|
|
|
|
|
return entityQueryImpl<EntityT>(world, engine, *options, selector);
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaCallbacks makeWorldCallbacks(World* world) {
|
|
|
|
LuaCallbacks callbacks;
|
|
|
|
|
|
|
|
addWorldDebugCallbacks(callbacks);
|
|
|
|
addWorldEnvironmentCallbacks(callbacks, world);
|
|
|
|
addWorldEntityCallbacks(callbacks, world);
|
|
|
|
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F, Maybe<Vec2F>>("magnitude", bind(&WorldCallbacks::magnitude, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Vec2F, Vec2F, Vec2F>("distance", bind(WorldCallbacks::distance, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, PolyF, Vec2F>("polyContains", bind(WorldCallbacks::polyContains, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaValue, LuaEngine&, LuaValue>("xwrap", bind(WorldCallbacks::xwrap, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaValue, LuaEngine&, Variant<Vec2F, float>, Variant<Vec2F, float>>("nearestTo", bind(WorldCallbacks::nearestTo, world, _1, _2, _3));
|
|
|
|
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectF, Maybe<CollisionSet>>("rectCollision", bind(WorldCallbacks::rectCollision, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F, Maybe<CollisionSet>>("pointTileCollision", bind(WorldCallbacks::pointTileCollision, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F, Vec2F, Maybe<CollisionSet>>("lineTileCollision", bind(WorldCallbacks::lineTileCollision, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<pair<Vec2F, Vec2I>>, Vec2F, Vec2F, Maybe<CollisionSet>>("lineTileCollisionPoint", bind(WorldCallbacks::lineTileCollisionPoint, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectF, Maybe<CollisionSet>>("rectTileCollision", bind(WorldCallbacks::rectTileCollision, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F, Maybe<CollisionSet>>("pointCollision", bind(WorldCallbacks::pointCollision, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTupleReturn<Maybe<Vec2F>, Maybe<Vec2F>>, Vec2F, Vec2F, Maybe<CollisionSet>>("lineCollision", bind(WorldCallbacks::lineCollision, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, PolyF, Maybe<Vec2F>, Maybe<CollisionSet>>("polyCollision", bind(WorldCallbacks::polyCollision, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<List<Vec2I>, Vec2F, Vec2F, Maybe<CollisionSet>, Maybe<int>>("collisionBlocksAlongLine", bind(WorldCallbacks::collisionBlocksAlongLine, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<List<pair<Vec2I, LiquidLevel>>, Vec2F, Vec2F>("liquidAlongLine", bind(WorldCallbacks::liquidAlongLine, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<Vec2F>, PolyF, Vec2F, float, Maybe<CollisionSet>>("resolvePolyCollision", bind(WorldCallbacks::resolvePolyCollision, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2I, Maybe<bool>, Maybe<bool>>("tileIsOccupied", bind(WorldCallbacks::tileIsOccupied, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, String, Vec2I, Maybe<int>, Json>("placeObject", bind(WorldCallbacks::placeObject, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, Json, Vec2F, Maybe<size_t>, Json, Maybe<Vec2F>, Maybe<float>>("spawnItem", bind(WorldCallbacks::spawnItem, world, _1, _2, _3, _4, _5, _6));
|
|
|
|
callbacks.registerCallbackWithSignature<List<EntityId>, Vec2F, String, float, Maybe<uint64_t>>("spawnTreasure", bind(WorldCallbacks::spawnTreasure, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, String, Vec2F, Maybe<JsonObject>>("spawnMonster", bind(WorldCallbacks::spawnMonster, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, Vec2F, String, String, float, Maybe<uint64_t>, Json>("spawnNpc", bind(WorldCallbacks::spawnNpc, world, _1, _2, _3, _4, _5, _6));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, Vec2F, String, Json>("spawnStagehand", bind(WorldCallbacks::spawnStagehand, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, String, Vec2F, Maybe<EntityId>, Maybe<Vec2F>, bool, Json>("spawnProjectile", bind(WorldCallbacks::spawnProjectile, world, _1, _2, _3, _4, _5, _6));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<EntityId>, String, Vec2F, Json>("spawnVehicle", bind(WorldCallbacks::spawnVehicle, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<float>("threatLevel", bind(&World::threatLevel, world));
|
|
|
|
callbacks.registerCallbackWithSignature<double>("time", bind(WorldCallbacks::time, world));
|
|
|
|
callbacks.registerCallbackWithSignature<uint64_t>("day", bind(WorldCallbacks::day, world));
|
|
|
|
callbacks.registerCallbackWithSignature<double>("timeOfDay", bind(WorldCallbacks::timeOfDay, world));
|
|
|
|
callbacks.registerCallbackWithSignature<float>("dayLength", bind(WorldCallbacks::dayLength, world));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, String, Json>("getProperty", bind(WorldCallbacks::getProperty, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<void, String, Json>("setProperty", bind(WorldCallbacks::setProperty, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LiquidLevel>, Variant<RectF, Vec2I>>("liquidAt", bind(WorldCallbacks::liquidAt, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F>("gravity", bind(WorldCallbacks::gravity, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F, LiquidId, float>("spawnLiquid", bind(WorldCallbacks::spawnLiquid, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LiquidLevel>, Vec2F>("destroyLiquid", bind(WorldCallbacks::destroyLiquid, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F>("isTileProtected", bind(WorldCallbacks::isTileProtected, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<PlatformerAStar::Path>, Vec2F, Vec2F, ActorMovementParameters, PlatformerAStar::Parameters>("findPlatformerPath", bind(WorldCallbacks::findPlatformerPath, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<PlatformerAStar::PathFinder, Vec2F, Vec2F, ActorMovementParameters, PlatformerAStar::Parameters>("platformerPathStart", bind(WorldCallbacks::platformerPathStart, world, _1, _2, _3, _4));
|
|
|
|
|
|
|
|
callbacks.registerCallback("type", [world](LuaEngine& engine) -> LuaString {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world)) {
|
|
|
|
if (auto worldParameters = serverWorld->worldTemplate()->worldParameters())
|
|
|
|
return engine.createString(worldParameters->typeName);
|
|
|
|
} else if (auto clientWorld = as<WorldClient>(world)) {
|
|
|
|
if (auto worldParameters = clientWorld->currentTemplate()->worldParameters())
|
|
|
|
return engine.createString(worldParameters->typeName);
|
|
|
|
}
|
|
|
|
return engine.createString("unknown");
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("size", [world]() -> Vec2I {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world))
|
|
|
|
return (Vec2I)serverWorld->worldTemplate()->size();
|
|
|
|
else if (auto clientWorld = as<WorldClient>(world))
|
|
|
|
return (Vec2I)clientWorld->currentTemplate()->size();
|
|
|
|
return Vec2I();
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("inSurfaceLayer", [world](Vec2I const& position) -> bool {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world))
|
|
|
|
return serverWorld->worldTemplate()->inSurfaceLayer(position);
|
|
|
|
else if (auto clientWorld = as<WorldClient>(world))
|
|
|
|
return clientWorld->currentTemplate()->inSurfaceLayer(position);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("surfaceLevel", [world]() -> float {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world))
|
|
|
|
return serverWorld->worldTemplate()->surfaceLevel();
|
|
|
|
else if (auto clientWorld = as<WorldClient>(world))
|
|
|
|
return clientWorld->currentTemplate()->surfaceLevel();
|
|
|
|
else
|
|
|
|
return world->geometry().size()[1] / 2.0f;
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("terrestrial", [world]() -> bool {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world)) {
|
|
|
|
if (auto worldParameters = serverWorld->worldTemplate()->worldParameters())
|
|
|
|
return worldParameters->type() == WorldParametersType::TerrestrialWorldParameters;
|
|
|
|
} else if (auto clientWorld = as<WorldClient>(world)) {
|
|
|
|
if (auto worldParameters = clientWorld->currentTemplate()->worldParameters())
|
|
|
|
return worldParameters->type() == WorldParametersType::TerrestrialWorldParameters;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("itemDropItem", [world](EntityId const& entityId) -> Json {
|
|
|
|
if (auto itemDrop = world->get<ItemDrop>(entityId))
|
|
|
|
return itemDrop->item()->descriptor().toJson();
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("biomeBlocksAt", [world](Vec2I position) -> Maybe<List<MaterialId>> {
|
|
|
|
WorldTemplateConstPtr worldTemplate;
|
|
|
|
if (auto worldClient = as<WorldClient>(world))
|
|
|
|
worldTemplate = worldClient->currentTemplate();
|
|
|
|
else if (auto worldServer = as<WorldServer>(world))
|
|
|
|
worldTemplate = worldServer->worldTemplate();
|
|
|
|
|
|
|
|
if (worldTemplate) {
|
|
|
|
WorldTemplate::BlockInfo block = worldTemplate->blockInfo(position[0], position[1]);
|
|
|
|
if (auto biome = worldTemplate->biome(block.blockBiomeIndex)) {
|
|
|
|
List<MaterialId> blocks = {biome->mainBlock};
|
|
|
|
blocks.appendAll(biome->subBlocks);
|
|
|
|
return blocks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
callbacks.registerCallback("dungeonId", [world](Vec2I position) -> DungeonId {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world)) {
|
|
|
|
return serverWorld->dungeonId(position);
|
|
|
|
} else {
|
|
|
|
return as<WorldClient>(world)->dungeonId(position);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (auto clientWorld = as<WorldClient>(world)) {
|
|
|
|
callbacks.registerCallbackWithSignature<RectI>("clientWindow", bind(ClientWorldCallbacks::clientWindow, clientWorld));
|
2023-07-03 08:22:44 +00:00
|
|
|
callbacks.registerCallback("players", [clientWorld]() {
|
|
|
|
List<EntityId> playerIds;
|
|
|
|
|
|
|
|
clientWorld->forAllEntities([&](EntityPtr const& entity) {
|
|
|
|
if (entity->entityType() == EntityType::Player)
|
|
|
|
playerIds.emplace_back(entity->entityId());
|
|
|
|
});
|
|
|
|
|
|
|
|
return playerIds;
|
|
|
|
});
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (auto serverWorld = as<WorldServer>(world)) {
|
|
|
|
callbacks.registerCallbackWithSignature<bool, EntityId, bool>("breakObject", bind(ServerWorldCallbacks::breakObject, serverWorld, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectF>("isVisibleToPlayer", bind(ServerWorldCallbacks::isVisibleToPlayer, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectF>("loadRegion", bind(ServerWorldCallbacks::loadRegion, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectF>("regionActive", bind(ServerWorldCallbacks::regionActive, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<void, DungeonId, bool>("setTileProtection", bind(ServerWorldCallbacks::setTileProtection, serverWorld, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, RectI>("isPlayerModified", bind(ServerWorldCallbacks::isPlayerModified, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LiquidLevel>, Vec2F>("forceDestroyLiquid", bind(ServerWorldCallbacks::forceDestroyLiquid, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<EntityId, String>("loadUniqueEntity", bind(ServerWorldCallbacks::loadUniqueEntity, serverWorld, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<void, EntityId, String>("setUniqueId", bind(ServerWorldCallbacks::setUniqueId, serverWorld, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Maybe<EntityId>>("takeItemDrop", bind(ServerWorldCallbacks::takeItemDrop, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<void, Vec2F, Maybe<bool>>("setPlayerStart", bind(ServerWorldCallbacks::setPlayerStart, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<List<EntityId>>("players", bind(ServerWorldCallbacks::players, world));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaString, LuaEngine&>("fidelity", bind(ServerWorldCallbacks::fidelity, world, _1));
|
2023-07-23 03:49:34 +00:00
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LuaValue>, String, String, LuaVariadic<LuaValue>>("callScriptContext", bind(ServerWorldCallbacks::callScriptContext, world, _1, _2, _3));
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
callbacks.registerCallbackWithSignature<double>("skyTime", [serverWorld]() {
|
|
|
|
return serverWorld->sky()->epochTime();
|
|
|
|
});
|
|
|
|
callbacks.registerCallbackWithSignature<void, double>("setSkyTime", [serverWorld](double skyTime) {
|
|
|
|
return serverWorld->sky()->setEpochTime(skyTime);
|
|
|
|
});
|
2023-07-22 15:01:23 +00:00
|
|
|
|
|
|
|
callbacks.registerCallback("setExpiryTime", [serverWorld](float expiryTime) { serverWorld->setExpiryTime(expiryTime); });
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
callbacks.registerCallback("flyingType", [serverWorld]() -> String { return FlyingTypeNames.getRight(serverWorld->sky()->flyingType()); });
|
|
|
|
callbacks.registerCallback("warpPhase", [serverWorld]() -> String { return WarpPhaseNames.getRight(serverWorld->sky()->warpPhase()); });
|
|
|
|
callbacks.registerCallback("setUniverseFlag", [serverWorld](String flagName) { return serverWorld->universeSettings()->setFlag(flagName); });
|
|
|
|
callbacks.registerCallback("universeFlags", [serverWorld]() { return serverWorld->universeSettings()->flags(); });
|
|
|
|
callbacks.registerCallback("universeFlagSet", [serverWorld](String const& flagName) { return serverWorld->universeSettings()->flags().contains(flagName); });
|
|
|
|
callbacks.registerCallback("placeDungeon", [serverWorld](String dungeonName, Vec2I position, Maybe<DungeonId> dungeonId) -> bool {
|
|
|
|
return serverWorld->placeDungeon(dungeonName, position, dungeonId);
|
|
|
|
});
|
|
|
|
callbacks.registerCallback("tryPlaceDungeon", [serverWorld](String dungeonName, Vec2I position, Maybe<DungeonId> dungeonId) -> bool {
|
|
|
|
return serverWorld->placeDungeon(dungeonName, position, dungeonId, false);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("addBiomeRegion", [serverWorld](Vec2I position, String biomeName, String subBlockSelector, int width) {
|
|
|
|
serverWorld->addBiomeRegion(position, biomeName, subBlockSelector, width);
|
|
|
|
});
|
|
|
|
callbacks.registerCallback("expandBiomeRegion", [serverWorld](Vec2I position, int width) {
|
|
|
|
serverWorld->expandBiomeRegion(position, width);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("pregenerateAddBiome", [serverWorld](Vec2I position, int width) {
|
|
|
|
return serverWorld->pregenerateAddBiome(position, width);
|
|
|
|
});
|
|
|
|
callbacks.registerCallback("pregenerateExpandBiome", [serverWorld](Vec2I position, int width) {
|
|
|
|
return serverWorld->pregenerateExpandBiome(position, width);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("setLayerEnvironmentBiome", [serverWorld](Vec2I position) {
|
|
|
|
serverWorld->setLayerEnvironmentBiome(position);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("setPlanetType", [serverWorld](String planetType, String primaryBiomeName) {
|
|
|
|
serverWorld->setPlanetType(planetType, primaryBiomeName);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("setDungeonGravity", [serverWorld](DungeonId dungeonId, Maybe<float> gravity) {
|
|
|
|
serverWorld->setDungeonGravity(dungeonId, gravity);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("setDungeonBreathable", [serverWorld](DungeonId dungeonId, Maybe<bool> breathable) {
|
|
|
|
serverWorld->setDungeonBreathable(dungeonId, breathable);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("setDungeonId", [serverWorld](RectI tileRegion, DungeonId dungeonId) {
|
|
|
|
serverWorld->setDungeonId(tileRegion, dungeonId);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("enqueuePlacement", [serverWorld](List<Json> distributionConfigs, Maybe<DungeonId> id) {
|
|
|
|
auto distributions = distributionConfigs.transformed([](Json const& config) {
|
|
|
|
return BiomeItemDistribution(config, Random::randu64());
|
|
|
|
});
|
|
|
|
return serverWorld->enqueuePlacement(move(distributions), id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return callbacks;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addWorldDebugCallbacks(LuaCallbacks& callbacks) {
|
|
|
|
callbacks.registerCallback("debugPoint", WorldDebugCallbacks::debugPoint);
|
|
|
|
callbacks.registerCallback("debugLine", WorldDebugCallbacks::debugLine);
|
|
|
|
callbacks.registerCallback("debugPoly", WorldDebugCallbacks::debugPoly);
|
|
|
|
callbacks.registerCallback("debugText", WorldDebugCallbacks::debugText);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addWorldEntityCallbacks(LuaCallbacks& callbacks, World* world) {
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("entityQuery", bind(WorldEntityCallbacks::entityQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("monsterQuery", bind(WorldEntityCallbacks::monsterQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("npcQuery", bind(WorldEntityCallbacks::npcQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("objectQuery", bind(WorldEntityCallbacks::objectQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("itemDropQuery", bind(WorldEntityCallbacks::itemDropQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("playerQuery", bind(WorldEntityCallbacks::playerQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, LuaValue, Maybe<LuaTable>>("loungeableQuery", bind(WorldEntityCallbacks::loungeableQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, Vec2F, Maybe<LuaTable>>("entityLineQuery", bind(WorldEntityCallbacks::entityLineQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, Vec2F, Maybe<LuaTable>>("objectLineQuery", bind(WorldEntityCallbacks::objectLineQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaTable, LuaEngine&, Vec2F, Vec2F, Maybe<LuaTable>>("npcLineQuery", bind(WorldEntityCallbacks::npcLineQuery, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallback("objectAt",
|
|
|
|
[world](Vec2I const& tilePosition) -> Maybe<int> {
|
|
|
|
if (auto object = world->findEntityAtTile(tilePosition, [](TileEntityPtr const& entity) { return is<Object>(entity); }))
|
|
|
|
return object->entityId();
|
|
|
|
else
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
callbacks.registerCallbackWithSignature<bool, int>("entityExists", bind(WorldEntityCallbacks::entityExists, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, int, int>("entityCanDamage", bind(WorldEntityCallbacks::entityCanDamage, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId>("entityDamageTeam", bind(WorldEntityCallbacks::entityDamageTeam, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId>("entityAggressive", bind(WorldEntityCallbacks::entityAggressive, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LuaString>, LuaEngine&, int>("entityType", bind(WorldEntityCallbacks::entityType, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<Vec2F>, int>("entityPosition", bind(WorldEntityCallbacks::entityPosition, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<Vec2F>, int>("entityVelocity", bind(WorldEntityCallbacks::entityVelocity, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<RectF>, int>("entityMetaBoundBox", bind(WorldEntityCallbacks::entityMetaBoundBox, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<uint64_t>, EntityId, String>("entityCurrency", bind(WorldEntityCallbacks::entityCurrency, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<uint64_t>, EntityId, Json, Maybe<bool>>("entityHasCountOfItem", bind(WorldEntityCallbacks::entityHasCountOfItem, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<Vec2F>, EntityId>("entityHealth", bind(WorldEntityCallbacks::entityHealth, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("entitySpecies", bind(WorldEntityCallbacks::entitySpecies, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("entityGender", bind(WorldEntityCallbacks::entityGender, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("entityName", bind(WorldEntityCallbacks::entityName, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId, Maybe<String>>("entityDescription", bind(WorldEntityCallbacks::entityDescription, world, _1, _2));
|
2023-07-24 14:49:20 +00:00
|
|
|
callbacks.registerCallbackWithSignature<LuaNullTermWrapper<Maybe<List<Drawable>>>, EntityId, String>("entityPortrait", bind(WorldEntityCallbacks::entityPortrait, world, _1, _2));
|
2023-06-20 04:33:09 +00:00
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId, String>("entityHandItem", bind(WorldEntityCallbacks::entityHandItem, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, String>("entityHandItemDescriptor", bind(WorldEntityCallbacks::entityHandItemDescriptor, world, _1, _2));
|
2023-07-24 14:49:20 +00:00
|
|
|
callbacks.registerCallbackWithSignature<LuaNullTermWrapper<Maybe<String>>, EntityId>("entityUniqueId", bind(WorldEntityCallbacks::entityUniqueId, world, _1));
|
2023-06-20 04:33:09 +00:00
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, String, Maybe<Json>>("getObjectParameter", bind(WorldEntityCallbacks::getObjectParameter, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, String, Maybe<Json>>("getNpcScriptParameter", bind(WorldEntityCallbacks::getNpcScriptParameter, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<List<Vec2I>, EntityId>("objectSpaces", bind(WorldEntityCallbacks::objectSpaces, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<int>, EntityId>("farmableStage", bind(WorldEntityCallbacks::farmableStage, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<int>, EntityId>("containerSize", bind(WorldEntityCallbacks::containerSize, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, EntityId>("containerClose", bind(WorldEntityCallbacks::containerClose, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, EntityId>("containerOpen", bind(WorldEntityCallbacks::containerOpen, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId>("containerItems", bind(WorldEntityCallbacks::containerItems, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, size_t>("containerItemAt", bind(WorldEntityCallbacks::containerItemAt, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<bool>, EntityId, Json>("containerConsume", bind(WorldEntityCallbacks::containerConsume, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<bool>, EntityId, size_t, int>("containerConsumeAt", bind(WorldEntityCallbacks::containerConsumeAt, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<size_t>, EntityId, Json>("containerAvailable", bind(WorldEntityCallbacks::containerAvailable, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId>("containerTakeAll", bind(WorldEntityCallbacks::containerTakeAll, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, size_t>("containerTakeAt", bind(WorldEntityCallbacks::containerTakeAt, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, size_t, int>("containerTakeNumItemsAt", bind(WorldEntityCallbacks::containerTakeNumItemsAt, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<size_t>, EntityId, Json>("containerItemsCanFit", bind(WorldEntityCallbacks::containerItemsCanFit, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json>("containerItemsFitWhere", bind(WorldEntityCallbacks::containerItemsFitWhere, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json>("containerAddItems", bind(WorldEntityCallbacks::containerAddItems, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json>("containerStackItems", bind(WorldEntityCallbacks::containerStackItems, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json, size_t>("containerPutItemsAt", bind(WorldEntityCallbacks::containerPutItemsAt, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json, size_t>("containerSwapItems", bind(WorldEntityCallbacks::containerSwapItems, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json, size_t>("containerSwapItemsNoCombine", bind(WorldEntityCallbacks::containerSwapItemsNoCombine, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Json, EntityId, Json, size_t>("containerItemApply", bind(WorldEntityCallbacks::containerItemApply, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<LuaValue>, EntityId, String, LuaVariadic<LuaValue>>("callScriptedEntity", bind(WorldEntityCallbacks::callScriptedEntity, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<RpcPromise<Vec2F>, String>("findUniqueEntity", bind(WorldEntityCallbacks::findUniqueEntity, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<RpcPromise<Json>, LuaEngine&, LuaValue, String, LuaVariadic<Json>>("sendEntityMessage", bind(WorldEntityCallbacks::sendEntityMessage, world, _1, _2, _3, _4));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<bool>, EntityId>("loungeableOccupied", bind(WorldEntityCallbacks::loungeableOccupied, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, EntityId, Maybe<bool>>("isMonster", bind(WorldEntityCallbacks::isMonster, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("monsterType", bind(WorldEntityCallbacks::monsterType, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("npcType", bind(WorldEntityCallbacks::npcType, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<Maybe<String>, EntityId>("stagehandType", bind(WorldEntityCallbacks::stagehandType, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, EntityId, Maybe<int>>("isNpc", bind(WorldEntityCallbacks::isNpc, world, _1, _2));
|
|
|
|
callbacks.registerCallback("isEntityInteractive", [world](EntityId entityId) -> Maybe<bool> {
|
|
|
|
if (auto entity = world->get<InteractiveEntity>(entityId))
|
|
|
|
return entity->isInteractive();
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
callbacks.registerCallback("entityMouthPosition", [world](EntityId entityId) -> Maybe<Vec2F> {
|
|
|
|
if (auto entity = world->get<ChattyEntity>(entityId))
|
|
|
|
return entity->mouthPosition();
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
callbacks.registerCallback("entityTypeName", [world](EntityId entityId) -> Maybe<String> {
|
|
|
|
auto entity = world->entity(entityId);
|
|
|
|
if (auto monster = as<Monster>(entity))
|
|
|
|
return monster->typeName();
|
|
|
|
if (auto npc = as<Npc>(entity))
|
|
|
|
return npc->npcType();
|
|
|
|
if (auto vehicle = as<Vehicle>(entity))
|
|
|
|
return vehicle->name();
|
|
|
|
if (auto object = as<Object>(entity))
|
|
|
|
return object->name();
|
|
|
|
if (auto itemDrop = as<ItemDrop>(entity)) {
|
|
|
|
if (itemDrop->item())
|
|
|
|
return itemDrop->item()->name();
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void addWorldEnvironmentCallbacks(LuaCallbacks& callbacks, World* world) {
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F>("lightLevel", bind(WorldEnvironmentCallbacks::lightLevel, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F>("windLevel", bind(WorldEnvironmentCallbacks::windLevel, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F>("breathable", bind(WorldEnvironmentCallbacks::breathable, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F>("underground", bind(WorldEnvironmentCallbacks::underground, world, _1));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaValue, LuaEngine&, Vec2F, String>("material", bind(WorldEnvironmentCallbacks::material, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<LuaValue, LuaEngine&, Vec2F, String>("mod", bind(WorldEnvironmentCallbacks::mod, world, _1, _2, _3));
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F, String>("materialHueShift", bind(WorldEnvironmentCallbacks::materialHueShift, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<float, Vec2F, String>("modHueShift", bind(WorldEnvironmentCallbacks::modHueShift, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<MaterialColorVariant, Vec2F, String>("materialColor", bind(WorldEnvironmentCallbacks::materialColor, world, _1, _2));
|
|
|
|
callbacks.registerCallbackWithSignature<void, Vec2F, String, MaterialColorVariant>("setMaterialColor", bind(WorldEnvironmentCallbacks::setMaterialColor, world, _1, _2, _3));
|
|
|
|
|
|
|
|
callbacks.registerCallback("oceanLevel", [world](Vec2I position) -> int {
|
|
|
|
if (auto serverWorld = as<WorldServer>(world)) {
|
|
|
|
return serverWorld->worldTemplate()->blockInfo(position[0], position[1]).oceanLiquidLevel;
|
|
|
|
} else {
|
|
|
|
auto clientWorld = as<WorldClient>(world);
|
|
|
|
return clientWorld->currentTemplate()->blockInfo(position[0], position[1]).oceanLiquidLevel;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallback("environmentStatusEffects", [world](Vec2F const& position) {
|
|
|
|
return world->environmentStatusEffects(position);
|
|
|
|
});
|
|
|
|
|
|
|
|
callbacks.registerCallbackWithSignature<bool, List<Vec2I>, String, Vec2F, String, float, Maybe<unsigned>, Maybe<EntityId>>("damageTiles", bind(WorldEnvironmentCallbacks::damageTiles, world, _1, _2, _3, _4, _5, _6, _7));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2F, float, String, Vec2F, String, float, Maybe<unsigned>, Maybe<EntityId>>("damageTileArea", bind(WorldEnvironmentCallbacks::damageTileArea, world, _1, _2, _3, _4, _5, _6, _7, _8));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2I, String, String, Maybe<int>, bool>("placeMaterial", bind(WorldEnvironmentCallbacks::placeMaterial, world, _1, _2, _3, _4, _5));
|
|
|
|
callbacks.registerCallbackWithSignature<bool, Vec2I, String, String, Maybe<int>, bool>("placeMod", bind(WorldEnvironmentCallbacks::placeMod, world, _1, _2, _3, _4, _5));
|
|
|
|
|
|
|
|
callbacks.registerCallback("radialTileQuery", [world](Vec2F center, float radius, String layerName) -> List<Vec2I> {
|
|
|
|
auto layer = TileLayerNames.getLeft(layerName);
|
|
|
|
return tileAreaBrush(radius, center, false).filtered([&](Vec2I const& t) -> bool {
|
|
|
|
return world->material(t, layer) != EmptyMaterialId;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldCallbacks::magnitude(World* world, Vec2F pos1, Maybe<Vec2F> pos2) {
|
|
|
|
if (pos2)
|
|
|
|
return world->geometry().diff(pos1, *pos2).magnitude();
|
|
|
|
else
|
|
|
|
return pos1.magnitude();
|
|
|
|
}
|
|
|
|
|
|
|
|
Vec2F WorldCallbacks::distance(World* world, Vec2F const& arg1, Vec2F const& arg2) {
|
|
|
|
return world->geometry().diff(arg1, arg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::polyContains(World* world, PolyF const& poly, Vec2F const& pos) {
|
|
|
|
return world->geometry().polyContains(poly, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaValue WorldCallbacks::xwrap(World* world, LuaEngine& engine, LuaValue const& positionOrX) {
|
|
|
|
if (auto x = engine.luaMaybeTo<float>(positionOrX))
|
|
|
|
return LuaFloat(world->geometry().xwrap(*x));
|
|
|
|
return engine.luaFrom<Vec2F>(world->geometry().xwrap(engine.luaTo<Vec2F>(positionOrX)));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaValue WorldCallbacks::nearestTo(World* world, LuaEngine& engine, Variant<Vec2F, float> const& sourcePositionOrX, Variant<Vec2F, float> const& targetPositionOrX) {
|
|
|
|
if (targetPositionOrX.is<Vec2F>()) {
|
|
|
|
Vec2F targetPosition = targetPositionOrX.get<Vec2F>();
|
|
|
|
Vec2F sourcePosition;
|
|
|
|
if (sourcePositionOrX.is<Vec2F>())
|
|
|
|
sourcePosition = sourcePositionOrX.get<Vec2F>();
|
|
|
|
else
|
|
|
|
sourcePosition[0] = sourcePositionOrX.get<float>();
|
|
|
|
|
|
|
|
return engine.luaFrom<Vec2F>(world->geometry().nearestTo(sourcePosition, targetPosition));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
float targetX = targetPositionOrX.get<float>();
|
|
|
|
float sourceX;
|
|
|
|
if (sourcePositionOrX.is<Vec2F>())
|
|
|
|
sourceX = sourcePositionOrX.get<Vec2F>()[0];
|
|
|
|
else
|
|
|
|
sourceX = sourcePositionOrX.get<float>();
|
|
|
|
|
|
|
|
return LuaFloat(world->geometry().nearestTo(sourceX, targetX));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::rectCollision(World* world, RectF const& arg1, Maybe<CollisionSet> const& arg2) {
|
|
|
|
PolyF body = PolyF(arg1);
|
|
|
|
|
|
|
|
if (arg2)
|
|
|
|
return world->polyCollision(body, *arg2);
|
|
|
|
else
|
|
|
|
return world->polyCollision(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::pointTileCollision(World* world, Vec2F const& arg1, Maybe<CollisionSet> const& arg2) {
|
|
|
|
if (arg2)
|
|
|
|
return world->pointTileCollision(arg1, *arg2);
|
|
|
|
else
|
|
|
|
return world->pointTileCollision(arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::lineTileCollision(
|
|
|
|
World* world, Vec2F const& arg1, Vec2F const& arg2, Maybe<CollisionSet> const& arg3) {
|
|
|
|
Vec2F const begin = arg1;
|
|
|
|
Vec2F const end = arg2;
|
|
|
|
|
|
|
|
if (arg3)
|
|
|
|
return world->lineTileCollision(begin, end, *arg3);
|
|
|
|
else
|
|
|
|
return world->lineTileCollision(begin, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<pair<Vec2F, Vec2I>> WorldCallbacks::lineTileCollisionPoint(World* world, Vec2F const& begin, Vec2F const& end, Maybe<CollisionSet> const& collisionSet) {
|
|
|
|
if (collisionSet)
|
|
|
|
return world->lineTileCollisionPoint(begin, end, *collisionSet);
|
|
|
|
else
|
|
|
|
return world->lineTileCollisionPoint(begin, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::rectTileCollision(World* world, RectF const& arg1, Maybe<CollisionSet> const& arg2) {
|
|
|
|
RectI const region = RectI::integral(arg1);
|
|
|
|
|
|
|
|
if (arg2)
|
|
|
|
return world->rectTileCollision(region, *arg2);
|
|
|
|
else
|
|
|
|
return world->rectTileCollision(region);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::pointCollision(World* world, Vec2F const& point, Maybe<CollisionSet> const& collisionSet) {
|
|
|
|
return world->pointCollision(point, collisionSet.value(DefaultCollisionSet));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTupleReturn<Maybe<Vec2F>, Maybe<Vec2F>> WorldCallbacks::lineCollision(World* world, Vec2F const& start, Vec2F const& end, Maybe<CollisionSet> const& collisionSet) {
|
|
|
|
Maybe<Vec2F> point;
|
|
|
|
Maybe<Vec2F> normal;
|
|
|
|
auto collision = world->lineCollision(Line2F(start, end), collisionSet.value(DefaultCollisionSet));
|
|
|
|
if (collision) {
|
|
|
|
point = collision->first;
|
|
|
|
normal = collision->second;
|
|
|
|
}
|
|
|
|
return luaTupleReturn(point, normal);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::polyCollision(
|
|
|
|
World* world, PolyF const& arg1, Maybe<Vec2F> const& arg2, Maybe<CollisionSet> const& arg3) {
|
|
|
|
PolyF body = arg1;
|
|
|
|
|
|
|
|
Vec2F center;
|
|
|
|
if (arg2) {
|
|
|
|
center = *arg2;
|
|
|
|
body.translate(center);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg3)
|
|
|
|
return world->polyCollision(body, *arg3);
|
|
|
|
else
|
|
|
|
return world->polyCollision(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Vec2I> WorldCallbacks::collisionBlocksAlongLine(
|
|
|
|
World* world, Vec2F const& arg1, Vec2F const& arg2, Maybe<CollisionSet> const& arg3, Maybe<int> const& arg4) {
|
|
|
|
Vec2F const begin = arg1;
|
|
|
|
Vec2F const end = arg2;
|
|
|
|
|
|
|
|
CollisionSet collisionSet = arg3.value(DefaultCollisionSet);
|
|
|
|
int const maxSize = arg4 ? *arg4 : -1;
|
|
|
|
return world->collidingTilesAlongLine(begin, end, collisionSet, maxSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
List<pair<Vec2I, LiquidLevel>> WorldCallbacks::liquidAlongLine(World* world, Vec2F const& start, Vec2F const& end) {
|
|
|
|
List<pair<Vec2I, LiquidLevel>> levels;
|
|
|
|
forBlocksAlongLine<float>(start, world->geometry().diff(end, start), [&](int x, int y) {
|
|
|
|
auto liquidLevel = world->liquidLevel(RectF::withSize(Vec2F(x, y), Vec2F(1, 1)));
|
|
|
|
if (liquidLevel.liquid != EmptyLiquidId)
|
|
|
|
levels.append(pair<Vec2I, LiquidLevel>(Vec2I(x, y), liquidLevel));
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
return levels;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<Vec2F> WorldCallbacks::resolvePolyCollision(
|
|
|
|
World* world, PolyF poly, Vec2F const& position, float maximumCorrection, Maybe<CollisionSet> const& maybeCollisionSet) {
|
|
|
|
struct CollisionPoly {
|
|
|
|
PolyF poly;
|
|
|
|
Vec2F center;
|
|
|
|
float sortingDistance;
|
|
|
|
};
|
|
|
|
|
|
|
|
poly.translate(position);
|
|
|
|
List<CollisionPoly> collisions;
|
|
|
|
CollisionSet collisionSet = maybeCollisionSet.value(DefaultCollisionSet);
|
|
|
|
world->forEachCollisionBlock(RectI::integral(poly.boundBox().padded(maximumCorrection + 1.0f)),
|
|
|
|
[&](auto const& block) {
|
|
|
|
if (collisionSet.contains(block.kind))
|
|
|
|
collisions.append({block.poly, Vec2F(block.space), 0.0f});
|
|
|
|
});
|
|
|
|
|
|
|
|
auto resolveCollision = [&](Maybe<Vec2F> direction, float maximumDistance, int loops) -> Maybe<Vec2F> {
|
|
|
|
PolyF body = poly;
|
|
|
|
Vec2F correction;
|
|
|
|
for (int i = 0; i < loops; ++i) {
|
|
|
|
Vec2F bodyCenter = body.center();
|
|
|
|
for (auto& cp : collisions)
|
|
|
|
cp.sortingDistance = vmagSquared(bodyCenter - cp.center);
|
|
|
|
sort(collisions, [](auto const& a, auto const& b) { return a.sortingDistance < b.sortingDistance; });
|
|
|
|
|
|
|
|
bool anyIntersects = false;
|
|
|
|
for (auto const& cp : collisions) {
|
|
|
|
PolyF::IntersectResult intersection;
|
|
|
|
if (direction)
|
|
|
|
intersection = body.directionalSatIntersection(cp.poly, *direction, false);
|
|
|
|
else
|
|
|
|
intersection = body.satIntersection(cp.poly);
|
|
|
|
|
|
|
|
if (intersection.intersects) {
|
|
|
|
anyIntersects = true;
|
|
|
|
body.translate(intersection.overlap);
|
|
|
|
correction += intersection.overlap;
|
|
|
|
if (vmag(correction) > maximumDistance)
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!anyIntersects)
|
|
|
|
return correction;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto const& cp : collisions) {
|
|
|
|
if (body.intersects(cp.poly))
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return correction;
|
|
|
|
};
|
|
|
|
|
|
|
|
// First try any-directional SAT separation for two loops
|
|
|
|
if (auto resolution = resolveCollision({}, maximumCorrection, 2))
|
|
|
|
return position + *resolution;
|
|
|
|
|
|
|
|
// Then, try direction-limiting SAT in cardinals, then 45 degs, then in
|
|
|
|
// between, for 16 total angles in a circle.
|
|
|
|
for (int i : {4, 8, 12, 0, 2, 6, 10, 14, 1, 3, 7, 5, 15, 13, 9, 11}) {
|
|
|
|
float angle = i * Constants::pi / 8;
|
|
|
|
Vec2F dir = Vec2F::withAngle(angle, 1.0f);
|
|
|
|
if (auto resolution = resolveCollision(dir, maximumCorrection, 1))
|
|
|
|
return position + *resolution;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::tileIsOccupied(
|
|
|
|
World* world, Vec2I const& arg1, Maybe<bool> const& arg2, Maybe<bool> const& arg3) {
|
|
|
|
Vec2I const tile = arg1;
|
|
|
|
bool const tileLayerBool = arg2.value(true);
|
|
|
|
bool const includeEphemeral = arg3.value(false);
|
|
|
|
|
|
|
|
TileLayer const tileLayer = tileLayerBool ? TileLayer::Foreground : TileLayer::Background;
|
|
|
|
|
|
|
|
return world->tileIsOccupied(tile, tileLayer, includeEphemeral);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::placeObject(World* world,
|
|
|
|
String const& objectType,
|
|
|
|
Vec2I const& worldPosition,
|
|
|
|
Maybe<int> const& objectDirection,
|
|
|
|
Json const& objectParameters) {
|
|
|
|
auto objectDatabase = Root::singleton().objectDatabase();
|
|
|
|
|
|
|
|
try {
|
|
|
|
Direction direction = Direction::Right;
|
|
|
|
if (objectDirection && *objectDirection < 0)
|
|
|
|
direction = Direction::Left;
|
|
|
|
|
|
|
|
Json parameters = objectParameters ? objectParameters : JsonObject();
|
|
|
|
|
|
|
|
auto placedObject = objectDatabase->createForPlacement(world, objectType, worldPosition, direction, parameters);
|
|
|
|
if (placedObject) {
|
|
|
|
world->addEntity(placedObject);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (StarException const& exception) {
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::warn("Could not create placable object of kind '{}', exception caught: {}",
|
2023-06-20 04:33:09 +00:00
|
|
|
objectType,
|
|
|
|
outputException(exception, false));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnItem(World* world,
|
|
|
|
Json const& itemType,
|
|
|
|
Vec2F const& worldPosition,
|
|
|
|
Maybe<size_t> const& inputCount,
|
|
|
|
Json const& inputParameters,
|
|
|
|
Maybe<Vec2F> const& initialVelocity,
|
|
|
|
Maybe<float> const& intangibleTime) {
|
|
|
|
Vec2F const position = worldPosition;
|
|
|
|
|
|
|
|
try {
|
|
|
|
ItemDescriptor descriptor;
|
|
|
|
if (itemType.isType(Json::Type::String)) {
|
|
|
|
size_t count = inputCount.value(1);
|
|
|
|
|
|
|
|
Json parameters = inputParameters ? inputParameters : JsonObject();
|
|
|
|
|
|
|
|
descriptor = ItemDescriptor(itemType.toString(), count, parameters);
|
|
|
|
} else {
|
|
|
|
descriptor = ItemDescriptor(itemType);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto itemDrop = ItemDrop::createRandomizedDrop(descriptor, position)) {
|
|
|
|
if (initialVelocity)
|
|
|
|
itemDrop->setVelocity(*initialVelocity);
|
|
|
|
if (intangibleTime)
|
|
|
|
itemDrop->setIntangibleTime(*intangibleTime);
|
|
|
|
world->addEntity(itemDrop);
|
|
|
|
return itemDrop->inWorld() ? itemDrop->entityId() : Maybe<EntityId>();
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::warn("Could not spawn item, item empty in WorldCallbacks::spawnItem");
|
|
|
|
} catch (StarException const& exception) {
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::warn("Could not spawn Item of kind '{}', exception caught: {}", itemType, outputException(exception, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
List<EntityId> WorldCallbacks::spawnTreasure(
|
|
|
|
World* world, Vec2F const& position, String const& pool, float level, Maybe<uint64_t> seed) {
|
|
|
|
List<EntityId> entities;
|
|
|
|
auto treasureDatabase = Root::singleton().treasureDatabase();
|
|
|
|
try {
|
|
|
|
for (auto const& treasureItem : treasureDatabase->createTreasure(pool, level, seed.value(Random::randu64()))) {
|
|
|
|
ItemDropPtr entity = ItemDrop::createRandomizedDrop(treasureItem, position);
|
|
|
|
entities.append(entity->entityId());
|
|
|
|
world->addEntity(entity);
|
|
|
|
}
|
|
|
|
} catch (StarException const& exception) {
|
|
|
|
Logger::warn(
|
2023-06-27 10:23:44 +00:00
|
|
|
"Could not spawn treasure from pool '{}', exception caught: {}", pool, outputException(exception, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
return entities;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnMonster(
|
|
|
|
World* world, String const& arg1, Vec2F const& arg2, Maybe<JsonObject> const& arg3) {
|
|
|
|
Vec2F const spawnPosition = arg2;
|
|
|
|
auto monsterDatabase = Root::singleton().monsterDatabase();
|
|
|
|
|
|
|
|
try {
|
|
|
|
JsonObject parameters;
|
|
|
|
parameters["aggressive"] = Random::randb();
|
|
|
|
if (arg3)
|
|
|
|
parameters.merge(*arg3, true);
|
|
|
|
|
|
|
|
float level = 1;
|
|
|
|
if (parameters.contains("level"))
|
|
|
|
level = parameters.get("level").toFloat();
|
|
|
|
auto monster = monsterDatabase->createMonster(monsterDatabase->randomMonster(arg1, parameters), level);
|
|
|
|
|
|
|
|
monster->setPosition(spawnPosition);
|
|
|
|
world->addEntity(monster);
|
|
|
|
return monster->inWorld() ? monster->entityId() : Maybe<EntityId>();
|
|
|
|
} catch (StarException const& exception) {
|
|
|
|
Logger::warn(
|
2023-06-27 10:23:44 +00:00
|
|
|
"Could not spawn Monster of type '{}', exception caught: {}", arg1, outputException(exception, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnNpc(World* world,
|
|
|
|
Vec2F const& arg1,
|
|
|
|
String const& arg2,
|
|
|
|
String const& arg3,
|
|
|
|
float arg4,
|
|
|
|
Maybe<uint64_t> arg5,
|
|
|
|
Json const& arg6) {
|
|
|
|
Vec2F const spawnPosition = arg1;
|
|
|
|
|
|
|
|
String typeName = arg3;
|
|
|
|
float level = arg4;
|
|
|
|
|
|
|
|
uint64_t seed;
|
|
|
|
if (arg5)
|
|
|
|
seed = *arg5;
|
|
|
|
else
|
|
|
|
seed = Random::randu64();
|
|
|
|
|
|
|
|
Json overrides = arg6 ? arg6 : JsonObject();
|
|
|
|
|
|
|
|
auto npcDatabase = Root::singleton().npcDatabase();
|
|
|
|
try {
|
|
|
|
auto npc = npcDatabase->createNpc(npcDatabase->generateNpcVariant(arg2, typeName, level, seed, overrides));
|
|
|
|
npc->setPosition(spawnPosition);
|
|
|
|
world->addEntity(npc);
|
|
|
|
return npc->inWorld() ? npc->entityId() : Maybe<EntityId>();
|
|
|
|
} catch (StarException const& exception) {
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::warn("Could not spawn NPC of species '{}' and type '{}', exception caught: {}",
|
2023-06-20 04:33:09 +00:00
|
|
|
arg2,
|
|
|
|
typeName,
|
|
|
|
outputException(exception, false));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnStagehand(
|
|
|
|
World* world, Vec2F const& spawnPosition, String const& typeName, Json const& overrides) {
|
|
|
|
auto stagehandDatabase = Root::singleton().stagehandDatabase();
|
|
|
|
try {
|
|
|
|
auto stagehand = stagehandDatabase->createStagehand(typeName, overrides);
|
|
|
|
stagehand->setPosition(spawnPosition);
|
|
|
|
world->addEntity(stagehand);
|
|
|
|
return stagehand->inWorld() ? stagehand->entityId() : Maybe<EntityId>();
|
|
|
|
} catch (StarException const& exception) {
|
|
|
|
Logger::warn(
|
2023-06-27 10:23:44 +00:00
|
|
|
"Could not spawn Stagehand of type '{}', exception caught: {}", typeName, outputException(exception, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnProjectile(World* world,
|
|
|
|
String const& projectileType,
|
|
|
|
Vec2F const& spawnPosition,
|
|
|
|
Maybe<EntityId> const& sourceEntityId,
|
|
|
|
Maybe<Vec2F> const& projectileDirection,
|
|
|
|
bool trackSourceEntity,
|
|
|
|
Json const& projectileParameters) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
auto projectileDatabase = Root::singleton().projectileDatabase();
|
|
|
|
auto projectile = projectileDatabase->createProjectile(projectileType, projectileParameters ? projectileParameters : JsonObject());
|
|
|
|
projectile->setInitialPosition(spawnPosition);
|
|
|
|
projectile->setInitialDirection(projectileDirection.value());
|
|
|
|
projectile->setSourceEntity(sourceEntityId.value(NullEntityId), trackSourceEntity);
|
|
|
|
world->addEntity(projectile);
|
|
|
|
return projectile->inWorld() ? projectile->entityId() : Maybe<EntityId>();
|
|
|
|
} catch (StarException const& exception) {
|
|
|
|
Logger::warn(
|
2023-06-27 10:23:44 +00:00
|
|
|
"Could not spawn Projectile of type '{}', exception caught: {}", projectileType, outputException(exception, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<EntityId> WorldCallbacks::spawnVehicle(
|
|
|
|
World* world, String const& vehicleName, Vec2F const& pos, Json const& extraConfig) {
|
|
|
|
auto vehicleDatabase = Root::singleton().vehicleDatabase();
|
|
|
|
auto vehicle = vehicleDatabase->create(vehicleName, extraConfig);
|
|
|
|
vehicle->setPosition(pos);
|
|
|
|
world->addEntity(vehicle);
|
|
|
|
if (vehicle->inWorld())
|
|
|
|
return vehicle->entityId();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
double WorldCallbacks::time(World* world) {
|
|
|
|
return world->epochTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t WorldCallbacks::day(World* world) {
|
|
|
|
return world->day();
|
|
|
|
}
|
|
|
|
|
|
|
|
double WorldCallbacks::timeOfDay(World* world) {
|
|
|
|
return world->timeOfDay() / world->dayLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldCallbacks::dayLength(World* world) {
|
|
|
|
return world->dayLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldCallbacks::getProperty(World* world, String const& arg1, Json const& arg2) {
|
|
|
|
return world->getProperty(arg1, arg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldCallbacks::setProperty(World* world, String const& arg1, Json const& arg2) {
|
|
|
|
world->setProperty(arg1, arg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<LiquidLevel> WorldCallbacks::liquidAt(World* world, Variant<RectF, Vec2I> boundBoxOrPoint) {
|
|
|
|
LiquidLevel liquidLevel = boundBoxOrPoint.call([world](auto const& bbop) { return world->liquidLevel(bbop); });
|
|
|
|
if (liquidLevel.liquid != EmptyLiquidId)
|
|
|
|
return liquidLevel;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldCallbacks::gravity(World* world, Vec2F const& arg1) {
|
|
|
|
return world->gravity(arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::spawnLiquid(World* world, Vec2F const& position, LiquidId liquid, float quantity) {
|
|
|
|
return world->modifyTile(Vec2I::floor(position), PlaceLiquid{liquid, quantity}, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<LiquidLevel> WorldCallbacks::destroyLiquid(World* world, Vec2F const& position) {
|
|
|
|
auto liquidLevel = world->liquidLevel(Vec2I::floor(position));
|
|
|
|
if (liquidLevel.liquid != EmptyLiquidId) {
|
|
|
|
if (world->modifyTile(Vec2I::floor(position), PlaceLiquid{EmptyLiquidId, 0}, true))
|
|
|
|
return liquidLevel;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldCallbacks::isTileProtected(World* world, Vec2F const& position) {
|
|
|
|
return world->isTileProtected(Vec2I::floor(position));
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<PlatformerAStar::Path> WorldCallbacks::findPlatformerPath(World* world,
|
|
|
|
Vec2F const& start,
|
|
|
|
Vec2F const& end,
|
|
|
|
ActorMovementParameters actorMovementParameters,
|
|
|
|
PlatformerAStar::Parameters searchParameters) {
|
|
|
|
PlatformerAStar::PathFinder pathFinder(world, start, end, move(actorMovementParameters), move(searchParameters));
|
|
|
|
pathFinder.explore({});
|
|
|
|
return pathFinder.result();
|
|
|
|
}
|
|
|
|
|
|
|
|
PlatformerAStar::PathFinder WorldCallbacks::platformerPathStart(World* world,
|
|
|
|
Vec2F const& start,
|
|
|
|
Vec2F const& end,
|
|
|
|
ActorMovementParameters actorMovementParameters,
|
|
|
|
PlatformerAStar::Parameters searchParameters) {
|
|
|
|
return PlatformerAStar::PathFinder(world, start, end, move(actorMovementParameters), move(searchParameters));
|
|
|
|
}
|
|
|
|
|
|
|
|
RectI ClientWorldCallbacks::clientWindow(WorldClient* world) {
|
|
|
|
return world->clientWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerWorldCallbacks::breakObject(WorldServer* world, EntityId arg1, bool arg2) {
|
|
|
|
if (auto entity = world->get<Object>(arg1)) {
|
|
|
|
bool smash = arg2;
|
|
|
|
entity->breakObject(smash);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerWorldCallbacks::isVisibleToPlayer(WorldServer* world, RectF const& arg1) {
|
|
|
|
return world->isVisibleToPlayer(arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerWorldCallbacks::loadRegion(WorldServer* world, RectF const& arg1) {
|
|
|
|
return world->signalRegion(RectI::integral(arg1));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerWorldCallbacks::regionActive(WorldServer* world, RectF const& arg1) {
|
|
|
|
return world->regionActive(RectI::integral(arg1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerWorldCallbacks::setTileProtection(WorldServer* world, DungeonId arg1, bool arg2) {
|
|
|
|
DungeonId dungeonId = arg1;
|
|
|
|
bool isProtected = arg2;
|
|
|
|
world->setTileProtection(dungeonId, isProtected);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerWorldCallbacks::isPlayerModified(WorldServer* world, RectI const& region) {
|
|
|
|
return world->isPlayerModified(region);
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<LiquidLevel> ServerWorldCallbacks::forceDestroyLiquid(WorldServer* world, Vec2F const& position) {
|
|
|
|
auto liquidLevel = world->liquidLevel(Vec2I::floor(position));
|
|
|
|
if (liquidLevel.liquid != EmptyLiquidId) {
|
|
|
|
if (world->forceModifyTile(Vec2I::floor(position), PlaceLiquid{EmptyLiquidId, 0}, true))
|
|
|
|
return liquidLevel;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
EntityId ServerWorldCallbacks::loadUniqueEntity(WorldServer* world, String const& uniqueId) {
|
|
|
|
return world->loadUniqueEntity(uniqueId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerWorldCallbacks::setUniqueId(WorldServer* world, EntityId entityId, Maybe<String> const& uniqueId) {
|
|
|
|
auto entity = world->entity(entityId);
|
|
|
|
if (auto npc = as<Npc>(entity.get()))
|
|
|
|
npc->setUniqueId(uniqueId);
|
|
|
|
else if (auto monster = as<Monster>(entity.get()))
|
|
|
|
monster->setUniqueId(uniqueId);
|
|
|
|
else if (auto object = as<Object>(entity.get()))
|
|
|
|
object->setUniqueId(uniqueId);
|
|
|
|
else if (auto stagehand = as<Stagehand>(entity.get()))
|
|
|
|
stagehand->setUniqueId(uniqueId);
|
|
|
|
else if (entity)
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException::format("Cannot set unique id on entity of type {}", EntityTypeNames.getRight(entity->entityType()));
|
2023-06-20 04:33:09 +00:00
|
|
|
else
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException::format("No such entity with id {}", entityId);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Json ServerWorldCallbacks::takeItemDrop(World* world, EntityId entityId, Maybe<EntityId> const& takenBy) {
|
|
|
|
auto itemDrop = world->get<ItemDrop>(entityId);
|
|
|
|
if (itemDrop && itemDrop->canTake() && itemDrop->isMaster()) {
|
|
|
|
ItemPtr item;
|
|
|
|
if (takenBy)
|
|
|
|
item = itemDrop->takeBy(*takenBy);
|
|
|
|
else
|
|
|
|
item = itemDrop->take();
|
|
|
|
|
|
|
|
if (item)
|
|
|
|
return item->descriptor().toJson();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerWorldCallbacks::setPlayerStart(World* world, Vec2F const& playerStart, Maybe<bool> respawnInWorld) {
|
|
|
|
as<WorldServer>(world)->setPlayerStart(playerStart, respawnInWorld.isValid() && respawnInWorld.value());
|
|
|
|
}
|
|
|
|
|
|
|
|
List<EntityId> ServerWorldCallbacks::players(World* world) {
|
|
|
|
return as<WorldServer>(world)->players();
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaString ServerWorldCallbacks::fidelity(World* world, LuaEngine& engine) {
|
|
|
|
return engine.createString(WorldServerFidelityNames.getRight(as<WorldServer>(world)->fidelity()));
|
|
|
|
}
|
|
|
|
|
2023-07-23 03:49:34 +00:00
|
|
|
Maybe<LuaValue> ServerWorldCallbacks::callScriptContext(World* world, String const& contextName, String const& function, LuaVariadic<LuaValue> const& args) {
|
|
|
|
auto context = as<WorldServer>(world)->scriptContext(contextName);
|
|
|
|
if (!context)
|
|
|
|
throw StarException::format("Context {} does not exist", contextName);
|
|
|
|
return context->invoke(function, args);
|
|
|
|
}
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
void WorldDebugCallbacks::debugPoint(Vec2F const& arg1, Color const& arg2) {
|
|
|
|
SpatialLogger::logPoint("world", arg1, arg2.toRgba());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldDebugCallbacks::debugLine(Vec2F const& arg1, Vec2F const& arg2, Color const& arg3) {
|
|
|
|
SpatialLogger::logLine("world", arg1, arg2, arg3.toRgba());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldDebugCallbacks::debugPoly(PolyF const& poly, Color const& color) {
|
|
|
|
SpatialLogger::logPoly("world", poly, color.toRgba());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldDebugCallbacks::debugText(LuaEngine& engine, LuaVariadic<LuaValue> const& args) {
|
|
|
|
if (args.size() < 3)
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Too few arguments to debugText: {}", args.size()));
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
Vec2F const position = engine.luaTo<Vec2F>(args.at(args.size() - 2));
|
|
|
|
Vec4B const color = engine.luaTo<Color>(args.at(args.size() - 1)).toRgba();
|
|
|
|
|
|
|
|
String text = formatLua(engine.luaTo<String>(args.at(0)), slice<List<LuaValue>>(args, 1, args.size() - 2));
|
|
|
|
SpatialLogger::logText("world", text, position, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::entityQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityQuery<Entity>(world, engine, pos1, pos2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::monsterQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityQuery<Monster>(world, engine, pos1, pos2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::npcQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityQuery<Npc>(world, engine, pos1, pos2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::objectQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
String objectName;
|
|
|
|
if (options)
|
|
|
|
objectName = options->get<Maybe<String>>("name").value();
|
|
|
|
|
|
|
|
return LuaBindings::entityQuery<Object>(world,
|
|
|
|
engine,
|
|
|
|
pos1,
|
|
|
|
pos2,
|
|
|
|
move(options),
|
|
|
|
[&objectName](shared_ptr<Object> const& entity) -> bool {
|
|
|
|
return objectName.empty() || entity->name() == objectName;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::itemDropQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityQuery<ItemDrop>(world, engine, pos1, pos2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::playerQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityQuery<Player>(world, engine, pos1, pos2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::loungeableQuery(World* world, LuaEngine& engine, Vec2F const& pos1, LuaValue const& pos2, Maybe<LuaTable> options) {
|
|
|
|
String orientationName;
|
|
|
|
if (options)
|
|
|
|
orientationName = options->get<Maybe<String>>("orientation").value();
|
|
|
|
|
|
|
|
LoungeOrientation orientation = LoungeOrientation::None;
|
|
|
|
if (orientationName == "sit")
|
|
|
|
orientation = LoungeOrientation::Sit;
|
|
|
|
else if (orientationName == "lay")
|
|
|
|
orientation = LoungeOrientation::Lay;
|
|
|
|
else if (orientationName == "stand")
|
|
|
|
orientation = LoungeOrientation::Stand;
|
|
|
|
else if (orientationName.empty())
|
|
|
|
orientation = LoungeOrientation::None;
|
|
|
|
else
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported loungeableQuery orientation {}", orientationName));
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
auto filter = [orientation](shared_ptr<LoungeableObject> const& entity) -> bool {
|
|
|
|
auto loungeable = as<LoungeableEntity>(entity);
|
|
|
|
if (!loungeable || loungeable->anchorCount() == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (orientation == LoungeOrientation::None)
|
|
|
|
return true;
|
|
|
|
auto pos = loungeable->loungeAnchor(0);
|
|
|
|
return pos && pos->orientation == orientation;
|
|
|
|
};
|
|
|
|
|
|
|
|
return LuaBindings::entityQuery<LoungeableObject>(world, engine, pos1, pos2, move(options), filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::entityLineQuery(World* world, LuaEngine& engine, Vec2F const& point1, Vec2F const& point2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityLineQuery<Entity>(world, engine, point1, point2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::objectLineQuery(World* world, LuaEngine& engine, Vec2F const& point1, Vec2F const& point2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityLineQuery<Object>(world, engine, point1, point2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaTable WorldEntityCallbacks::npcLineQuery(World* world, LuaEngine& engine, Vec2F const& point1, Vec2F const& point2, Maybe<LuaTable> options) {
|
|
|
|
return LuaBindings::entityLineQuery<Npc>(world, engine, point1, point2, move(options));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::entityExists(World* world, EntityId entityId) {
|
|
|
|
return (bool)world->entity(entityId);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::entityCanDamage(World* world, EntityId sourceId, EntityId targetId) {
|
|
|
|
auto source = world->entity(sourceId);
|
|
|
|
auto target = world->entity(targetId);
|
|
|
|
|
|
|
|
if (!source || !target || !source->getTeam().canDamage(target->getTeam(), false))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::entityDamageTeam(World* world, EntityId entityId) {
|
|
|
|
if (auto entity = world->entity(entityId))
|
|
|
|
return entity->getTeam().toJson();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::entityAggressive(World* world, EntityId entityId) {
|
|
|
|
auto entity = world->entity(entityId);
|
|
|
|
if (auto monster = as<Monster>(entity))
|
|
|
|
return monster->aggressive();
|
|
|
|
if (auto npc = as<Npc>(entity))
|
|
|
|
return npc->aggressive();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<LuaString> WorldEntityCallbacks::entityType(World* world, LuaEngine& engine, EntityId entityId) {
|
|
|
|
if (auto entity = world->entity(entityId))
|
|
|
|
return engine.createString(EntityTypeNames.getRight(entity->entityType()));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<Vec2F> WorldEntityCallbacks::entityPosition(World* world, EntityId entityId) {
|
|
|
|
if (auto entity = world->entity(entityId)) {
|
|
|
|
return entity->position();
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<RectF> WorldEntityCallbacks::entityMetaBoundBox(World* world, EntityId entityId) {
|
|
|
|
if (auto entity = world->entity(entityId)) {
|
|
|
|
return entity->metaBoundBox();
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<Vec2F> WorldEntityCallbacks::entityVelocity(World* world, EntityId entityId) {
|
|
|
|
auto entity = world->entity(entityId);
|
|
|
|
|
|
|
|
if (auto monsterEntity = as<Monster>(entity))
|
|
|
|
return monsterEntity->velocity();
|
|
|
|
else if (auto npcEntity = as<Npc>(entity))
|
|
|
|
return npcEntity->velocity();
|
|
|
|
else if (auto playerEntity = as<Player>(entity))
|
|
|
|
return playerEntity->velocity();
|
|
|
|
else if (auto vehicleEntity = as<Vehicle>(entity))
|
|
|
|
return vehicleEntity->velocity();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<uint64_t> WorldEntityCallbacks::entityCurrency(World* world, EntityId entityId, String const& currencyType) {
|
|
|
|
if (auto player = world->get<Player>(entityId)) {
|
|
|
|
return player->currency(currencyType);
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<uint64_t> WorldEntityCallbacks::entityHasCountOfItem(World* world, EntityId entityId, Json descriptor, Maybe<bool> exactMatch) {
|
|
|
|
if (auto player = world->get<Player>(entityId)) {
|
|
|
|
return player->inventory()->hasCountOfItem(ItemDescriptor(descriptor), exactMatch.value(false));
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<Vec2F> WorldEntityCallbacks::entityHealth(World* world, EntityId entityId) {
|
|
|
|
if (auto entity = world->get<DamageBarEntity>(entityId)) {
|
|
|
|
return Vec2F(entity->health(), entity->maxHealth());
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::entitySpecies(World* world, EntityId entityId) {
|
|
|
|
if (auto player = world->get<Player>(entityId)) {
|
|
|
|
return player->species();
|
|
|
|
} else if (auto npc = world->get<Npc>(entityId)) {
|
|
|
|
return npc->species();
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::entityGender(World* world, EntityId entityId) {
|
|
|
|
if (auto player = world->get<Player>(entityId)) {
|
|
|
|
return GenderNames.getRight(player->gender());
|
|
|
|
} else if (auto npc = world->get<Npc>(entityId)) {
|
|
|
|
return GenderNames.getRight(npc->gender());
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::entityName(World* world, EntityId entityId) {
|
|
|
|
auto entity = world->entity(entityId);
|
|
|
|
|
|
|
|
if (auto portraitEntity = as<PortraitEntity>(entity)) {
|
|
|
|
return portraitEntity->name();
|
|
|
|
} else if (auto objectEntity = as<Object>(entity)) {
|
|
|
|
return objectEntity->name();
|
|
|
|
} else if (auto itemDropEntity = as<ItemDrop>(entity)) {
|
|
|
|
if (itemDropEntity->item())
|
|
|
|
return itemDropEntity->item()->name();
|
|
|
|
} else if (auto vehicleEntity = as<Vehicle>(entity)) {
|
|
|
|
return vehicleEntity->name();
|
|
|
|
} else if (auto stagehandEntity = as<Stagehand>(entity)) {
|
|
|
|
return stagehandEntity->typeName();
|
|
|
|
} else if (auto projectileEntity = as<Projectile>(entity)) {
|
|
|
|
return projectileEntity->typeName();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::entityDescription(World* world, EntityId entityId, Maybe<String> const& species) {
|
|
|
|
if (auto entity = world->entity(entityId)) {
|
|
|
|
if (auto inspectableEntity = as<InspectableEntity>(entity)) {
|
|
|
|
if (species)
|
|
|
|
return inspectableEntity->inspectionDescription(*species);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entity->description();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-07-24 14:49:20 +00:00
|
|
|
LuaNullTermWrapper<Maybe<List<Drawable>>> WorldEntityCallbacks::entityPortrait(World* world, EntityId entityId, String const& portraitMode) {
|
|
|
|
if (auto portraitEntity = as<PortraitEntity>(world->entity(entityId)))
|
|
|
|
return portraitEntity->portrait(PortraitModeNames.getLeft(portraitMode));
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::entityHandItem(World* world, EntityId entityId, String const& handName) {
|
|
|
|
ToolHand toolHand;
|
|
|
|
if (handName == "primary") {
|
|
|
|
toolHand = ToolHand::Primary;
|
|
|
|
} else if (handName == "alt") {
|
|
|
|
toolHand = ToolHand::Alt;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unknown tool hand {}", handName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (auto entity = world->get<ToolUserEntity>(entityId)) {
|
|
|
|
if (auto item = entity->handItem(toolHand)) {
|
|
|
|
return item->name();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::entityHandItemDescriptor(World* world, EntityId entityId, String const& handName) {
|
|
|
|
ToolHand toolHand;
|
|
|
|
if (handName == "primary") {
|
|
|
|
toolHand = ToolHand::Primary;
|
|
|
|
} else if (handName == "alt") {
|
|
|
|
toolHand = ToolHand::Alt;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unknown tool hand {}", handName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (auto entity = world->get<ToolUserEntity>(entityId)) {
|
|
|
|
if (auto item = entity->handItem(toolHand)) {
|
|
|
|
return item->descriptor().toJson();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
2023-07-24 13:37:55 +00:00
|
|
|
LuaNullTermWrapper<Maybe<String>> WorldEntityCallbacks::entityUniqueId(World* world, EntityId entityId) {
|
2023-06-20 04:33:09 +00:00
|
|
|
if (auto entity = world->entity(entityId))
|
|
|
|
return entity->uniqueId();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::getObjectParameter(World* world, EntityId entityId, String const& parameterName, Maybe<Json> const& defaultValue) {
|
|
|
|
Json val = Json();
|
|
|
|
|
|
|
|
if (auto objectEntity = as<Object>(world->entity(entityId))) {
|
|
|
|
val = objectEntity->configValue(parameterName);
|
|
|
|
if (!val && defaultValue)
|
|
|
|
val = *defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::getNpcScriptParameter(World* world, EntityId entityId, String const& parameterName, Maybe<Json> const& defaultValue) {
|
|
|
|
Json val = Json();
|
|
|
|
|
|
|
|
if (auto npcEntity = as<Npc>(world->entity(entityId))) {
|
|
|
|
val = npcEntity->scriptConfigParameter(parameterName);
|
|
|
|
if (!val && defaultValue)
|
|
|
|
val = *defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Vec2I> WorldEntityCallbacks::objectSpaces(World* world, EntityId entityId) {
|
|
|
|
if (auto tileEntity = as<TileEntity>(world->entity(entityId)))
|
|
|
|
return tileEntity->spaces();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<int> WorldEntityCallbacks::farmableStage(World* world, EntityId entityId) {
|
|
|
|
if (auto farmable = world->get<FarmableObject>(entityId)) {
|
|
|
|
return farmable->stage();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<int> WorldEntityCallbacks::containerSize(World* world, EntityId entityId) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId))
|
|
|
|
return container->containerSize();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::containerClose(World* world, EntityId entityId) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
container->containerClose();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::containerOpen(World* world, EntityId entityId) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
container->containerOpen();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerItems(World* world, EntityId entityId) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
JsonArray res;
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
for (auto const& item : container->itemBag()->items())
|
|
|
|
res.append(itemDb->toJson(item));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerItemAt(World* world, EntityId entityId, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto items = container->itemBag()->items();
|
|
|
|
if (offset < items.size()) {
|
|
|
|
return itemDb->toJson(items.at(offset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<bool> WorldEntityCallbacks::containerConsume(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto toConsume = ItemDescriptor(items);
|
|
|
|
return container->consumeItems(toConsume).result();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<bool> WorldEntityCallbacks::containerConsumeAt(World* world, EntityId entityId, size_t offset, int count) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
return container->consumeItems(offset, count).result();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<size_t> WorldEntityCallbacks::containerAvailable(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemBag = container->itemBag();
|
|
|
|
auto toCheck = ItemDescriptor(items);
|
|
|
|
return itemBag->available(toCheck);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerTakeAll(World* world, EntityId entityId) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
if (auto itemList = container->clearContainer().result()) {
|
|
|
|
JsonArray res;
|
|
|
|
for (auto item : *itemList)
|
|
|
|
res.append(itemDb->toJson(item));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerTakeAt(World* world, EntityId entityId, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->takeItems(offset).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerTakeNumItemsAt(World* world, EntityId entityId, size_t offset, int const& count) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->takeItems(offset, count).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<size_t> WorldEntityCallbacks::containerItemsCanFit(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto itemBag = container->itemBag();
|
|
|
|
auto toSearch = itemDb->fromJson(items);
|
|
|
|
return itemBag->itemsCanFit(toSearch);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerItemsFitWhere(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto itemBag = container->itemBag();
|
|
|
|
auto toSearch = itemDb->fromJson(items);
|
|
|
|
auto res = itemBag->itemsFitWhere(toSearch);
|
|
|
|
return JsonObject{
|
|
|
|
{"leftover", res.leftover},
|
|
|
|
{"slots", jsonFromList<size_t>(res.slots)}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return Json();
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerAddItems(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toInsert = itemDb->fromJson(items);
|
|
|
|
if (auto res = container->addItems(toInsert).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerStackItems(World* world, EntityId entityId, Json const& items) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toInsert = itemDb->fromJson(items);
|
|
|
|
if (auto res = container->addItems(toInsert).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerPutItemsAt(World* world, EntityId entityId, Json const& items, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toInsert = itemDb->fromJson(items);
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->putItems(offset, toInsert).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerSwapItems(World* world, EntityId entityId, Json const& items, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toSwap = itemDb->fromJson(items);
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->swapItems(offset, toSwap, true).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerSwapItemsNoCombine(World* world, EntityId entityId, Json const& items, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toSwap = itemDb->fromJson(items);
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->swapItems(offset, toSwap, false).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json WorldEntityCallbacks::containerItemApply(World* world, EntityId entityId, Json const& items, size_t offset) {
|
|
|
|
if (auto container = world->get<ContainerObject>(entityId)) {
|
|
|
|
auto itemDb = Root::singleton().itemDatabase();
|
|
|
|
auto toSwap = itemDb->fromJson(items);
|
|
|
|
if (offset < container->containerSize()) {
|
|
|
|
if (auto res = container->swapItems(offset, toSwap, false).result())
|
|
|
|
return itemDb->toJson(*res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<LuaValue> WorldEntityCallbacks::callScriptedEntity(World* world, EntityId entityId, String const& function, LuaVariadic<LuaValue> const& args) {
|
|
|
|
auto entity = as<ScriptedEntity>(world->entity(entityId));
|
|
|
|
if (!entity || !entity->isMaster())
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException::format("Entity {} does not exist or is not a local master scripted entity", entityId);
|
2023-06-20 04:33:09 +00:00
|
|
|
return entity->callScript(function, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
RpcPromise<Vec2F> WorldEntityCallbacks::findUniqueEntity(World* world, String const& uniqueId) {
|
|
|
|
return world->findUniqueEntity(uniqueId);
|
|
|
|
}
|
|
|
|
|
|
|
|
RpcPromise<Json> WorldEntityCallbacks::sendEntityMessage(World* world, LuaEngine& engine, LuaValue entityId, String const& message, LuaVariadic<Json> args) {
|
|
|
|
if (entityId.is<LuaString>())
|
|
|
|
return world->sendEntityMessage(engine.luaTo<String>(entityId), message, JsonArray::from(move(args)));
|
|
|
|
else
|
|
|
|
return world->sendEntityMessage(engine.luaTo<EntityId>(entityId), message, JsonArray::from(move(args)));
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<bool> WorldEntityCallbacks::loungeableOccupied(World* world, EntityId entityId) {
|
|
|
|
auto entity = world->get<LoungeableEntity>(entityId);
|
|
|
|
if (entity && entity->anchorCount() > 0)
|
|
|
|
return !entity->entitiesLoungingIn(0).empty();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::isMonster(World* world, EntityId entityId, Maybe<bool> const& aggressive) {
|
|
|
|
if (auto entity = world->get<Monster>(entityId))
|
|
|
|
return !aggressive || *aggressive == entity->aggressive();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::monsterType(World* world, EntityId entityId) {
|
|
|
|
if (auto monster = world->get<Monster>(entityId))
|
|
|
|
return monster->typeName();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::npcType(World* world, EntityId entityId) {
|
|
|
|
if (auto npc = world->get<Npc>(entityId))
|
|
|
|
return npc->npcType();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<String> WorldEntityCallbacks::stagehandType(World* world, EntityId entityId) {
|
|
|
|
if (auto stagehand = world->get<Stagehand>(entityId))
|
|
|
|
return stagehand->typeName();
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEntityCallbacks::isNpc(World* world, EntityId entityId, Maybe<int> const& damageTeam) {
|
|
|
|
if (auto entity = world->get<Npc>(entityId)) {
|
|
|
|
return !damageTeam || *damageTeam == entity->getTeam().team;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldEnvironmentCallbacks::lightLevel(World* world, Vec2F const& position) {
|
|
|
|
return world->lightLevel(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldEnvironmentCallbacks::windLevel(World* world, Vec2F const& position) {
|
|
|
|
return world->windLevel(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::breathable(World* world, Vec2F const& position) {
|
|
|
|
return world->breathable(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::underground(World* world, Vec2F const& position) {
|
|
|
|
return world->isUnderground(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaValue WorldEnvironmentCallbacks::material(World* world, LuaEngine& engine, Vec2F const& position, String const& layerName) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported material layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto materialId = world->material(Vec2I::floor(position), layer);
|
|
|
|
if (materialId == NullMaterialId) {
|
|
|
|
return LuaNil;
|
|
|
|
} else if (materialId == EmptyMaterialId) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
auto materialDatabase = Root::singleton().materialDatabase();
|
|
|
|
return engine.createString(materialDatabase->materialName(materialId));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaValue WorldEnvironmentCallbacks::mod(World* world, LuaEngine& engine, Vec2F const& position, String const& layerName) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported mod layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto modId = world->mod(Vec2I::floor(position), layer);
|
|
|
|
if (isRealMod(modId)) {
|
|
|
|
auto materialDatabase = Root::singleton().materialDatabase();
|
|
|
|
return engine.createString(materialDatabase->modName(modId));
|
|
|
|
}
|
|
|
|
|
|
|
|
return LuaNil;
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldEnvironmentCallbacks::materialHueShift(World* world, Vec2F const& position, String const& layerName) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported material layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return world->materialHueShift(Vec2I::floor(position), layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
float WorldEnvironmentCallbacks::modHueShift(World* world, Vec2F const& position, String const& layerName) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported material layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return world->modHueShift(Vec2I::floor(position), layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
MaterialColorVariant WorldEnvironmentCallbacks::materialColor(World* world, Vec2F const& position, String const& layerName) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported material layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return world->colorVariant(Vec2I::floor(position), layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorldEnvironmentCallbacks::setMaterialColor(World* world, Vec2F const& position, String const& layerName, MaterialColorVariant color) {
|
|
|
|
TileLayer layer;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unsupported material layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
world->modifyTile(Vec2I::floor(position), PlaceMaterialColor{layer, color}, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::damageTiles(World* world,
|
|
|
|
List<Vec2I> const& arg1,
|
|
|
|
String const& arg2,
|
|
|
|
Vec2F const& arg3,
|
|
|
|
String const& arg4,
|
|
|
|
float arg5,
|
|
|
|
Maybe<unsigned> const& arg6,
|
|
|
|
Maybe<EntityId> sourceEntity) {
|
|
|
|
List<Vec2I> tilePositions = arg1;
|
|
|
|
|
|
|
|
TileLayer layer;
|
|
|
|
auto layerName = arg2;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-08-19 08:26:52 +00:00
|
|
|
throw StarException(strf("Unsupported tile layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned harvestLevel = 999;
|
|
|
|
if (arg6)
|
|
|
|
harvestLevel = *arg6;
|
|
|
|
|
|
|
|
auto tileDamage = TileDamage(TileDamageTypeNames.getLeft(arg4), arg5, harvestLevel);
|
|
|
|
auto res = world->damageTiles(tilePositions, layer, arg3, tileDamage, sourceEntity);
|
|
|
|
return res != TileDamageResult::None;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::damageTileArea(World* world,
|
|
|
|
Vec2F center,
|
|
|
|
float radius,
|
|
|
|
String layer,
|
|
|
|
Vec2F sourcePosition,
|
|
|
|
String damageType,
|
|
|
|
float damage,
|
|
|
|
Maybe<unsigned> const& harvestLevel,
|
|
|
|
Maybe<EntityId> sourceEntity) {
|
|
|
|
auto tiles = tileAreaBrush(radius, center, false);
|
|
|
|
return damageTiles(world, tiles, layer, sourcePosition, damageType, damage, harvestLevel, sourceEntity);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::placeMaterial(World* world, Vec2I const& arg1, String const& arg2, String const& arg3, Maybe<int> const& arg4, bool arg5) {
|
|
|
|
auto tilePosition = arg1;
|
|
|
|
|
|
|
|
PlaceMaterial placeMaterial;
|
|
|
|
|
2023-08-19 08:26:52 +00:00
|
|
|
std::string layerName = arg2.utf8();
|
|
|
|
auto split = layerName.find_first_of('+');
|
|
|
|
if (split != NPos) {
|
|
|
|
auto overrideName = layerName.substr(split + 1);
|
|
|
|
layerName = layerName.substr(0, split);
|
|
|
|
if (overrideName == "empty" || overrideName == "none")
|
|
|
|
placeMaterial.collisionOverride = TileCollisionOverride::Empty;
|
2023-08-19 10:47:58 +00:00
|
|
|
else if (overrideName == "block")
|
|
|
|
placeMaterial.collisionOverride = TileCollisionOverride::Block;
|
2023-08-19 08:26:52 +00:00
|
|
|
else if (overrideName == "platform")
|
|
|
|
placeMaterial.collisionOverride = TileCollisionOverride::Platform;
|
|
|
|
else
|
|
|
|
throw StarException(strf("Unsupported collision override {}", overrideName));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (layerName == "foreground")
|
2023-06-20 04:33:09 +00:00
|
|
|
placeMaterial.layer = TileLayer::Foreground;
|
2023-08-19 08:26:52 +00:00
|
|
|
else if (layerName == "background")
|
2023-06-20 04:33:09 +00:00
|
|
|
placeMaterial.layer = TileLayer::Background;
|
2023-08-19 08:26:52 +00:00
|
|
|
else
|
|
|
|
throw StarException(strf("Unsupported tile layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
|
|
|
|
auto materialName = arg3;
|
|
|
|
auto materialDatabase = Root::singleton().materialDatabase();
|
|
|
|
if (!materialDatabase->materialNames().contains(materialName))
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unknown material name {}", materialName));
|
2023-06-20 04:33:09 +00:00
|
|
|
placeMaterial.material = materialDatabase->materialId(materialName);
|
|
|
|
|
|
|
|
if (arg4)
|
|
|
|
placeMaterial.materialHueShift = (MaterialHue)*arg4;
|
|
|
|
|
|
|
|
bool allowOverlap = arg5;
|
|
|
|
|
|
|
|
return world->modifyTile(tilePosition, placeMaterial, allowOverlap);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WorldEnvironmentCallbacks::placeMod(World* world, Vec2I const& arg1, String const& arg2, String const& arg3, Maybe<int> const& arg4, bool arg5) {
|
|
|
|
auto tilePosition = arg1;
|
|
|
|
|
|
|
|
PlaceMod placeMod;
|
|
|
|
|
|
|
|
auto layerName = arg2;
|
|
|
|
if (layerName == "foreground") {
|
|
|
|
placeMod.layer = TileLayer::Foreground;
|
|
|
|
} else if (layerName == "background") {
|
|
|
|
placeMod.layer = TileLayer::Background;
|
|
|
|
} else {
|
2023-08-19 08:26:52 +00:00
|
|
|
throw StarException(strf("Unsupported tile layer {}", layerName));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto modName = arg3;
|
|
|
|
auto materialDatabase = Root::singleton().materialDatabase();
|
|
|
|
if (!materialDatabase->modNames().contains(modName))
|
2023-06-27 10:23:44 +00:00
|
|
|
throw StarException(strf("Unknown mod name {}", modName));
|
2023-06-20 04:33:09 +00:00
|
|
|
placeMod.mod = materialDatabase->modId(modName);
|
|
|
|
|
|
|
|
if (arg4)
|
|
|
|
placeMod.modHueShift = (MaterialHue)*arg4;
|
|
|
|
|
|
|
|
bool allowOverlap = arg5;
|
|
|
|
|
|
|
|
return world->modifyTile(tilePosition, placeMod, allowOverlap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|