# Modding and behavior changes This document briefly documents what have been added (or removed) regarding modding capabilities or engine behavior(s) ## JSON additions --------------- ### Worldgen * Where applicable, Perlin noise now can have custom seed specified * Change above allows to explicitly specify universe seed (as `celestial.config:systemTypePerlin:seed`) * `treasurechests` now can specify `treasurePool` as array #### Terrain * All composing terrain selectors (such as `min`, `displacement`, `rotate`, etc) now can reference other terrain selectors by name (the `.terrain` files) instead of embedding entire config inside them * They can be referenced by either specifying corresponding field as string, or as object like so: `{"name": "namedselector"}` * `min`, `max` and `minmax` terrain selectors now also accept next format: `{"name": "namedselector", "seedBias": 4}` * `mix` terrain selector got `mixSeedBias`, `aSeedBias` and `bSeedBias` fields, whose deviate respective selectors seeds (default to `0`) * `displacement` terrain selector has `seedBias` added, which deviate seed of `source` selector (default to `0`) * `displacement` terrain selector has `xClamp` added, works like `yClamp` * `rotate` terrain selector has `rotationWidth` (defaults to `0.5`) and `rotationHeight` (defaults to `0.0`) added, which are multiplied by world's size and world's height respectively to determine rotation point center * `min` terrain selector added, opposite of existing `max` (json format is the same as `max`) * `cache` terrain selector removed due it not being documented, and having little practical value * `perlin` terrain selector now accepts `type`, `frequency` and `amplitude` values (naming inconsistency fix) * `ridgeblocks` terrain selector now accepts `amplitude` and `frequency` values (naming inconsistency fix); * `ridgeblocks` has `octaves` added (defaults to `2`), `perlinOctaves` (defaults to `1`) #### Biomes * Tree biome placeables now have `variantsRange` (defaults to `[1, 1]`) and `subVariantsRange` (defaults to `[2, 2]`) * `variantsRange` is responsible for "stem-foliage" combinations * `subVariantsRange` is responsible for "stem-foliage" hue shift combinations * Rolled per each "stem-foliage" combination * Also two more properties were added: `sameStemHueShift` (defaults to `true`) and `sameFoliageHueShift` (defaults to `false`), which fixate hue shifts within same "stem-foliage" combination * Original engine always generates two tree types when processing placeable items, new engine however, allows to generate any number of trees. #### Dungeons * `front` and `back` brushes now can properly accept detailed data as json object on second position (e.g. `["front", { "material": ... }]`), with following structure (previously, due to oversight in code, it was impossible to specify this structure through any means, because brush definition itself can't be an object): ```kotlin val material: Registry.Ref = BuiltinMetaMaterials.EMPTY.ref val modifier: Registry.Ref = BuiltinMetaMaterials.EMPTY_MOD.ref val hueShift: Float = 0f val modHueShift: Float = 0f val color: TileColor = TileColor.DEFAULT ``` * `item` brush now can accept proper item descriptors (in json object tag), * Previous behavior remains unchanged (if specified as string, creates _randomized_ item, if as object, creates _exactly_ what have been specified) * To stop randomizing as Tiled tileset brush, specify `"dont_randomize"` as anything (e.g. as `""`) * `liquid` brush now can accept 'level' as second argument * Previous behavior is unchanged, `["liquid", "water", true]` will result into infinite water as before, but `["liquid", "water", 0.5, false]` will spawn half-filled water * In tiled, you already can do this using `"quantity"` property * `dungeonid` brush has been hooked up to legacy dungeons and now can be directly specified inside `"brush"` (previously they were only accessible when using Tiled' tilesets). * By default, they mark entire _part_ of dungeon with their ID. To mark specific tile inside dungeon with its own Dungeon ID, supply `true` as third value to brush (e.g `["dungeonid", 40000, true"]`) * Tiled map behavior is unchanged, and marks their position only. --------------- ### player.config * Inventory bags are no longer limited to 255 slots * However, when joining original servers with mod which increase bag size past 255 slots will result in undefined behavior (joining servers with inventory size bag mods will already result in nearly instant desync though, so you may not ever live to see the side effects; and if original server installs said mod, original clients and original server will experience severe desyncs/undefined behavior too) --------------- ### Prototypes * `damageTable` can be defined directly, without referencing other JSON file (experimental feature) #### Items * `inventoryIcon` additions if specified as array: * `scale`, either as float or as vector (for x and y scales); both in prototype file and in `parameters`. * `color` (defaults to white `[255, 255, 255, 255]`) * `rotation` (in degrees, defaults to `0`) * `mirrored` (defaults to `false`, this is different from setting scale to `-1f` since it obeys center point) * `centered` (defaults to `true`) * `fullbright` (defaults to `false`) #### .liquid * `liquidId` is no longer essential and can be skipped; engine **will not** assign it to anything, but liquid will still be fully functional from engine's point of view * However, this has serious implications: * Liquid will become "invisible" to legacy clients (this is not guaranteed, and if it ever "bleeds" into structures sent to legacy clients due to missed workarounds in code, legacy client will blow up.) * Lua scripts written solely for original engine won't see this liquid too (this includes base game assets!), unless they use new improved functions * `liquidId` can be specified as any number in 1 -> 2^31 - 1 range (0 is reserved for "empty" meta-liquid) * This will make liquid "invisible" to original clients only, Lua code should continue to function normally * This is not guaranteed, and if it ever "bleeds" into structures sent to legacy clients due to missed workarounds in code, legacy client will blow up. #### .matierial * Meta-materials are no longer treated uniquely, and are defined as "real" materials, just like every other material, but still preserve unique interactions. * `materialId` is no longer essential and can be skipped, with same notes as described in `liquidId`. * `materialId` can be specified as any number in 1 -> 2^31 - 1 (softly excluding reserved "meta materials" ID range, since this range is not actually reserved, but is expected to be used solely by meta materials), with legacy client implications only. * Implemented `isConnectable`, which was planned by original developers, but scrapped in process (defaults to `true`, by default only next meta-materials have it set to false: `empty`, `null` and `boundary`) * Used by object and plant anchoring code to determine valid placement * Used by world tile rendering code (render piece rule `Connects`) * And finally, used by `canPlaceMaterial` to determine whenever player can place blocks next to it (at least one such tile should be present for player to be able to place blocks next to it) #### .matmod * `modId` is no longer essential and can be skipped, or specified as any number in 1 -> 2^31 range, with notes of `materialId` and `liquidId` apply. ## Scripting --------------- #### Random * Added `random:randn(deviation: double, mean: double): double`, returns normally distributed double, where `deviation` stands for [Standard deviation](https://en.wikipedia.org/wiki/Standard_deviation), and `mean` specifies middle point * Removed `random:addEntropy` #### animator * Added `animator.targetRotationAngle(rotationGroup: string): double` * Added `animator.hasRotationGroup(rotationGroup: string): boolean` * Added `animator.rotationGroups(): List` (returns valid names for `rotateGroup`, `currentRotationAngle` and `targetRotationAngle`) * Added `animator.transformationGroups(): List` * Added `animator.particleEmitters(): List` * Added `animator.hasParticleEmitter(emitter: string): boolean` * Added `animator.lights(): List` * Added `animator.hasLight(light: string): boolean` * Added `animator.sounds(): List` * Added `animator.effects(): List` * Added `animator.hasEffect(effect: string): boolean` * Added `animator.parts(): List` #### world * Added `world.liquidNamesAlongLine(start: Vector2d, end: Vector2d): List`, will return Liquid' name instead of its ID * Added `world.liquidNameAt(at: Vector2i): LiquidState?`, will return Liquid' name instead of its ID * Added `world.biomeBlockNamesAt(at: Vector2i): List?`, will return Block names instead of their IDs * Added `world.destroyNamedLiquid(at: Vector2i): LiquidState?`, will return Liquid' name instead of its ID * Added `world.gravityVector(at: Vector2d): Vector2d`. **Attention:** directional gravity is WIP. * Added `world.itemDropLineQuery(p0: Vector2d, p1: Vector2d, options: Table?): List` * Added `world.playerLineQuery(p0: Vector2d, p1: Vector2d, options: Table?): List` * Added `world.objectLineQuery(p0: Vector2d, p1: Vector2d, options: Table?): List` * Added `world.loungeableLineQuery(p0: Vector2d, p1: Vector2d, options: Table?): List` * `world.entityCanDamage(source: EntityID, target: EntityID): Boolean` now properly accounts for case when `source == target` ## Behavior --------------- ### universe_server.config * Added `useNewWireProcessing`, which defaults to `true` * New wire updating system is insanely fast (because wiring is updated along entity ticking, and doesn't involve intense entity map lookups) * However, it is not a complete replacement for legacy system, because some mods might rely on fact that in legacy system when wired entities update, they load all other endpoints into memory (basically, chunkload all connected entities). In new system if wired entity references unloaded entities it simply does not update its state. * If specified as `false`, original behavior will be restored, but beware of performance degradation! If you are a modder, **PLEASE** consider other ways around instead of enabling the old behavior, because performance cost of using old system is almost always gonna outweight "benefits" of chunkloaded wiring systems. ### Worldgen * Major dungeon placement on planets is now deterministic * Container item population in dungeons is now deterministic and is based on dungeon seed * However, this might backfire, if you specify `seed` inside `/instance_worlds.config`; since that will set dungeon's contents in stone (don't do this, remove seed from your dungeon data, please. Both original and new engines will provide random seed for you on each world generation if you remove your own seed from data) #### Dungeons * All brushes are now deterministic