Version 1.4.0 released!

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 destroyEnemyEntities(bool isKill)

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 (pickup sfx

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 Collider2D.SetLogicLayerPlayerShotNoFruit()

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(alue, min, max` instea

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
