Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarObjectDatabase.hpp
blob: b4314455f368655c1a857febd8c2f9a0946b51c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#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, NetCompatibilityRules rules = {}) 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;
};

}