Full notes
Full Platypus Reclayed update
Read the full published notes in a cleaner layout. The original post stays linked below.
What changed
- Gameplay
- Performance
- UI and audio
- Workshop
- Fixes
- Compatibility
Version 1.4.0 has landed!
This update focuses on a whole host of new modding features and some fixes sprinkled in too.
New Level commands
Added `waitForNoSpecificEntity(string entity, int timeout = -1)`
Added `ifNotSpecificEntityOnScreen(string entity, int places)`
Added `ifCreditCountBelow(int creditCount, int places)`
Added `ifCreditCountAbove(int creditCount, int places)`
Added `ifCreditCountEqual(int creditCount, int places)`
Added `ifPlayerWeaponIs(int weaponID, int places)`
Added `ifPlayerHasPods(int places)`
Added `destroyEntityOfType(string entityName, bool isKill)`
Added `destroyEntitiesOfType(string entityName, bool isKill)`
Added [c]destroyEnemyEntities(bool isKill)[/c]
Audio Definitions
Added `s_pause`: this is a duplicate of `s_star1`
Added `s_exit_attract`: this is a duplicate of `s_star1`
Added `s_ui_main_toggle_sfx_setting`: this is a duplicate of `s_star1`
Added optional `s_fruit_cherry`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_cherry_small`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_bananas`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_bananas_small`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_grapes`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_grapes_small`: if not defined, we simply fall back to `s_fruit` (pickup sfx)
Added optional `s_fruit_coin`: if not defined, we simply fall back to `s_fruit[c] (pickup sfx[/c]
Lua API
Piped lua's default print function to the game's log
Made use of JSONObject fallbacks in `saucer.lua`'s commandArgs
Changed `SpawnEntityWorld(entityName, worldPosition, arguments)`: Now returns an integer, this is the entityID
Changed `SpawnEntityWorld(entityName, worldPosition, arguments)`: Argument parameter is now optional (no longer requiring creation of an empty JSONObject)
Changed `SpawnEntityLocal(entityName, localPosition, arguments)`: Now returns an integer, this is the entityID
Changed `SpawnEntityLocal(entityName, localPosition, arguments)`: Argument parameter is now optional (no longer requiring creation of an empty JSONObject)
Changed `SpawnEntityChild(entityName, parent, positionOffset, arguments)`: Now returns an integer, this is the entityID
Changed `SpawnEntityChild(entityName, parent, positionOffset, arguments)`: Argument parameter is now optional (no longer requiring creation of an empty JSONObject)
Added `GetEntity(entityID)`
Changed `GetPlayer(index)`: Now expects an integer
Added `MoveTowardsAngle(current, target, maxDelta)`
Added `DeltaAngle(current, target)`
Added `IsOriginalVersion()`
Added `GetSpriteDimensions(animationName, spriteIndex)`
Added `MakeBonuses(x, y, fruitSetID)`
Added `AdjustXToWideScreen(x)`
Added `NewDiffDictInt(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictFloat(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictBool(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictString(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictIntArray(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictFloatArray(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictBoolArray(veryEasy, easy, normal, hard, nasty)`
Added `NewDiffDictStringArray(veryEasy, easy, normal, hard, nasty)`
Added `Globals.isPassiveResistance`
Added `Globals.trainEntities`
Added `Globals.trainBonus`
Added `Globals.trainCounter`
Added `Globals.custom`
Fixed `Globals.ScrollingSpeed(layer)` being nil
Fixed `Globals.firewait` being nil
Fixed `Globals.levelLifetime` being nil
Fixed `Globals.createSplashes` being nil
Fixed `Globals.bonusScores` being nil
Fixed `Globals.layerScaleValues` being nil
Fixed `Globals.horizonLevels` being nil
Fixed `Globals.layerReflections` being nil
Fixed `Globals.firingChanceSaucer` being nil
Fixed `Globals.firingChanceRandomFishFighter` being nil
Added `SpriteAnimator.worldPosition`
Added `SpriteAnimator.rotation`
Added `SpriteAnimator.enabled`
Added `Player.lastUpdateTime`
Added `Player.nextUpdateTime`
Added `Player.lastPosition`
Added `Player.nextPosition`
Added `Player.position`
Added `Player.worldPosition`
Added `Player.isActive`
Deprecated `Player.GetPosition()`: use `Player.position` instead
Deprecated `Player.GetWorldPosition()`: use `Player.worldPosition` instead
Added `BaseEntity.customBehaviourData`
Added `BaseEntity.entityID`
Added `BaseEntity.lastUpdateTime`
Added `BaseEntity.nextUpdateTime`
Added `BaseEntity.lastPosition`
Added `BaseEntity.nextPosition`
Added `BaseEntity.rotation`
Added `BaseEntity.defaultOnHitByBulletBehaviour`
Added `BaseEntity.defaultOnHitByPlayerBehaviour`
Added `BaseEntity.BulletConsume(consumer)`
Added `BaseEntity.BulletGetKillCreditIndex()`
Added `BaseEntity.EndKillTimer()`
Fixed `BaseEntity.GetDamageFrame(maxHitPoints, hitPoints, frameCount)` being nil
Fixed `BaseEntity.CheckCollision(collider, playerCollisionCallback?, bulletCollisionCallback?)` behaving incorrectly when passing nil as callbacks
Fixed `BaseEntity.SpawnShipDebris(count, mxMin, mxMax, myMin, myMax, xOffset, yOffset, mxSpawnDirectionMin, mxSpawnDirectionMax, mySpawnDirectionMin, mySpawnDirectionMax)` not spawning secondary debris
Changed `JSONObject.GetFieldString(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldString(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldFloat(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldFloat(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldInt(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldInt(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldBool(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldBool(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldStringArray(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldStringArray(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldFloatArray(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldFloatArray(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldIntArray(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldIntArray(my_name, my_fallback_value)`
Changed `JSONObject.GetFieldBoolArray(name)`: To take an additional `fallback?` parameter: e.g. `GetFieldBoolArray(my_name, my_fallback_value)`
Removed `FirePattern.NewFirePatternFromEntityData(data)`: It was nil before, so no compatibility breaks
Added `TurretData.CalculateBulletParams(worldPosition, angleOffset)`
Added `Collider2D.boolean`
Added `Collider2D.SetLogicLayerDefault()`
Added `Collider2D.SetLogicLayerTransparentFX()`
Added `Collider2D.SetLogicLayerIgnoreRaycast()`
Added `Collider2D.SetLogicLayerWater()`
Added `Collider2D.SetLogicLayerUI()`
Added `Collider2D.SetLogicLayerPlayer()`
Added `Collider2D.SetLogicLayerPlayerShot()`
Added `Collider2D.SetLogicLayerEnemy()`
Added `Collider2D.SetLogicLayerEnemyShot()`
Added `Collider2D.SetLogicLayerLoadScreen()`
Added `Collider2D.SetLogicLayerPlayerPickupCollider()`
Added `Collider2D.SetLogicLayerPickup()`
Added [c]Collider2D.SetLogicLayerPlayerShotNoFruit()[/c]
Lua globals.lua
Defined `AnimationType`: Enum for usable values (previously only in the documentation)
Defined `GameDifficulty`: Enum for usable values (previously only in the documentation)
Defined `ScoreSource`: Enum for usable values (previously only in the documentation)
Added `RandRangeF(min, max)`
Added `RandRange(min, max)`
Added `Round(value)`
Added `Clamp(value, min, max)`
Added `Lerp(min, max, t)`
Added `CreateTurret(entity, xOff, yOff, parent, waiter)`
Deprecated `round(value)`: Use `Round(value)` instead
Deprecated `clamp(value, min, max)`: Use `Clamp([c]alue, min, max` instea[/c]
Example scripts
Implemented `car.lua` example script
This is the stage one boss in the game
Example of turret entities, in this case spawning them manually
Example of Global counters
Example of multi entity enemies
Example of a boss fight
Example of custom hit responses using custom colliders
Example of linked enemies (movement depending on the next segment)
Example of `BaseEntity.IsKilledManually`
Example of spawning other entities
Example of `EntityData.customBehaviourData`
Implemented `enemy_bullet.lua` example script
This is the basic bullets fired by saucers and other enemies
Example of `EntityData.customBehaviourData`
Example of manual sprite frame handling
Example of player dependent logic
Implemented `reclayed_turret.lua` example script
This is the base for most of the turrets used
Example of TurretData
Example of FirePattern
Example of stationary enemy, designed to be attached to another
Example of properly spawning bullets
Example of `EntityData.customBehaviourData`
Example of player dependent logic
Misc
Fixed Boss 1 sometimes not sorting correctly, this was visible on the chain element
Fixed `EnemyHomerBullet` corrupting when multiple entities used it with differing `customBehaviourData`
Exposed `speed` and `tickTurnDegrees` as `customBehaviourData` in `HomingMissile` behaviour
Increased class strictness in LLS definition `types.lua` (Will now warn you if you try to use a field that doesn't exist)
Added `Trail` behaviour, functioning as a way to create customizable trails for any entity. See `"exampleTrail"` in `entities.json`
Fixed `Lua` behaviours not defaulting to the default layer when reused
Fixed `Lua` cleaning up their functions too early (the game expects function pointers to still be valid during the frame kill was called)
Updated documentation
Source
Changelog.gg summarizes and formats this update. How we read updates.
