osb/source/game/StarObjectDatabase.hpp

222 lines
6.7 KiB
C++

#pragma once
#include "StarPeriodicFunction.hpp"
#include "StarTtlCache.hpp"
#include "StarGameTypes.hpp"
#include "StarItemDescriptor.hpp"
#include "StarParticle.hpp"
#include "StarSet.hpp"
#include "StarTileDamage.hpp"
#include "StarDamageTypes.hpp"
#include "StarStatusTypes.hpp"
#include "StarEntityRendering.hpp"
#include "StarTileEntity.hpp"
namespace Star {
STAR_CLASS(World);
STAR_CLASS(Image);
STAR_CLASS(ItemDatabase);
STAR_CLASS(RecipeDatabase);
STAR_CLASS(Object);
STAR_STRUCT(ObjectOrientation);
STAR_STRUCT(ObjectConfig);
STAR_CLASS(ObjectDatabase);
STAR_EXCEPTION(ObjectException, StarException);
struct ObjectOrientation {
struct Anchor {
TileLayer layer;
Vec2I position;
bool tilled;
bool soil;
Maybe<MaterialId> material;
};
struct ParticleEmissionEntry {
float particleEmissionRate;
float particleEmissionRateVariance;
// Particle positions are considered relative to image pixels, and are
// flipped with image flipping
Particle particle;
Particle particleVariance;
bool placeInSpaces;
};
// The JSON values that were used to configure this orientation.
Json config;
EntityRenderLayer renderLayer;
List<Drawable> imageLayers;
bool flipImages;
// Offset of image from (0, 0) object position, in tile coordinates
Vec2F imagePosition;
// If an object has frames > 1, then the image name will have the marker
// "{frame}" replaced with an integer in [0, frames)
unsigned frames;
float animationCycle;
// Spaces the object occupies. By default, this is simply the single space
// at the object position, but can be specified in config as either a list of
// Vec2I, or by setting a threshold value using "spaceScanning", which will
// scan the image (frame 1) for non-transparent pixels.
List<Vec2I> spaces;
RectI boundBox;
// Allow an orientation to override the metaboundbox in case you don't want to
// specify spaces
Maybe<RectF> metaBoundBox;
// Anchors of the object to place it in the world
// For background tiles set in order for the object to
// remain placed. Must be within 1 space of the bounding box of spaces.
// For foreground tiles this cannot logically contain any position
// also in spaces, as objects cannot overlap with foreground tiles.
List<Anchor> anchors;
// if true, only one anchor needs to be valid for the orientation to be valid,
// otherwise all anchors must be valid
bool anchorAny;
Maybe<Direction> directionAffinity;
// Optional list of material spaces
List<MaterialSpace> materialSpaces;
// optionally override the default spaces used for interaction
Maybe<List<Vec2I>> interactiveSpaces;
Vec2F lightPosition;
float beamAngle;
List<ParticleEmissionEntry> particleEmitters;
Maybe<PolyF> statusEffectArea;
Json touchDamageConfig;
static ParticleEmissionEntry parseParticleEmitter(String const& path, Json const& config);
bool placementValid(World const* world, Vec2I const& position) const;
bool anchorsValid(World const* world, Vec2I const& position) const;
};
// TODO: This is used very strangely and inconsistently. We go to all the trouble of populating
// this ObjectConfig structure from the JSON, but then keep around the JSON anyway. In some
// places we access the objectConfig, but in many more we use the object's configValue method
// to access the raw config JSON which means it's inconsistent which parameters can be overridden
// by instance values at various levels. This whole system needs reevaluation.
struct ObjectConfig {
// Returns the index of the best valid orientation. If no orientations are
// valid, returns NPos
size_t findValidOrientation(World const* world, Vec2I const& position, Maybe<Direction> directionAffinity = Maybe<Direction>()) const;
String path;
// The JSON values that were used to configure this Object
Json config;
String name;
String type;
String race;
String category;
StringList colonyTags;
StringList scripts;
StringList animationScripts;
unsigned price;
bool printable;
bool scannable;
bool interactive;
StringMap<Color> lightColors;
LightType lightType;
float pointBeam;
float beamAmbience;
Maybe<PeriodicFunction<float>> lightFlickering;
String soundEffect;
float soundEffectRangeMultiplier;
List<PersistentStatusEffect> statusEffects;
Json touchDamageConfig;
bool hasObjectItem;
bool retainObjectParametersInItem;
bool smashable;
bool smashOnBreak;
bool unbreakable;
String smashDropPool;
List<List<ItemDescriptor>> smashDropOptions;
StringList smashSoundOptions;
JsonArray smashParticles;
String breakDropPool;
List<List<ItemDescriptor>> breakDropOptions;
TileDamageParameters tileDamageParameters;
float damageShakeMagnitude;
String damageMaterialKind;
EntityDamageTeam damageTeam;
Maybe<float> minimumLiquidLevel;
Maybe<float> maximumLiquidLevel;
float liquidCheckInterval;
float health;
Json animationConfig;
List<ObjectOrientationPtr> orientations;
// If true, the object will root - it will prevent the blocks it is
// anchored to from being destroyed directly, and damage from those
// blocks will be redirected to the object
bool rooting;
bool biomePlaced;
};
class ObjectDatabase {
public:
static List<Vec2I> scanImageSpaces(ImageConstPtr const& image, Vec2F const& position, float fillLimit, bool flip = false);
static Json parseTouchDamage(String const& path, Json const& touchDamage);
static List<ObjectOrientationPtr> parseOrientations(String const& path, Json const& configList);
ObjectDatabase();
void cleanup();
StringList allObjects() const;
bool isObject(String const& name) const;
ObjectConfigPtr getConfig(String const& objectName) const;
List<ObjectOrientationPtr> const& getOrientations(String const& objectName) const;
ObjectPtr createObject(String const& objectName, Json const& objectParameters = JsonObject()) const;
ObjectPtr diskLoadObject(Json const& diskStore) const;
ObjectPtr netLoadObject(ByteArray const& netStore) const;
bool canPlaceObject(World const* world, Vec2I const& position, String const& objectName) const;
// If the object is placeable in the given position, creates the given object
// and sets its position and direction and returns it, otherwise returns
// null.
ObjectPtr createForPlacement(World const* world, String const& objectName, Vec2I const& position,
Direction direction, Json const& parameters = JsonObject()) const;
List<Drawable> cursorHintDrawables(World const* world, String const& objectName, Vec2I const& position,
Direction direction, Json parameters = {}) const;
private:
static ObjectConfigPtr readConfig(String const& path);
StringMap<String> m_paths;
mutable Mutex m_cacheMutex;
mutable HashTtlCache<String, ObjectConfigPtr> m_configCache;
};
}