New Upstream Release - surgescript

Ready changes

Summary

Merged new upstream version: 0.5.6.1 (was: 0.5.4.4).

Resulting package

Built on 2022-12-24T20:58 (took 4m25s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases libsurgescript-devapt install -t fresh-releases libsurgescript0.5.4.4-dbgsymapt install -t fresh-releases libsurgescript0.5.4.4apt install -t fresh-releases surgescript-dbgsymapt install -t fresh-releases surgescript

Lintian Result

Diff

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..490a5ae
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: alemart # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.gitignore b/.gitignore
index 77575e0..e97e3d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,11 @@
 !.vscode/launch.json
 !.vscode/extensions.json
 
+# msvc
+.vs/
+out/
+CMakeSettings.json
+
 # vim
 # swap
 [._]*.s[a-v][a-z]
@@ -22,14 +27,7 @@ Session.vim
 *~
 
 # mine
-surgescript
-*.a
-*.so
-*.so.*
-*.dll
-*.exe
-*.pc
-site/
+docs_html/
 build/
 private/
 CMakeFiles/
diff --git a/CHANGES.md b/CHANGES.md
index 6d8c7ee..2e28dba 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,25 @@
 # Release Notes
 
+## 0.5.6.1 - September 22nd, 2022
+
+* Tweaks to the build system
+
+## 0.5.6 - September 1st, 2022
+
+* Improved the SurgeScript CLI with a time limit option, the ability to run scripts from stdin and optional multithreading support
+* Added Visual Studio support (Cody Licorish)
+* Added Emscripten support
+* Updated docs
+* General improvements
+
+## 0.5.5 - January 22nd, 2021
+
+* Added the ability to pause the SurgeScript VM
+* Added utility macros for checking the SurgeScript version at compile time
+* Introduced a dedicated module for keeping track of time
+* Renamed Object.childCount to Object.__childCount
+* Updated docs
+
 ## 0.5.4.4 - April 16th, 2020
 
 * Added LIB_SUFFIX compilation option
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d004e8..e6442a3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,43 +1,40 @@
 # ------------------------------------------------------------------------------
 # SurgeScript
 # A scripting language for games
-# Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+# Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
 # ------------------------------------------------------------------------------
 
 # Project info
 cmake_minimum_required(VERSION 3.2)
 project(
     surgescript
-    VERSION 0.5.4.4
+    VERSION 0.5.6.1
     LANGUAGES C
 )
+include(GNUInstallDirs)
+include(CheckLibraryExists)
+set(PROJECT_YEARS "2016-2022")
 
 # Default config
-set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD 11)
 if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
     set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build: Debug | Release | MinSizeRel | RelWithDebInfo" FORCE)
     set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
 endif()
-if(UNIX AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
-  set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Install path prefix (prepended onto install directories)" FORCE)
-endif()
 
 # User options
 option(WANT_SHARED "Build SurgeScript as a shared library" ON)
 option(WANT_STATIC "Build SurgeScript as a static library" ON)
-option(WANT_EXECUTABLE "Build the surgescript executable" ON)
-set(LIB_SUFFIX "" CACHE STRING "Suffix to append to 'lib' directories, e.g., '64'") # libs must be installed to "lib64" in some systems
-set(PKGCONFIG_PATH "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/pkgconfig" CACHE PATH "Destination folder of the pkg-config (.pc) file")
+option(WANT_EXECUTABLE "Build the SurgeScript CLI" ON)
+option(WANT_EXECUTABLE_MULTITHREAD "Enable multithreading on the SurgeScript CLI" ON)
+set(PKGCONFIG_PATH "pkgconfig" CACHE PATH "Destination folder of the pkg-config (.pc) file")
 if(UNIX)
-    set(METAINFO_PATH "/usr/share/metainfo" CACHE PATH "Destination folder of the metainfo file")
-    set(ICON_PATH "/usr/share/pixmaps" CACHE PATH "Destination folder of the icon file")
-    file(TO_CMAKE_PATH "${ICON_PATH}/surgescript.png" ICON_FILEPATH)
+    set(METAINFO_PATH "metainfo" CACHE PATH "Destination folder of the metainfo file")
+    set(ICON_PATH "pixmaps" CACHE PATH "Destination folder of the icon file")
 endif()
 
-# Output folder
-set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR})
-set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
-set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
+# Library search
+CHECK_LIBRARY_EXISTS(m sqrt "${CMAKE_SYSTEM_LIBRARY_PATH}" SURGESCRIPT_libm_EXISTS)
 
 # Sources
 set(
@@ -75,11 +72,12 @@ set(
     src/surgescript/runtime/tag_system.c
     src/surgescript/runtime/variable.c
     src/surgescript/runtime/vm.c
+    src/surgescript/runtime/vm_time.c
     src/surgescript/util/transform.c
     src/surgescript/util/utf8.c
     src/surgescript/util/util.c
     src/surgescript/util/xoroshiro128plus.c
-    ${CMAKE_CURRENT_BINARY_DIR}/info.c
+    ${CMAKE_BINARY_DIR}/src/surgescript/misc/info.c
 )
 
 # Headers
@@ -103,6 +101,7 @@ set(
     src/surgescript/runtime/tag_system.h
     src/surgescript/runtime/variable.h
     src/surgescript/runtime/vm.h
+    src/surgescript/runtime/vm_time.h
     src/surgescript/util/fasthash.h
     src/surgescript/util/ssarray.h
     src/surgescript/util/transform.h
@@ -112,9 +111,12 @@ set(
     src/surgescript.h
 )
 
+# Output folder
+set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
+
 # Generate files from templates
 function(generate_file TEMPLATE)
-    configure_file(src/surgescript/misc/${TEMPLATE}.in ${TEMPLATE} @ONLY)
+    configure_file(src/surgescript/${TEMPLATE}.in "${CMAKE_BINARY_DIR}/src/surgescript/${TEMPLATE}" @ONLY)
 endfunction()
 
 function(generate_pc_file LINKAGE)
@@ -123,68 +125,120 @@ function(generate_pc_file LINKAGE)
         set(LIB_LINKAGE "-static")
     endif()
     configure_file(src/surgescript/misc/surgescript.pc.in "${LIBRARY_OUTPUT_PATH}/surgescript${LIB_LINKAGE}.pc" @ONLY)
-    install(FILES "${LIBRARY_OUTPUT_PATH}/surgescript${LIB_LINKAGE}.pc" DESTINATION "${PKGCONFIG_PATH}")
+    install(FILES "${LIBRARY_OUTPUT_PATH}/surgescript${LIB_LINKAGE}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PKGCONFIG_PATH}")
 endfunction()
 
-generate_file("info.c")
+generate_file("misc/info.c")
+generate_file("util/version.h")
 
+# Use relative paths in __FILE__
+function(drop_compilation_paths TARGET)
+    if(NOT MSVC)
+        target_compile_options(${TARGET} PUBLIC "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
+    endif()
+endfunction()
 
-# Library
+# Build the library
 if(NOT WANT_SHARED AND NOT WANT_STATIC)
     message(FATAL_ERROR "Options WANT_SHARED and WANT_STATIC are both set to OFF. Nothing to do.")
 endif()
 
 if(WANT_SHARED)
+    set(LIB_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") # x.y.z: backwards compatibility
     message(STATUS "Will build libsurgescript")
     generate_pc_file("shared")
     add_library(surgescript SHARED ${SURGESCRIPT_SOURCES} ${SURGESCRIPT_HEADERS})
-    target_link_libraries(surgescript m)
-    set_target_properties(surgescript PROPERTIES VERSION ${PROJECT_VERSION})
-    install(TARGETS surgescript DESTINATION "lib${LIB_SUFFIX}")
+    if (SURGESCRIPT_libm_EXISTS)
+        target_link_libraries(surgescript m)
+    endif()
+    set_target_properties(surgescript PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${LIB_SOVERSION})
+    drop_compilation_paths(surgescript)
 endif()
 
 if(WANT_STATIC)
     message(STATUS "Will build libsurgescript-static")
     generate_pc_file("static")
     add_library(surgescript-static STATIC ${SURGESCRIPT_SOURCES} ${SURGESCRIPT_HEADERS})
-    target_link_libraries(surgescript-static m)
+    if (SURGESCRIPT_libm_EXISTS)
+        target_link_libraries(surgescript-static m)
+    endif ()
     set_target_properties(surgescript-static PROPERTIES VERSION ${PROJECT_VERSION})
-    install(TARGETS surgescript-static DESTINATION "lib${LIB_SUFFIX}")
+    drop_compilation_paths(surgescript-static)
+endif()
+
+# Install the headers
+install(DIRECTORY src/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h")
+install(DIRECTORY "${CMAKE_BINARY_DIR}/src/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h")
+
+# Install the library
+if(WANT_SHARED)
+    install(TARGETS surgescript DESTINATION "${CMAKE_INSTALL_LIBDIR}")
 endif()
 
-install(DIRECTORY src/ DESTINATION include FILES_MATCHING PATTERN "*.h") # installing the headers
+if(WANT_STATIC)
+    install(TARGETS surgescript-static DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
 
-# Executable
+# Build the SurgeScript CLI
 if(WANT_EXECUTABLE)
+    message(STATUS "Will build the SurgeScript CLI")
+
     # Set the appropriate lib
     set(LIBSURGESCRIPT "surgescript")
     if(WANT_STATIC AND (NOT WANT_SHARED OR WIN32))
         set(LIBSURGESCRIPT "surgescript-static")
     endif()
 
+    # Use multithreading?
+    set(LIBTHREAD "")
+    set(ENABLE_THREADS 0)
+    if(WANT_EXECUTABLE_MULTITHREAD)
+
+        # Header search
+        find_path(THREADS_H NAMES "threads.h" PATHS "${CMAKE_INCLUDE_PATH}")
+        if(NOT THREADS_H)
+            message(WARNING "Can't find threads.h. Will not use multithreading on the SurgeScript CLI")
+        else()
+            message(STATUS "Will use multithreading on the SurgeScript CLI")
+            set(ENABLE_THREADS 1)
+
+            # hmmmm...
+            set(LIBTHREAD "pthread")
+        endif()
+
+    endif()
+
     # Create the executable
-    message(STATUS "Will build surgescript")
     add_executable(surgescript.bin src/main.c)
-    target_link_libraries(surgescript.bin ${LIBSURGESCRIPT})
+    include_directories("${CMAKE_BINARY_DIR}/src")
+    target_compile_definitions(surgescript.bin PUBLIC ENABLE_THREADS=${ENABLE_THREADS})
+    target_link_libraries(surgescript.bin ${LIBSURGESCRIPT} ${LIBTHREAD})
     target_include_directories(surgescript.bin PRIVATE src)
     set_target_properties(surgescript.bin PROPERTIES OUTPUT_NAME surgescript)
+    drop_compilation_paths(surgescript.bin)
+
+    # WebAssembly
+    if(EMSCRIPTEN)
+        target_link_options(surgescript.bin PRIVATE -Os --closure 1 -sMODULARIZE=1 -sMALLOC="emmalloc" -sEXPORTED_FUNCTIONS=["_main"]) # Reduce output size
+    endif()
 
     # Windows icon
     if(WIN32 AND MINGW)
         if(NOT CMAKE_RC_COMPILER)
             set(CMAKE_RC_COMPILER windres)
         endif()
-        execute_process(COMMAND "${CMAKE_RC_COMPILER}" -O coff -o "${CMAKE_CURRENT_BINARY_DIR}/iconwin.res" -i "${CMAKE_SOURCE_DIR}/src/surgescript/misc/iconwin.rc" -I "${CMAKE_SOURCE_DIR}")
-        set_target_properties(surgescript.bin PROPERTIES LINK_FLAGS "-static-libgcc \"${CMAKE_CURRENT_BINARY_DIR}/iconwin.res\"")
+        execute_process(COMMAND "${CMAKE_RC_COMPILER}" -O coff -o "${CMAKE_BINARY_DIR}/iconwin.res" -i "${CMAKE_SOURCE_DIR}/src/surgescript/misc/iconwin.rc" -I "${CMAKE_SOURCE_DIR}")
+        set_target_properties(surgescript.bin PROPERTIES LINK_FLAGS "-static-libgcc \"${CMAKE_BINARY_DIR}/iconwin.res\"")
     endif()
 
     # *nix metadata & icon
     if(UNIX)
-        generate_file("surgescript.appdata.xml")
-        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/surgescript.appdata.xml DESTINATION "${METAINFO_PATH}")
-        install(FILES src/surgescript/misc/surgescript.png DESTINATION "${ICON_PATH}")
+        file(TO_CMAKE_PATH "${CMAKE_INSTALL_DATADIR}/${ICON_PATH}/surgescript.png" ICON_FILEPATH)
+        generate_file("misc/surgescript.appdata.xml")
+        install(FILES "${CMAKE_BINARY_DIR}/src/surgescript/misc/surgescript.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATADIR}/${METAINFO_PATH}")
+        install(FILES src/surgescript/misc/surgescript.png DESTINATION "${CMAKE_INSTALL_DATADIR}/${ICON_PATH}")
     endif()
 
     # Installing the executable
-    install(TARGETS surgescript.bin DESTINATION bin)
+    install(TARGETS surgescript.bin DESTINATION "${CMAKE_INSTALL_BINDIR}")
 endif()
diff --git a/debian/changelog b/debian/changelog
index e2c9307..c00a491 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+surgescript (0.5.6.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sat, 24 Dec 2022 20:55:11 -0000
+
 surgescript (0.5.4.4-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/docs/engine/actor.md b/docs/engine/actor.md
index 7c77f2e..680d1fb 100644
--- a/docs/engine/actor.md
+++ b/docs/engine/actor.md
@@ -52,24 +52,12 @@ A shortcut to `animation.id`: an integer corresponding to the animation number.
 
 Reference to the Animation object of the Actor.
 
-#### alpha
-
-`alpha`: number.
-
-Opacity value, ranging from zero (0% opaque) to one (100% opaque). Defaults to 1.0.
-
 #### entity
 
 `entity`: object, read-only.
 
 The entity associated with this component.
 
-#### offset
-
-`offset`: [Vector2](/engine/vector2) object.
-
-A *(x,y)* offset relative to the parent object. Defaults to zero.
-
 #### hflip
 
 `hflip`: boolean.
@@ -88,6 +76,50 @@ Should the actor be flipped vertically? Defaults to `false`.
 
 Should the actor be rendered? Defaults to `true`.
 
+#### alpha
+
+`alpha`: number.
+
+Opacity value, ranging from zero (0% opaque) to one (100% opaque). Defaults to 1.0.
+
+#### offset
+
+`offset`: [Vector2](/engine/vector2) object.
+
+A *(x,y)* offset relative to the parent object. Defaults to zero.
+
+#### anchor
+
+`anchor`: [Vector2](/engine/vector2) object.
+
+A shortcut to `animation.anchor`. See also: [anchor](/engine/animation#anchor).
+
+*Available since:* Open Surge 0.6.0
+
+#### hotSpot
+
+`hotSpot`: [Vector2](/engine/vector2) object.
+
+A shortcut to `animation.hotSpot`. See also: [hot spot](/engine/animation#hotspot).
+
+*Available since:* Open Surge 0.6.0. In versions prior to 0.6.0, you may get the hot spot using the [Animation](/engine/animation#hotspot) object.
+
+#### actionSpot
+
+`actionSpot`: [Vector2](/engine/vector2) object.
+
+A shortcut to `animation.actionSpot`. See also: [action spot](/engine/animation#actionspot).
+
+*Available since:* Open Surge 0.6.0
+
+#### actionOffset
+
+`actionOffset`: [Vector2](/engine/vector2) object, read-only.
+
+A shortcut to `animation.actionOffset`. See also: [action offset](/engine/animation#actionoffset).
+
+*Available since:* Open Surge 0.6.0
+
 #### width
 
 `width`: number, read-only.
diff --git a/docs/engine/animation.md b/docs/engine/animation.md
index c4f93f2..b311e2c 100644
--- a/docs/engine/animation.md
+++ b/docs/engine/animation.md
@@ -27,7 +27,7 @@ Properties
 
 `id`: number.
 
-The number of the current animation, defined in a .spr file.
+The number of the animation, defined in a .spr file.
 
 #### sprite
 
@@ -39,7 +39,7 @@ The name of the sprite, defined in a .spr file.
 
 `exists`: boolean, read-only.
 
-Will be `true` if the current animation exists, i.e., if its sprite and its animation number have been defined in a .spr file.
+Will be `true` if the animation exists, i.e., if its sprite and its animation number have been defined in a .spr file.
 
 *Available since:* Open Surge 0.5.1
 
@@ -47,31 +47,57 @@ Will be `true` if the current animation exists, i.e., if its sprite and its anim
 
 `finished`: boolean, read-only.
 
-Will be `true` if the current animation has finished playing.
+Will be `true` if the animation has finished playing.
 
-#### hotspot
+#### anchor
 
-`hotspot`: [Vector2](/engine/vector2) object, read-only.
+`anchor`: [Vector2](/engine/vector2) object, read-only.
 
-The position of the hot spot of the current animation.
+The hot spot of the animation normalized to [0,1] x [0,1].
+
+*Available since:* Open Surge 0.6.0
+
+#### hotSpot
+
+`hotSpot`: [Vector2](/engine/vector2) object, read-only.
+
+The hot spot of the animation. Coordinates are given in pixels.
+
+*Note:* prior to Open Surge 0.6.0, this property was called `hotspot`.
+
+#### actionSpot
+
+`actionSpot`: [Vector2](/engine/vector2) object, read-only.
+
+The action spot of the animation. Coordinates are given in pixels. If the sprite is flipped, the action spot is automatically flipped relative to the [hot spot](#hotspot) of the animation.
+
+*Available since:* Open Surge 0.6.0
+
+#### actionOffset
+
+`actionOffset`: [Vector2](/engine/vector2) object, read-only.
+
+When this vector is added to the position of the sprite, you'll get the position of the [action spot](#actionspot). This is suitable to be used with [transform.localPosition](/engine/transform#localposition).
+
+*Available since:* Open Surge 0.6.0
 
 #### fps
 
 `fps`: number, read-only.
 
-Frames per second of the current animation.
+Frames per second of the animation.
 
 #### repeats
 
 `repeats`: boolean, read-only.
 
-Does the current animation repeat itself?
+Does the animation repeat itself?
 
 #### frameCount
 
 `frameCount`: number, read-only.
 
-The number of frames of the current animation.
+The number of frames of the animation.
 
 #### frame
 
@@ -89,4 +115,4 @@ While the [FPS](#fps) rate controls the speed of the animation, the speed factor
 
 `sync`: boolean.
 
-Is the current animation is synchronized? A synchronized animation is a repeating animation that displays the same frame across multiple sprites. Defaults to `false`.
\ No newline at end of file
+Is the animation is synchronized? A synchronized animation is a repeating animation that displays the same frame across multiple sprites. Defaults to `false`.
diff --git a/docs/engine/collider.md b/docs/engine/collider.md
index b309381..e96334b 100644
--- a/docs/engine/collider.md
+++ b/docs/engine/collider.md
@@ -74,6 +74,14 @@ Is the collider enabled? A collider that is not enabled will not notify the pare
 
 Is the collider visible? This is useful for debugging. The default value is `false`.
 
+#### anchor
+
+`anchor`: [Vector2](/engine/vector2) object.
+
+The anchor of the collider. See also: [setAnchor](#setanchor).
+
+*Available since:* Open Surge 0.6.0
+
 Functions
 ---------
 
@@ -109,7 +117,7 @@ Returns `true` if the point is contained in the collider, or `false` otherwise.
 
 `setAnchor(x, y)`
 
-Defines the anchor of the collider to be (`x`, `y`), where these values are (usually) numbers between 0.0 and 1.0. Imagine a bounding box of the collider. Point (0.5, 0.5) is the default, representing its center. Point (0.0, 0.0) is the top-left and (1,0, 1.0), the bottom-right. The anchor of the collider will be aligned to the hot spot of the sprite of the entity.
+Defines the anchor of the collider to be (`x`, `y`), where these values are (usually) numbers between 0.0 and 1.0. Imagine a bounding box of the collider. Point (0.5, 0.5) is the default, representing its center. Point (0.0, 0.0) is the top-left and (1,0, 1.0), the bottom-right. The anchor of the collider will be aligned to the hot spot of the sprite of the entity. See also: [anchor](#anchor).
 
 *Arguments*
 
diff --git a/docs/engine/level.md b/docs/engine/level.md
index ad1fca7..598ef87 100644
--- a/docs/engine/level.md
+++ b/docs/engine/level.md
@@ -142,14 +142,16 @@ The position where the player is placed when the level starts.
 
 `gravity`: number, read-only.
 
-A default value for the level gravity, in pixels per second squared.
+The acceleration of gravity, measured in pixels per second squared.
 
 #### time
 
-`time`: number, read-only.
+`time`: number.
 
 Elapsed time in the level, given in seconds.
 
+*Note:* this property is writable since Open Surge 0.6.0.
+
 #### next
 
 `next`: number.
diff --git a/docs/engine/platformer.md b/docs/engine/platformer.md
index fe354f0..b7fc047 100644
--- a/docs/engine/platformer.md
+++ b/docs/engine/platformer.md
@@ -58,6 +58,14 @@ Walking speed, in pixels per second.
 
 Jump speed, in pixels per second. The higher the value, the more intense the jump.
 
+#### gravityMultiplier
+
+`gravityMultiplier`: number.
+
+A multiplier used to modify how the Platformer is affected by gravity. When set to 1.0, the Platformer will subject to the [default acceleration of gravity](/engine/level#gravity). When set to 2.0 (0.5), the Platformer will be subject to twice (half) the default gravity, and so on. Zero means no gravity. Defaults to 1.0.
+
+*Available since:* Open Surge 0.6.0
+
 #### direction
 
 `direction`: number, read-only.
@@ -205,10 +213,14 @@ object "My Jumping Baddie" is "entity", "enemy"
 
 #### forceJump
 
-`forceJump()`
+`forceJump(speed)`
 
 Makes the platformer jump, regardless if it's touching the ground or not.
 
+*Arguments*
+
+* `speed`: number. Jump speed in pixels per second.
+
 *Returns*
 
 Returns the platformer itself.
@@ -227,3 +239,41 @@ The platformer spawns invisible [sensors](/engine/sensor) to detect collisions.
 *Returns*
 
 Returns the platformer itself.
+
+#### showSensors
+
+`showSensors()`
+
+Displays the internal sensors of the platformer, which are invisible by default. For debugging only.
+
+*Available since:* Open Surge 0.5.2
+
+*Returns*
+
+Returns the platformer itself.
+
+*Example*
+
+```
+using SurgeEngine.Actor;
+using SurgeEngine.Behaviors.Platformer;
+
+object "Testing Stuff" is "entity"
+{
+    actor = Actor("Testing Stuff");
+    platformer = Platformer().showSensors();
+    // ...
+}
+```
+
+#### hideSensors
+
+`hideSensors()`
+
+Hides the internal sensors of the platformer. This is the default.
+
+*Available since:* Open Surge 0.5.2
+
+*Returns*
+
+Returns the platformer itself.
diff --git a/docs/engine/player.md b/docs/engine/player.md
index 8ca8cd8..ae63005 100644
--- a/docs/engine/player.md
+++ b/docs/engine/player.md
@@ -177,17 +177,49 @@ object "Application"
 }
 ```
 
+#### animation
+
+`animation`: [Animation](/engine/animation) object, read-only.
+
+Reference to the Animation object of the Player.
+
 #### anim
 
 `anim`: number.
 
-A shortcut to `animation.id`: an integer corresponding to the animation number.
+A shortcut to `animation.id`: an integer corresponding to the animation number. See also: [Animation](/engine/animation), [id](/engine/animation#id).
 
-#### animation
+#### anchor
 
-`animation`: [Animation](/engine/animation) object, read-only.
+`anchor`: [Vector2](/engine/vector2) object.
 
-Reference to the Animation object of the Player.
+A shortcut to `animation.anchor`. See also: [anchor](/engine/animation#anchor).
+
+*Available since:* Open Surge 0.6.0
+
+#### hotSpot
+
+`hotSpot`: [Vector2](/engine/vector2) object.
+
+A shortcut to `animation.hotSpot`. See also: [hot spot](/engine/animation#hotspot).
+
+*Available since:* Open Surge 0.6.0. In versions prior to 0.6.0, you may get the hot spot using the [Animation](/engine/animation#hotspot) object.
+
+#### actionSpot
+
+`actionSpot`: [Vector2](/engine/vector2) object.
+
+A shortcut to `animation.actionSpot`. See also: [action spot](/engine/animation#actionspot).
+
+*Available since:* Open Surge 0.6.0
+
+#### actionOffset
+
+`actionOffset`: [Vector2](/engine/vector2) object, read-only.
+
+A shortcut to `animation.actionOffset`. See also: [action offset](/engine/animation#actionoffset).
+
+*Available since:* Open Surge 0.6.0
 
 #### attacking
 
@@ -449,6 +481,14 @@ Vertical speed, in pixels per second.
 
 The angle of the player, in degrees. The same as `transform.angle`.
 
+#### slope
+
+`slope`: number, read-only.
+
+The angle detected by the physics system, in degrees. Unlike [angle](#angle), slope is read-only and does not feature any smoothing for visual aesthetics. Its purpose is to be a helper for physics calculations.
+
+*Available since:* Open Surge 0.5.2
+
 #### width
 
 `width`: number, read-only.
diff --git a/docs/engine/prefs.md b/docs/engine/prefs.md
index 1f61517..47a7a09 100644
--- a/docs/engine/prefs.md
+++ b/docs/engine/prefs.md
@@ -15,6 +15,12 @@ object "Application"
     {
         Prefs["counter"] += 1;
         Console.print("counter: " + Prefs["counter"]);
+
+        state = "idle";
+    }
+
+    state "idle"
+    {
     }
 
     fun constructor()
diff --git a/docs/engine/text.md b/docs/engine/text.md
index 22ea15f..b720ac6 100644
--- a/docs/engine/text.md
+++ b/docs/engine/text.md
@@ -64,7 +64,7 @@ The name of the font in use.
 
 #### size
 
-`size`: [Vector2](/engine/vector2) object.
+`size`: [Vector2](/engine/vector2) object, read-only.
 
 The size, in pixels, of the rendered text.
 
diff --git a/docs/index.md b/docs/index.md
index e8aea21..98b4eb1 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,7 +1,7 @@
 Welcome to SurgeScript!
 =======================
 
-<img src="img/surge.png" alt="Surge" align="right" width="384">
+<img src="img/surge.png" alt="Surge" align="right" width="256">
 
 Unleash your creativity!
 ------------------------
@@ -13,7 +13,7 @@ SurgeScript is a scripting language for games. Use it to unleash your creativity
 How do I learn SurgeScript?
 ---------------------------
 
-Check out the [SurgeScript Crash Course](/tutorials/hello)! Also take a look on the [video tutorials](https://youtube.com/alemart88) and on the examples folder that comes with the software.
+Check out the [SurgeScript Crash Course](/tutorials/hello)! Additionally, take a look at the [video tutorials](https://youtube.com/alemart88) and at the examples that come with the software.
 
 SurgeScript in a nutshell
 -------------------------
@@ -33,13 +33,13 @@ Why use SurgeScript?
 
 Unlike other programming languages, SurgeScript has been designed with the specific needs of games in mind. Its features include:
 
-- The state-machine pattern: objects are state machines, making it easy to create in-game entities
+- The state-machine pattern: objects are state machines, making it easy to create game entities
 - The composition approach: you may design complex objects and behaviors by means of composition
 - The hierarchy system: objects have a parent and may have children, in a tree-like structure
 - The game loop: it's defined implicitly
 - Automatic garbage collection, object tagging and more!
 
-SurgeScript is meant to be used in games and in interactive applications. It's easy to integrate it into existing code, it's easy to extend, it features a C-like syntax, and it's free and open-source software.
+SurgeScript is meant to be used in games and in interactive applications. It's easy to integrate it into existing code, it's easy to extend it, it features a C-like syntax, and it's free and open-source software.
 
 SurgeScript has been designed based on the experience of its developer dealing with game engines, applications related to computer graphics and so on. Some of the best practices have been incorporated into the language itself, making things really easy for developers and modders.
 
diff --git a/docs/reference/object.md b/docs/reference/object.md
index 39e91f5..3f9fe40 100644
--- a/docs/reference/object.md
+++ b/docs/reference/object.md
@@ -12,12 +12,6 @@ Properties
 
 Reference to the parent object.
 
-#### childCount
-
-`childCount`: number, read-only.
-
-The number of children of the object.
-
 #### __name
 
 `__name`: string, read-only.
@@ -42,7 +36,7 @@ The functions of this object represented as a collection of strings.
 
 `__timespent`: number, read-only.
 
-The approximate time spent in this object in the last frame (in seconds).
+The approximate time spent in the current state (in seconds).
 
 #### __file
 
@@ -52,6 +46,29 @@ The source file of this object.
 
 *Available since:* SurgeScript 0.5.3
 
+#### __children
+
+`__children`: [Array](/reference/array) object, read-only.
+
+The children of this object.
+
+*Available since:* SurgeScript 0.5.4
+
+*Returns*
+
+A new array featuring all the children of this object. If there are no children, an empty array is returned.
+
+#### __childCount
+
+`__childCount`: number, read-only.
+
+The number of children of the object.
+
+*Available since:* SurgeScript 0.5.5
+
+*Note:* prior to SurgeScript 0.5.5, you would use `object.childCount` instead. That form is now obsolete.
+
+
 Functions
 ---------
 
diff --git a/docs/tutorials/advanced_features.md b/docs/tutorials/advanced_features.md
index 50ff3c3..9f995db 100644
--- a/docs/tutorials/advanced_features.md
+++ b/docs/tutorials/advanced_features.md
@@ -8,7 +8,7 @@ Lookup operator
 
 Some programming languages, such as C++, have a feature called *operator overloading*. It's a *syntactic sugar* that allows the programmer to attribute custom implementations to different operators.
 
-In SurgeScript, the `[]` operator (also called the *lookup operator*), used by [Arrays](/reference/array) and [Dictionaries](/reference/dictionary), is used to **get** and **set** values from/to the data structure. In fact, the `[]` operator can be used with any object. It is necessary to define, in your object, functions `get()` and `set()` with the following signature:
+In SurgeScript, the `[]` operator (also called the *lookup operator*), used by [Arrays](/reference/array) and [Dictionaries](/reference/dictionary), is used to **get** and **set** values from/to the data structure. In fact, the `[]` operator can be used with any object. It is necessary to define, in your object, functions named `get()` and `set()` with the following signature:
 
 ```
 fun get(key)
diff --git a/docs/tutorials/best_practices.md b/docs/tutorials/best_practices.md
index b71d03e..3bdbe2b 100644
--- a/docs/tutorials/best_practices.md
+++ b/docs/tutorials/best_practices.md
@@ -5,7 +5,7 @@ Best Practices
 - Follow the golden rule: objects should **not** mess with each others' internals!
     - Objects should **not** change others' internal variables or states directly (not allowed!)
     - Objects should define [functions](/tutorials/functions) that can be operated upon by the external world (API)
-- Use a consistent style, e.g.,
+- Use a consistent coding style. Suggestion:
     - Use *camelCase* names for both variables and functions.
     - Use *4 spaces* when indenting your code.
 - Combine related [packages](/tutorials/packages) into a single one: don't pollute the global namespace.
diff --git a/docs/tutorials/expressions.md b/docs/tutorials/expressions.md
index ea56dca..8a1c303 100644
--- a/docs/tutorials/expressions.md
+++ b/docs/tutorials/expressions.md
@@ -109,9 +109,9 @@ The table below summarizes the basic logical expressions:
 
 Expression|Result
 ----------|------
-`a && b` |`true` only if both a and b are true
-`a || b` | `true` if a is true or b is true
-`!a` | `true` if a is false, `false` if a is true
+`a && b` |`true` only if both `a` and `b` are true
+`a || b` | `true` if `a` is true or `b` is true
+`!a` | `true` if `a` is false, `false` if `a` is true
 
 Notice that **not** has higher precedence than the other two operators. Examples:
 
diff --git a/docs/tutorials/object_tree.md b/docs/tutorials/object_tree.md
index 8b85ff4..a6f3f8e 100644
--- a/docs/tutorials/object_tree.md
+++ b/docs/tutorials/object_tree.md
@@ -83,9 +83,9 @@ Relevant data about the object hierarchy can be obtained using the following pro
 Function / property|Description
 -------------------|-----------
 `obj.parent` (read-only) | The parent object
-`obj.childCount` (read-only) | Number of immediate children
 `obj.child(name)` | Gets a child object named `name`
 `obj.findObject(name)` | Finds a descendant named `name`
+`obj.__childCount` (read-only) | Number of immediate children
 
 Example:
 
@@ -97,7 +97,7 @@ object "Parent"
 
     state "main"
     {
-        Console.print("This object has " + this.childCount + " children.");
+        Console.print("This object has " + this.__childCount + " children.");
         destroy();
     }
 }
diff --git a/examples/package.ss b/examples/package.ss
index eef5b18..0409c0a 100644
--- a/examples/package.ss
+++ b/examples/package.ss
@@ -1,7 +1,7 @@
 //
 // package.ss
 // Package example
-// Copyright 2018, 2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+// Copyright 2018-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
 //
 
 // import the package
diff --git a/examples/unit_testing.ss b/examples/unit_testing.ss
index b5abb32..8c40b6a 100644
--- a/examples/unit_testing.ss
+++ b/examples/unit_testing.ss
@@ -1,7 +1,7 @@
 //
 // unit_testing.ss
 // A Unit Testing Script for SurgeScript
-// Copyright (C) 2017-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+// Copyright 2017-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
 //
 
 object "Application"
diff --git a/mkdocs.yml b/mkdocs.yml
index d5445b1..811082a 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -2,10 +2,11 @@ site_name: 'SurgeScript Documentation'
 site_url: 'https://docs.opensurge2d.org/'
 site_author: 'Alexandre Martins'
 site_description: 'SurgeScript is a scripting language for games.'
-copyright: 'SurgeScript is a scripting language for games. Copyright © 2016-2020 Alexandre Martins.'
+site_dir: 'docs_html/'
+copyright: 'SurgeScript is a scripting language for games. Copyright © 2016-2022 Alexandre Martins.'
 google_analytics: ['UA-120511928-1', 'opensurge2d.org']
 repo_url: 'https://github.com/alemart/surgescript'
-strict: true
+strict: false
 theme:
     name: 'material'
 nav:
diff --git a/src/main.c b/src/main.c
index 0883063..fb3c8d2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,18 +16,34 @@
  * limitations under the License.
  *
  * runtime/main.c
- * SurgeScript Runtime Engine entry point
+ * SurgeScript CLI
  */
 
 #include <surgescript.h>
 #include <string.h>
 #include <stdio.h>
 
-static surgescript_vm_t* make_vm(int argc, char** argv);
+/* multithread support */
+#if ENABLE_THREADS
+# if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
+#  include <threads.h>
+# else
+#  error "Can't compile the SurgeScript CLI: threads.h is not found on this environment. Please change the environment or disable multithreading."
+# endif
+#endif
+
+static surgescript_vm_t* make_vm(int argc, char** argv, uint64_t* time_limit);
+static void run_vm(surgescript_vm_t* vm, uint64_t time_limit);
+static void destroy_vm(surgescript_vm_t* vm);
 static void print_to_stdout(const char* message);
 static void print_to_stderr(const char* message);
 static void discard_message(const char* message);
 static void show_help(const char* executable);
+static char* read_from_stdin();
+static int main_loop(void* arg);
+
+/* default time limit, given in milliseconds */
+#define DEFAULT_TIME_LIMIT 30000
 
 /*
  * main()
@@ -35,26 +51,103 @@ static void show_help(const char* executable);
  */
 int main(int argc, char* argv[])
 {
-    if(argc > 1) {
-        /* create the VM and compile the input file(s) */
-        surgescript_vm_t* vm = make_vm(argc, argv);
-        if(vm != NULL) {
-            /* run the VM */
-            while(surgescript_vm_update(vm)) {
-                ;
+    uint64_t time_limit = DEFAULT_TIME_LIMIT;
+
+    /* Create the VM and compile the input file(s) */
+    surgescript_vm_t* vm = make_vm(argc, argv, &time_limit);
+
+    /* got a VM? */
+    if(vm != NULL) {
+        /* run the VM */
+        run_vm(vm, time_limit);
+
+        /* destroy the VM */
+        destroy_vm(vm);
+    }
+
+    /* done! */
+    return 0;
+}
+
+/**
+ * run_vm()
+ * Run the VM with a time limit
+ */
+void run_vm(surgescript_vm_t* vm, uint64_t time_limit)
+{
+    uint64_t start_time = surgescript_util_gettickcount();
+    #define show_time_limit_error() \
+        fprintf(stderr, "Time limit of %.1lf seconds exceeded.\n", (double)time_limit * 0.001)
+
+#if !ENABLE_THREADS
+
+    /* main loop */
+    while(surgescript_vm_update(vm)) {
+
+        /* time limit */
+        if(time_limit > 0 && surgescript_util_gettickcount() > start_time + time_limit) {
+            show_time_limit_error();
+            break;
+        }
+
+    }
+
+#else
+
+    /* run the SurgeScript VM on a separate thread */
+    thrd_t thread;
+    thrd_create(&thread, main_loop, vm);
+
+    /* handle the time limit, if it's been set */
+    if(time_limit > 0) {
+        while(surgescript_vm_is_active(vm)) {
+            if(surgescript_util_gettickcount() > start_time + time_limit) {
+                show_time_limit_error();
+                exit(1); /* TODO we should kill the other thread instead */
             }
 
-            /* destroy the VM */
-            surgescript_vm_destroy(vm);
+            thrd_yield();
         }
     }
-    else {
-        /* print usage */
-        show_help(surgescript_util_basename(argv[0]));
+
+    /* wait for the other thread to complete */
+    thrd_join(thread, NULL);
+
+#endif
+
+}
+
+/**
+ * destroy_vm()
+ * Destroy a SurgeScript VM
+ */
+void destroy_vm(surgescript_vm_t* vm)
+{
+    surgescript_vm_destroy(vm);
+}
+
+/**
+ * main_loop()
+ * Game loop for multithreaded execution
+ */
+int main_loop(void* arg)
+{
+#if !ENABLE_THREADS
+
+    (void)arg;
+    return 0;
+
+#else
+
+    surgescript_vm_t* vm = (surgescript_vm_t*)arg;
+
+    while(surgescript_vm_update(vm)) {
+        thrd_yield();
     }
 
-    /* done! */
     return 0;
+
+#endif
 }
 
 /*
@@ -62,7 +155,7 @@ int main(int argc, char* argv[])
  * Parses the command line arguments and creates a VM
  * with the compiled scripts
  */
-surgescript_vm_t* make_vm(int argc, char** argv)
+surgescript_vm_t* make_vm(int argc, char** argv, uint64_t* time_limit)
 {
     surgescript_vm_t* vm = NULL;
     int i;
@@ -87,8 +180,20 @@ surgescript_vm_t* make_vm(int argc, char** argv)
             show_help(surgescript_util_basename(argv[0]));
             return NULL;
         }
+        else if(strcmp(arg, "--timelimit") == 0 || strcmp(arg, "-t") == 0) {
+            /* set time limit (maximum execution time) */
+            if(++i < argc && time_limit != NULL) {
+                double seconds = atof(argv[i]);
+                *time_limit = (seconds > 0.0) ? (uint64_t)(seconds * 1000.0) : 0;
+            }
+        }
+        else if(strcmp(arg, "--") == 0) {
+            /* user-specific command line arguments */
+            break;
+        }
         else {
-            printf("Unrecognized option: '%s'.\nType '%s --help' for more information.\n", arg, surgescript_util_basename(argv[0]));
+            /* unrecognized option */
+            fprintf(stderr, "Unrecognized option: '%s'.\nType '%s --help' for more information.\n", arg, surgescript_util_basename(argv[0]));
             return NULL;
         }
     }
@@ -97,13 +202,32 @@ surgescript_vm_t* make_vm(int argc, char** argv)
     vm = surgescript_vm_create();
 
     /* compile the scripts */
-    for(; i < argc && strcmp(argv[i], "--") != 0; i++) {
-        const char* file = argv[i];
-        surgescript_vm_compile(vm, file);
+    if(i < argc && strcmp(argv[i], "--") != 0) {
+        /* read files */
+        for(; i < argc && strcmp(argv[i], "--") != 0; i++) {
+            const char* file = argv[i];
+            surgescript_vm_compile(vm, file);
+        }
+    }
+    else {
+        fprintf(stderr, "Reading from stdin... Run '%s -h' for help.\n", surgescript_util_basename(argv[0]));
+
+        /* read from stdin */
+        char* code = read_from_stdin();
+        surgescript_vm_compile_code_in_memory(vm, code);
+        ssfree(code);
     }
 
     /* launch the VM */
-    surgescript_vm_launch_ex(vm, argc, argv);
+    if(i < argc && strcmp(argv[i], "--") == 0) {
+        /* launch with user-specific command line arguments */
+        ++i;
+        surgescript_vm_launch_ex(vm, argc - i, (char**)(argv + i));
+    }
+    else {
+        /* launch without user-specific command line arguments */
+        surgescript_vm_launch(vm);
+    }
 
     /* done! */
     return vm;
@@ -126,6 +250,7 @@ void show_help(const char* executable)
         "Options:\n"
         "    -v, --version                         shows the version of SurgeScript\n"
         "    -D, --debug                           prints debugging information\n"
+        "    -t, --timelimit                       sets a maximum execution time, in seconds (0 = no limit)\n"
         "    -h, --help                            shows this message\n"
         "\n"
         "Examples:\n"
@@ -133,8 +258,9 @@ void show_help(const char* executable)
         "    %s file1.ss file2.ss         compiles and executes file1.ss and file2.ss\n"
         "    %s --debug test.ss           compiles and runs test.ss with debugging information\n"
         "    %s file.ss -- -x -y          passes custom arguments -x and -y to file.ss\n"
+        "    %s -t 5                      runs a script read from stdin, with a time limit of 5 seconds\n"
         "\n"
-        "Full documentation at: <%s>\n",
+        "Full documentation available at: <%s>\n",
         surgescript_util_version(),
         surgescript_util_year(),
         surgescript_util_authors(),
@@ -144,6 +270,7 @@ void show_help(const char* executable)
         executable,
         executable,
         executable,
+        executable,
         surgescript_util_website()
     );
 }
@@ -173,4 +300,26 @@ void print_to_stderr(const char* message)
 void discard_message(const char* message)
 {
     ;
-}
\ No newline at end of file
+}
+
+/**
+ * read_from_stdin()
+ * Read data from stdin and store it in a string
+ */
+char* read_from_stdin()
+{
+    const size_t BUFSIZE = 1024;
+    char* data = NULL;
+    size_t read_chars = 0, data_size = 0;
+
+    /* read to data[] */
+    do {
+        data_size += BUFSIZE;
+        data = ssrealloc(data, data_size + 1);
+        read_chars += fread(data + read_chars, sizeof(char), BUFSIZE, stdin);
+        data[read_chars] = '\0';
+    } while(read_chars == data_size);
+
+    /* done! */
+    return data;
+}
diff --git a/src/surgescript.h b/src/surgescript.h
index 98c4d4f..fccad2a 100644
--- a/src/surgescript.h
+++ b/src/surgescript.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,8 +30,9 @@ extern "C" {
 #include "surgescript/runtime/program.h"
 #include "surgescript/runtime/object.h"
 #include "surgescript/runtime/program_pool.h"
-#include "surgescript/runtime/tag_system.h"
 #include "surgescript/runtime/object_manager.h"
+#include "surgescript/runtime/tag_system.h"
+#include "surgescript/runtime/vm_time.h"
 #include "surgescript/runtime/heap.h"
 #include "surgescript/runtime/stack.h"
 #include "surgescript/runtime/variable.h"
@@ -39,9 +40,10 @@ extern "C" {
 #include "surgescript/util/transform.h"
 #include "surgescript/util/ssarray.h"
 #include "surgescript/util/util.h"
+#include "surgescript/util/version.h"
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/surgescript/compiler/asm.c b/src/surgescript/compiler/asm.c
index 11761a7..490ce5b 100644
--- a/src/surgescript/compiler/asm.c
+++ b/src/surgescript/compiler/asm.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/asm.h b/src/surgescript/compiler/asm.h
index 09eace9..78add52 100644
--- a/src/surgescript/compiler/asm.h
+++ b/src/surgescript/compiler/asm.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/lexer.c b/src/surgescript/compiler/lexer.c
index 78561a1..23df17e 100644
--- a/src/surgescript/compiler/lexer.c
+++ b/src/surgescript/compiler/lexer.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/lexer.h b/src/surgescript/compiler/lexer.h
index 238fa91..af32442 100644
--- a/src/surgescript/compiler/lexer.h
+++ b/src/surgescript/compiler/lexer.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/nodecontext.h b/src/surgescript/compiler/nodecontext.h
index 4e6d6c1..fb123d1 100644
--- a/src/surgescript/compiler/nodecontext.h
+++ b/src/surgescript/compiler/nodecontext.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/parser.c b/src/surgescript/compiler/parser.c
index 2a1b3fc..dfb8331 100644
--- a/src/surgescript/compiler/parser.c
+++ b/src/surgescript/compiler/parser.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -191,7 +191,7 @@ bool surgescript_parser_parsefile(surgescript_parser_t* parser, const char* abso
 {
     FILE* fp = surgescript_util_fopen_utf8(absolute_path, "rb"); /* use binary mode, so offsets don't get messed up */
     if(fp) {
-        static size_t BUFSIZE = 1024;
+        const size_t BUFSIZE = 1024;
         char* data = NULL;
         size_t read_chars = 0, data_size = 0;
 
@@ -1105,7 +1105,7 @@ void unaryexpr(surgescript_parser_t* parser, surgescript_nodecontext_t context)
         if(!is_state_context(context))
             ssfatal("Compile Error: timeout can only be used inside a state (see %s:%d).", context.source_file, surgescript_token_linenumber(parser->previous));
         match(parser, SSTOK_LPAREN);
-        expr(parser, context);
+        assignexpr(parser, context);
         emit_timeout(context);
         match(parser, SSTOK_RPAREN);
     }
diff --git a/src/surgescript/compiler/parser.h b/src/surgescript/compiler/parser.h
index c6325de..5026dfa 100644
--- a/src/surgescript/compiler/parser.h
+++ b/src/surgescript/compiler/parser.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@
  *             |  ! <unaryexpr>
  *             |  typeof <unaryexpr> | typeof ( <expr> )
  *             |  ++ identifier | -- identifier
- *             |  timeout ( <expr> )
+ *             |  timeout ( <assignexpr> )
  *             |  <postfixexpr>
  * <postfixexpr> := identifier ++ | identifier --
  *               |  <funcallexpr> <postfixexpr1>
@@ -155,4 +155,4 @@ void surgescript_parser_foreach_plugin(surgescript_parser_t* parser, void* data,
 void surgescript_parser_set_flags(surgescript_parser_t* parser, surgescript_parser_flags_t flags); /* set parser options (flags) */
 surgescript_parser_flags_t surgescript_parser_get_flags(surgescript_parser_t* parser); /* get parser flags */
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/surgescript/compiler/symtable.c b/src/surgescript/compiler/symtable.c
index 36bb8e9..66f87df 100644
--- a/src/surgescript/compiler/symtable.c
+++ b/src/surgescript/compiler/symtable.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/symtable.h b/src/surgescript/compiler/symtable.h
index 2b3300f..12a4c3d 100644
--- a/src/surgescript/compiler/symtable.h
+++ b/src/surgescript/compiler/symtable.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/token.c b/src/surgescript/compiler/token.c
index ed1fcae..f057b09 100644
--- a/src/surgescript/compiler/token.c
+++ b/src/surgescript/compiler/token.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/compiler/token.h b/src/surgescript/compiler/token.h
index b0cf4f2..1ce4011 100644
--- a/src/surgescript/compiler/token.h
+++ b/src/surgescript/compiler/token.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/misc/icon.png b/src/surgescript/misc/icon.png
index d00463e..08c2e61 100644
Binary files a/src/surgescript/misc/icon.png and b/src/surgescript/misc/icon.png differ
diff --git a/src/surgescript/misc/info.c.in b/src/surgescript/misc/info.c.in
index 429130b..9737703 100644
--- a/src/surgescript/misc/info.c.in
+++ b/src/surgescript/misc/info.c.in
@@ -1,13 +1,12 @@
-/* Project info */
-const char SURGESCRIPT_AUTHORS[] = "Alexandre Martins";
-const char SURGESCRIPT_YEARS[] = "2016-2020";
-const char SURGESCRIPT_WEBSITE[] = "https://github.com/alemart/surgescript";
-
-/* compiled version of SurgeScript */
-#cmakedefine PROJECT_VERSION "@PROJECT_VERSION@"
+/* SurgeScript info */
+#include "../util/version.h"
 
-#if defined(PROJECT_VERSION)
-const char SURGESCRIPT_VERSION[] = PROJECT_VERSION;
-#else
-#error "Undefined PROJECT_VERSION (major.minor.patch.tweak)"
+#cmakedefine PROJECT_YEARS "@PROJECT_YEARS@"
+#if !defined(PROJECT_YEARS)
+#error Undefined PROJECT_YEARS
 #endif
+
+const char SURGESCRIPT_AUTHORS[] = "Alexandre Martins";
+const char SURGESCRIPT_WEBSITE[] = "https://github.com/alemart/surgescript";
+const char SURGESCRIPT_VERSION[] = SURGESCRIPT_VERSION_STR;
+const char SURGESCRIPT_YEARS[]   = PROJECT_YEARS;
\ No newline at end of file
diff --git a/src/surgescript/misc/pack-windows.sh b/src/surgescript/misc/pack-windows.sh
new file mode 100755
index 0000000..36f5444
--- /dev/null
+++ b/src/surgescript/misc/pack-windows.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+# This utility packs the Windows build of SurgeScript
+
+UNIX2DOS="todos" #"unix2dos"
+SOURCE_FOLDER="../../.."
+BUILD_FOLDER="$SOURCE_FOLDER/build"
+OUTPUT_FOLDER="/tmp"
+
+# File surgescript.exe must be present before packaging
+if [ ! -f "$BUILD_FOLDER/surgescript.exe" ]; then
+    echo "File surgescript.exe not found"
+    exit 1
+fi
+
+# Extract the SurgeScript version
+VERSION=$(wine "$BUILD_FOLDER/surgescript.exe" -v | tr -cd [:digit:][:punct:])
+PACKAGE="surgescript-$VERSION-win"
+echo "Found SurgeScript version $VERSION."
+
+# Create a temporary folder for packaging
+TMP_FOLDER="/tmp/$PACKAGE"
+rm -rf "$TMP_FOLDER" 2>/dev/null
+mkdir -p "$TMP_FOLDER"
+
+# Write a README for Windows
+cat > "$TMP_FOLDER/README-Windows.txt" << EOF
+--------------------------------------------------
+SurgeScript
+A scripting language for games
+Copyright (C) 2016-$(date +%Y)  Alexandre Martins
+--------------------------------------------------
+This is the Windows build of SurgeScript $VERSION.
+
+To test SurgeScript, run the surgescript executable via the Command Prompt.
+Pass the scripts you want to test via the command line, as in the examples:
+
+  ** See the available options: **
+  C:\path\to\surgescript> surgescript
+
+  ** Run a test script: **
+  C:\path\to\surgescript> surgescript examples\hello.ss
+
+  ** Run another test script: **
+  C:\path\to\surgescript> surgescript examples\count_to_10.ss
+
+There are many example scripts to try out. Check the examples folder for
+more information.
+
+Visit the SurgeScript website at: https://github.com/alemart/surgescript/
+EOF
+
+# Copy files
+for file in surgescript.exe libsurgescript-static.a libsurgescript.dll.a libsurgescript.dll surgescript.pc surgescript-static.pc; do
+    echo "Copying $file..."
+    cp "$BUILD_FOLDER/$file" "$TMP_FOLDER"
+done
+
+for file in LICENSE README.md CHANGES.md CMakeLists.txt mkdocs.yml; do
+    echo "Copying $file..."
+    cp "$SOURCE_FOLDER/$file" "$TMP_FOLDER"
+done
+
+for folder in examples src cmake; do
+    echo "Copying $folder/ ..."
+    cp -r "$SOURCE_FOLDER/$folder" "$TMP_FOLDER"
+done
+
+mv "$TMP_FOLDER/LICENSE" "$TMP_FOLDER/LICENSE.txt"
+
+# Converting newlines of all text files
+for f in `find "$TMP_FOLDER" -type f -exec grep -Iq . {} \; -print`; do
+    ${UNIX2DOS} $f
+done
+
+# Generate the docs
+pushd "$SOURCE_FOLDER"
+mkdocs build
+popd
+
+# Copying the docs
+for folder in docs docs_html; do
+    echo "Copying $folder/ ..."
+    cp -r "$SOURCE_FOLDER/$folder" "$TMP_FOLDER"
+done
+
+# Create the .zip package
+pushd "$TMP_FOLDER"
+echo "Packaging..."
+zip -r "$OUTPUT_FOLDER/$PACKAGE.zip" ./*
+echo "Packaged to $OUTPUT_FOLDER/$PACKAGE.zip"
+popd
+#rm -rf "${TMP_FOLDER}"
\ No newline at end of file
diff --git a/src/surgescript/misc/surgescript.appdata.xml.in b/src/surgescript/misc/surgescript.appdata.xml.in
index 85ac66e..7c45cc3 100644
--- a/src/surgescript/misc/surgescript.appdata.xml.in
+++ b/src/surgescript/misc/surgescript.appdata.xml.in
@@ -1,11 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright 2020 Alexandre Martins -->
 <component type="console-application">
   <id>org.opensurge2d.SurgeScript</id>
   <name>SurgeScript</name>
   <developer_name>Alexandre Martins</developer_name>
   <update_contact>alemartf_at_gmail.com</update_contact>
-  <metadata_license>MIT</metadata_license>
+  <metadata_license>Apache-2.0</metadata_license>
   <project_license>Apache-2.0</project_license>
   <summary>A scripting language for games</summary>
   <description>
diff --git a/src/surgescript/misc/surgescript.ico b/src/surgescript/misc/surgescript.ico
index a178441..6425285 100644
Binary files a/src/surgescript/misc/surgescript.ico and b/src/surgescript/misc/surgescript.ico differ
diff --git a/src/surgescript/misc/surgescript.png b/src/surgescript/misc/surgescript.png
index d00463e..08c2e61 100644
Binary files a/src/surgescript/misc/surgescript.png and b/src/surgescript/misc/surgescript.png differ
diff --git a/src/surgescript/runtime/heap.c b/src/surgescript/runtime/heap.c
index b08bfd0..6b9079e 100644
--- a/src/surgescript/runtime/heap.c
+++ b/src/surgescript/runtime/heap.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/heap.h b/src/surgescript/runtime/heap.h
index 1e7fadf..7328611 100644
--- a/src/surgescript/runtime/heap.h
+++ b/src/surgescript/runtime/heap.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/object.c b/src/surgescript/runtime/object.c
index 3c77558..a6b9349 100644
--- a/src/surgescript/runtime/object.c
+++ b/src/surgescript/runtime/object.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include "heap.h"
 #include "stack.h"
 #include "renv.h"
+#include "vm_time.h"
 #include "../util/transform.h"
 #include "../util/ssarray.h"
 #include "../util/util.h"
@@ -52,6 +53,9 @@ struct surgescript_object_t
     bool is_active; /* can i run programs? */
     bool is_killed; /* am i scheduled to be destroyed? */
     bool is_reachable; /* is this object reachable through some other? (garbage-collection) */
+
+    /* internal timer */
+    const surgescript_vmtime_t* vmtime; /* VM time */
     uint64_t last_state_change; /* moment of the last state change */
     uint64_t time_spent; /* how much time did this object consume since the last state change */
 
@@ -89,7 +93,7 @@ static bool simple_traversal(surgescript_object_t* object, void* data);
  * surgescript_object_create()
  * Creates a new blank object
  */
-surgescript_object_t* surgescript_object_create(const char* name, unsigned handle, surgescript_objectmanager_t* object_manager, surgescript_programpool_t* program_pool, surgescript_stack_t* stack, void* user_data)
+surgescript_object_t* surgescript_object_create(const char* name, unsigned handle, surgescript_objectmanager_t* object_manager, surgescript_programpool_t* program_pool, surgescript_stack_t* stack, const surgescript_vmtime_t* vmtime, void* user_data)
 {
     surgescript_object_t* obj = ssmalloc(sizeof *obj);
 
@@ -107,12 +111,14 @@ surgescript_object_t* surgescript_object_create(const char* name, unsigned handl
 
     obj->state_name = ssstrdup(MAIN_STATE);
     obj->current_state = get_state_program(obj, obj->state_name);
-    obj->last_state_change = surgescript_util_gettickcount();
-    obj->time_spent = 0;
     obj->is_active = true;
     obj->is_killed = false;
     obj->is_reachable = false;
 
+    obj->vmtime = vmtime;
+    obj->last_state_change = surgescript_vmtime_time(obj->vmtime);
+    obj->time_spent = 0;
+
     obj->transform = NULL;
     obj->user_data = user_data;
 
@@ -574,7 +580,7 @@ void surgescript_object_set_state(surgescript_object_t* object, const char* stat
         ssfree(object->state_name);
         object->state_name = ssstrdup(state_name ? state_name : MAIN_STATE);
         object->current_state = get_state_program(object, object->state_name);
-        object->last_state_change = surgescript_util_gettickcount();
+        object->last_state_change = surgescript_vmtime_time(object->vmtime);
         object->time_spent = 0;
     }
 }
@@ -585,7 +591,7 @@ void surgescript_object_set_state(surgescript_object_t* object, const char* stat
  */
 double surgescript_object_elapsed_time(const surgescript_object_t* object)
 {
-    return (surgescript_util_gettickcount() - object->last_state_change) * 0.001;
+    return (surgescript_vmtime_time(object->vmtime) - object->last_state_change) * 0.001;
 }
 
 /*
@@ -836,7 +842,7 @@ double surgescript_object_timespent(const surgescript_object_t* object)
 {
     uint64_t now = surgescript_util_gettickcount();
     uint64_t dt = now > object->last_state_change ? now - object->last_state_change : 1;
-    return (double)(object->time_spent * 0.001) / dt;
+    return ((double)(object->time_spent) * 0.001) / ((double)dt);
 }
 
 /*
diff --git a/src/surgescript/runtime/object.h b/src/surgescript/runtime/object.h
index 924bb88..0b679a0 100644
--- a/src/surgescript/runtime/object.h
+++ b/src/surgescript/runtime/object.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/object_manager.c b/src/surgescript/runtime/object_manager.c
index 752aaaa..d6dcacc 100644
--- a/src/surgescript/runtime/object_manager.c
+++ b/src/surgescript/runtime/object_manager.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include "object.h"
 #include "program_pool.h"
 #include "tag_system.h"
+#include "vm_time.h"
 #include "stack.h"
 #include "heap.h"
 #include "variable.h"
@@ -43,6 +44,7 @@ struct surgescript_objectmanager_t
     surgescript_stack_t* stack; /* reference to the stack */
     surgescript_tagsystem_t* tag_system; /* tag system */
     surgescript_vmargs_t* args; /* VM command-line arguments (NULL-terminated array) */
+    const surgescript_vmtime_t* vmtime; /* VM time */
     SSARRAY(surgescript_objecthandle_t, objects_to_be_scanned); /* garbage collection */
     int first_object_to_be_scanned; /* an index of objects_to_be_scanned */
     int reachables_count; /* garbage-collector stuff */
@@ -80,7 +82,7 @@ static const char* SYSTEM_OBJECTS[] = {
 }; /* this must be a NULL-terminated array */
 
 /* object methods acessible by me */
-extern surgescript_object_t* surgescript_object_create(const char* name, unsigned handle, struct surgescript_objectmanager_t* object_manager, struct surgescript_programpool_t* program_pool, struct surgescript_stack_t* stack, void* user_data); /* creates a new blank object */
+extern surgescript_object_t* surgescript_object_create(const char* name, unsigned handle, surgescript_objectmanager_t* object_manager, surgescript_programpool_t* program_pool, surgescript_stack_t* stack, const surgescript_vmtime_t* vmtime, void* user_data); /* creates a new blank object */
 extern surgescript_object_t* surgescript_object_destroy(surgescript_object_t* object); /* destroys an object */
 
 /* the life-cycle of the objects is handled by me */
@@ -111,7 +113,7 @@ static inline surgescript_object_t* plugin_object(const surgescript_objectmanage
  * surgescript_objectmanager_create()
  * Creates a new object manager
  */
-surgescript_objectmanager_t* surgescript_objectmanager_create(surgescript_programpool_t* program_pool, surgescript_tagsystem_t* tag_system, surgescript_stack_t* stack, surgescript_vmargs_t* args)
+surgescript_objectmanager_t* surgescript_objectmanager_create(surgescript_programpool_t* program_pool, surgescript_tagsystem_t* tag_system, surgescript_stack_t* stack, surgescript_vmargs_t* args, const surgescript_vmtime_t* vmtime)
 {
     surgescript_objectmanager_t* manager = ssmalloc(sizeof *manager);
 
@@ -123,6 +125,7 @@ surgescript_objectmanager_t* surgescript_objectmanager_create(surgescript_progra
     manager->tag_system = tag_system;
     manager->stack = stack;
     manager->args = args;
+    manager->vmtime = vmtime;
     manager->handle_ptr = ROOT_HANDLE;
 
     ssarray_init(manager->objects_to_be_scanned);
@@ -162,7 +165,7 @@ surgescript_objecthandle_t surgescript_objectmanager_spawn(surgescript_objectman
 {
     surgescript_objecthandle_t handle = new_handle(manager);
     surgescript_object_t *parent_object = surgescript_objectmanager_get(manager, parent);
-    surgescript_object_t *object = surgescript_object_create(object_name, handle, manager, manager->program_pool, manager->stack, user_data);
+    surgescript_object_t *object = surgescript_object_create(object_name, handle, manager, manager->program_pool, manager->stack, manager->vmtime, user_data);
 
     /* store the object */
     if(handle >= ssarray_length(manager->data) && handle > ROOT_HANDLE) {
@@ -204,7 +207,7 @@ surgescript_objecthandle_t surgescript_objectmanager_spawn_root(surgescript_obje
         char** data[] = { (char**)SYSTEM_OBJECTS, plugins };
 
         /* spawn the root object */
-        surgescript_object_t *object = surgescript_object_create(ROOT_OBJECT, ROOT_HANDLE, manager, manager->program_pool, manager->stack, data);
+        surgescript_object_t *object = surgescript_object_create(ROOT_OBJECT, ROOT_HANDLE, manager, manager->program_pool, manager->stack, manager->vmtime, data);
         ssarray_push(manager->data, object);
         manager->count++;
 
diff --git a/src/surgescript/runtime/object_manager.h b/src/surgescript/runtime/object_manager.h
index e1775b0..20170cf 100644
--- a/src/surgescript/runtime/object_manager.h
+++ b/src/surgescript/runtime/object_manager.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,12 +34,13 @@ struct surgescript_programpool_t;
 struct surgescript_stack_t;
 struct surgescript_tagsystem_t;
 struct surgescript_vmargs_t;
+struct surgescript_vmtime_t;
 
 
 /* public methods */
 
 /* life-cycle */
-surgescript_objectmanager_t* surgescript_objectmanager_create(struct surgescript_programpool_t* program_pool, struct surgescript_tagsystem_t* tag_system, struct surgescript_stack_t* stack, struct surgescript_vmargs_t* args);
+surgescript_objectmanager_t* surgescript_objectmanager_create(struct surgescript_programpool_t* program_pool, struct surgescript_tagsystem_t* tag_system, struct surgescript_stack_t* stack, struct surgescript_vmargs_t* args, const struct surgescript_vmtime_t* vmtime);
 surgescript_objectmanager_t* surgescript_objectmanager_destroy(surgescript_objectmanager_t* manager);
 
 /* operations */
diff --git a/src/surgescript/runtime/program.c b/src/surgescript/runtime/program.c
index a45a6d6..b7e5fba 100644
--- a/src/surgescript/runtime/program.c
+++ b/src/surgescript/runtime/program.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -518,12 +518,8 @@ void run_instruction(surgescript_program_t* program, surgescript_renv_t* runtime
             break;
 
         case SSOP_DIV:
-            if(fast_notzero(surgescript_var_get_number(t(b))))
-                surgescript_var_set_number(t(a), surgescript_var_get_number(t(a)) / surgescript_var_get_number(t(b)));
-            else if(fast_sign(surgescript_var_get_number(t(a))) >= 0)
-                surgescript_var_set_number(t(a), INFINITY * fast_sign1(surgescript_var_get_number(t(b))));
-            else
-                surgescript_var_set_number(t(a), -INFINITY * fast_sign1(surgescript_var_get_number(t(b))));
+            /* division by zero should follow the IEEE-754 */
+            surgescript_var_set_number(t(a), surgescript_var_get_number(t(a)) / surgescript_var_get_number(t(b)));
             break;
 
         case SSOP_MOD:
@@ -590,48 +586,42 @@ void run_instruction(surgescript_program_t* program, surgescript_renv_t* runtime
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         case SSOP_JNE:
             if(surgescript_var_get_rawbits(_t[2])) {
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         case SSOP_JL:
             if(surgescript_var_get_rawbits(_t[2]) < 0) {
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         case SSOP_JG:
             if(surgescript_var_get_rawbits(_t[2]) > 0) {
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         case SSOP_JLE:
             if(surgescript_var_get_rawbits(_t[2]) <= 0) {
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         case SSOP_JGE:
             if(surgescript_var_get_rawbits(_t[2]) >= 0) {
                 *ip = a.u;
                 return;
             }
-            else
-                break;
+            break;
 
         /* function calls */
         case SSOP_CALL:
diff --git a/src/surgescript/runtime/program.h b/src/surgescript/runtime/program.h
index 040599c..a2dc173 100644
--- a/src/surgescript/runtime/program.h
+++ b/src/surgescript/runtime/program.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/program_operators.h b/src/surgescript/runtime/program_operators.h
index 4bcea79..a713f47 100644
--- a/src/surgescript/runtime/program_operators.h
+++ b/src/surgescript/runtime/program_operators.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/program_pool.c b/src/surgescript/runtime/program_pool.c
index 8a39b08..4c0c897 100644
--- a/src/surgescript/runtime/program_pool.c
+++ b/src/surgescript/runtime/program_pool.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/program_pool.h b/src/surgescript/runtime/program_pool.h
index 796fec5..68026a4 100644
--- a/src/surgescript/runtime/program_pool.h
+++ b/src/surgescript/runtime/program_pool.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/renv.c b/src/surgescript/runtime/renv.c
index 856383c..7b39dad 100644
--- a/src/surgescript/runtime/renv.c
+++ b/src/surgescript/runtime/renv.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/renv.h b/src/surgescript/runtime/renv.h
index 2fd1012..872c6cf 100644
--- a/src/surgescript/runtime/renv.h
+++ b/src/surgescript/runtime/renv.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/application.c b/src/surgescript/runtime/sslib/application.c
index e1a441d..2b7cd5a 100644
--- a/src/surgescript/runtime/sslib/application.c
+++ b/src/surgescript/runtime/sslib/application.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/arguments.c b/src/surgescript/runtime/sslib/arguments.c
index b4f6815..875dca4 100644
--- a/src/surgescript/runtime/sslib/arguments.c
+++ b/src/surgescript/runtime/sslib/arguments.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018, 2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/array.c b/src/surgescript/runtime/sslib/array.c
index b524166..7794c0f 100644
--- a/src/surgescript/runtime/sslib/array.c
+++ b/src/surgescript/runtime/sslib/array.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/boolean.c b/src/surgescript/runtime/sslib/boolean.c
index 9bc9cd8..44d8bb3 100644
--- a/src/surgescript/runtime/sslib/boolean.c
+++ b/src/surgescript/runtime/sslib/boolean.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/console.c b/src/surgescript/runtime/sslib/console.c
index b7ef346..8ad58fb 100644
--- a/src/surgescript/runtime/sslib/console.c
+++ b/src/surgescript/runtime/sslib/console.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/date.c b/src/surgescript/runtime/sslib/date.c
index d737aab..0d68e70 100644
--- a/src/surgescript/runtime/sslib/date.c
+++ b/src/surgescript/runtime/sslib/date.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/dictionary.c b/src/surgescript/runtime/sslib/dictionary.c
index 8484124..1d30e5b 100644
--- a/src/surgescript/runtime/sslib/dictionary.c
+++ b/src/surgescript/runtime/sslib/dictionary.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/gc.c b/src/surgescript/runtime/sslib/gc.c
index abc40a8..d8120b5 100644
--- a/src/surgescript/runtime/sslib/gc.c
+++ b/src/surgescript/runtime/sslib/gc.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/math.c b/src/surgescript/runtime/sslib/math.c
index 496fac0..2a64db8 100644
--- a/src/surgescript/runtime/sslib/math.c
+++ b/src/surgescript/runtime/sslib/math.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/number.c b/src/surgescript/runtime/sslib/number.c
index 62a8b25..fc6ae39 100644
--- a/src/surgescript/runtime/sslib/number.c
+++ b/src/surgescript/runtime/sslib/number.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/object.c b/src/surgescript/runtime/sslib/object.c
index da8f371..a16314f 100644
--- a/src/surgescript/runtime/sslib/object.c
+++ b/src/surgescript/runtime/sslib/object.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -79,7 +79,6 @@ void surgescript_sslib_register_object(surgescript_vm_t* vm)
     surgescript_vm_bind(vm, "Object", "children", fun_children, 1);
     surgescript_vm_bind(vm, "Object", "childWithTag", fun_childwithtag, 1);
     surgescript_vm_bind(vm, "Object", "childrenWithTag", fun_childrenwithtag, 1);
-    surgescript_vm_bind(vm, "Object", "get_childCount", fun_childcount, 0);
     surgescript_vm_bind(vm, "Object", "findObject", fun_findobject, 1);
     surgescript_vm_bind(vm, "Object", "findObjects", fun_findobjects, 1);
     surgescript_vm_bind(vm, "Object", "findObjectWithTag", fun_findobjectwithtag, 1);
@@ -101,6 +100,8 @@ void surgescript_sslib_register_object(surgescript_vm_t* vm)
     surgescript_vm_bind(vm, "Object", "get___timespent", fun_timespent, 0);
     surgescript_vm_bind(vm, "Object", "get___memspent", fun_memspent, 0);
     surgescript_vm_bind(vm, "Object", "get___file", fun_file, 0);
+    surgescript_vm_bind(vm, "Object", "get___childCount", fun_childcount, 0);
+    surgescript_vm_bind(vm, "Object", "get_childCount", fun_childcount, 0); /* obsolete since 0.5.5 */
 }
 
 
diff --git a/src/surgescript/runtime/sslib/plugin.c b/src/surgescript/runtime/sslib/plugin.c
index a8e31c2..fc17a2f 100644
--- a/src/surgescript/runtime/sslib/plugin.c
+++ b/src/surgescript/runtime/sslib/plugin.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/sslib.h b/src/surgescript/runtime/sslib/sslib.h
index 278edeb..44becc2 100644
--- a/src/surgescript/runtime/sslib/sslib.h
+++ b/src/surgescript/runtime/sslib/sslib.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/string.c b/src/surgescript/runtime/sslib/string.c
index e9193a1..05bc1ed 100644
--- a/src/surgescript/runtime/sslib/string.c
+++ b/src/surgescript/runtime/sslib/string.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/surgescript.c b/src/surgescript/runtime/sslib/surgescript.c
index 03e53de..1f785aa 100644
--- a/src/surgescript/runtime/sslib/surgescript.c
+++ b/src/surgescript/runtime/sslib/surgescript.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/system.c b/src/surgescript/runtime/sslib/system.c
index 46f15c5..f53ddc5 100644
--- a/src/surgescript/runtime/sslib/system.c
+++ b/src/surgescript/runtime/sslib/system.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/tags.c b/src/surgescript/runtime/sslib/tags.c
index c8b2043..4ce4e86 100644
--- a/src/surgescript/runtime/sslib/tags.c
+++ b/src/surgescript/runtime/sslib/tags.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/temp.c b/src/surgescript/runtime/sslib/temp.c
index 739d3e3..bbf2217 100644
--- a/src/surgescript/runtime/sslib/temp.c
+++ b/src/surgescript/runtime/sslib/temp.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/sslib/time.c b/src/surgescript/runtime/sslib/time.c
index 6e91355..19297db 100644
--- a/src/surgescript/runtime/sslib/time.c
+++ b/src/surgescript/runtime/sslib/time.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@ surgescript_var_t* fun_constructor(surgescript_object_t* object, const surgescri
     ssassert(START_ADDR == surgescript_heap_malloc(heap));
 
     surgescript_var_set_number(surgescript_heap_at(heap, TIME_ADDR), 0.0);
-    surgescript_var_set_number(surgescript_heap_at(heap, DELTA_ADDR), 0.016);
+    surgescript_var_set_number(surgescript_heap_at(heap, DELTA_ADDR), 0.01667);
     surgescript_var_set_number(surgescript_heap_at(heap, START_ADDR), surgescript_util_gettickcount() * 0.001);
 
     return NULL;
diff --git a/src/surgescript/runtime/stack.c b/src/surgescript/runtime/stack.c
index c4e02bf..a36f75e 100644
--- a/src/surgescript/runtime/stack.c
+++ b/src/surgescript/runtime/stack.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/stack.h b/src/surgescript/runtime/stack.h
index 0777994..0569192 100644
--- a/src/surgescript/runtime/stack.h
+++ b/src/surgescript/runtime/stack.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/tag_system.c b/src/surgescript/runtime/tag_system.c
index b2e26ae..66ab397 100644
--- a/src/surgescript/runtime/tag_system.c
+++ b/src/surgescript/runtime/tag_system.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/tag_system.h b/src/surgescript/runtime/tag_system.h
index 499bfe4..41aaa8c 100644
--- a/src/surgescript/runtime/tag_system.h
+++ b/src/surgescript/runtime/tag_system.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/variable.c b/src/surgescript/runtime/variable.c
index 95f2060..035f877 100644
--- a/src/surgescript/runtime/variable.c
+++ b/src/surgescript/runtime/variable.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -299,15 +299,19 @@ char* surgescript_var_get_string(const surgescript_var_t* var, const surgescript
     switch(var->type) {
         case SSVAR_NULL:
             return ssstrdup("null");
+
         case SSVAR_BOOL:
             return ssstrdup(var->boolean ? "true" : "false");
+
         case SSVAR_STRING:
             return ssstrdup(var->string);
+
         case SSVAR_NUMBER: {
             char buf[32];
             surgescript_var_to_string(var, buf, sizeof(buf));
             return ssstrdup(buf);
         }
+
         case SSVAR_OBJECTHANDLE: {
             if(manager != NULL) {
                 surgescript_object_t* obj = surgescript_objectmanager_get(manager, var->handle);
@@ -320,8 +324,12 @@ char* surgescript_var_get_string(const surgescript_var_t* var, const surgescript
             else
                 return ssstrdup("[object]");
         }
+
         case SSVAR_RAW:
             return ssstrdup("<raw>");
+
+        default:
+            return ssstrdup("<unknown>");
     }
 }
 
@@ -335,15 +343,17 @@ unsigned surgescript_var_get_objecthandle(const surgescript_var_t* var)
     switch(var->type) {
         case SSVAR_OBJECTHANDLE:
             return var->handle;
+
         case SSVAR_NUMBER:
             return surgescript_objectmanager_system_object(NULL, "Number");
+
         case SSVAR_STRING:
             return surgescript_objectmanager_system_object(NULL, "String");
+
         case SSVAR_BOOL:
             return surgescript_objectmanager_system_object(NULL, "Boolean");
-        case SSVAR_NULL:
-            return surgescript_objectmanager_null(NULL);
-        case SSVAR_RAW:
+
+        default:
             return surgescript_objectmanager_null(NULL);
     }
 }
@@ -521,7 +531,7 @@ int surgescript_var_compare(const surgescript_var_t* a, const surgescript_var_t*
             case SSVAR_NUMBER: {
                 /* encourage users to use approximatelyEqual() */
                 /* epsilon comparisons may cause underlying problems, e.g., with infinity */
-                return (a->number > b->number) - (a->number < b->number);
+                return isgreater(a->number, b->number) - isless(a->number, b->number);
             }
             case SSVAR_RAW:
                 return (a->raw > b->raw) - (a->raw < b->raw);
@@ -560,9 +570,10 @@ int surgescript_var_compare(const surgescript_var_t* a, const surgescript_var_t*
             unsigned long y = surgescript_var_get_objecthandle(b);
             return (x > y) - (x < y);
         }
-        else
-            return 0; /* this shouldn't happen */
     }
+
+    /* this shouldn't happen */
+    return 0;
 }
 
 /*
@@ -748,6 +759,10 @@ surgescript_varbucket_t* allocate_bucket()
 /* Deallocates a bucket (must be fast) */
 void free_bucket(surgescript_varbucket_t* bucket)
 {
+    /* can't free if not in use */
+    ssassert(bucket->in_use);
+
+    /* put the bucket back in the pool */
     bucket->in_use = false;
     bucket->next = varpool_currbucket;
     varpool_currbucket = bucket;
diff --git a/src/surgescript/runtime/variable.h b/src/surgescript/runtime/variable.h
index 0997a8c..f665066 100644
--- a/src/surgescript/runtime/variable.h
+++ b/src/surgescript/runtime/variable.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/runtime/vm.c b/src/surgescript/runtime/vm.c
index 8db64ce..4105f29 100644
--- a/src/surgescript/runtime/vm.c
+++ b/src/surgescript/runtime/vm.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include "program_pool.h"
 #include "tag_system.h"
 #include "object_manager.h"
+#include "vm_time.h"
 #include "sslib/sslib.h"
 #include "../compiler/parser.h"
 #include "../util/util.h"
@@ -62,13 +63,13 @@ struct surgescript_vm_t
     surgescript_objectmanager_t* object_manager;
     surgescript_parser_t* parser;
     surgescript_vmargs_t* args;
-    double start_time;
+    surgescript_vmtime_t* time;
+    bool is_paused;
 };
 
 /* misc */
-static void create_vm_components(surgescript_vm_t* vm);
-static void destroy_vm_components(surgescript_vm_t* vm);
-static void setup_sslib(surgescript_vm_t* vm);
+static void init_vm(surgescript_vm_t* vm);
+static void release_vm(surgescript_vm_t* vm);
 static bool call_updater1(surgescript_object_t* object, void* updater);
 static bool call_updater2(surgescript_object_t* object, void* updater);
 static bool call_updater3(surgescript_object_t* object, void* updater);
@@ -89,8 +90,7 @@ surgescript_vm_t* surgescript_vm_create()
     /* set up the VM */
     sslog("Creating the VM...");
     surgescript_var_init_pool();
-    create_vm_components(vm);
-    setup_sslib(vm);
+    init_vm(vm);
 
     /* done! */
     return vm;
@@ -103,7 +103,7 @@ surgescript_vm_t* surgescript_vm_create()
 surgescript_vm_t* surgescript_vm_destroy(surgescript_vm_t* vm)
 {
     sslog("Shutting down the VM...");
-    destroy_vm_components(vm);
+    release_vm(vm);
     surgescript_var_release_pool();
     return ssfree(vm);
 }
@@ -119,14 +119,13 @@ bool surgescript_vm_reset(surgescript_vm_t* vm)
     if(surgescript_vm_is_active(vm)) {
         /* shut down */
         sslog("Shutting down the VM...");
-        destroy_vm_components(vm);
+        release_vm(vm);
         surgescript_var_release_pool();
 
         /* set up the VM again */
         sslog("Starting the VM again...");
         surgescript_var_init_pool();
-        create_vm_components(vm);
-        setup_sslib(vm);
+        init_vm(vm);
 
         /* done */
         return true;
@@ -194,7 +193,7 @@ void surgescript_vm_launch_ex(surgescript_vm_t* vm, int argc, char** argv)
 
 /*
  * surgescript_vm_is_active()
- * Is the VM active?
+ * Is the VM active? (i.e., turned ON)
  */
 bool surgescript_vm_is_active(surgescript_vm_t* vm)
 {
@@ -204,29 +203,27 @@ bool surgescript_vm_is_active(surgescript_vm_t* vm)
 
 /*
  * surgescript_vm_update()
- * Updates the VM
+ * Updates the VM. Returns true if the VM is active after this update cycle
  */
 bool surgescript_vm_update(surgescript_vm_t* vm)
 {
-    if(surgescript_vm_is_active(vm)) {
-        surgescript_object_t* root = surgescript_vm_root_object(vm);
-        surgescript_object_traverse_tree(root, surgescript_object_update);
-        return surgescript_vm_is_active(vm);
-    }
-    else
-        return false;
+    return surgescript_vm_update_ex(vm, NULL, NULL, NULL);
 }
 
 /*
  * surgescript_vm_update_ex()
- * Updates the VM, allowing user-defined callbacks as well
+ * Updates the VM, allowing user-defined callbacks as well.
+ * Returns true if the VM is active after this update cycle
  */
 bool surgescript_vm_update_ex(surgescript_vm_t* vm, void* user_data, void (*user_update)(surgescript_object_t*,void*), void (*late_update)(surgescript_object_t*,void*))
 {
-    if(surgescript_vm_is_active(vm)) {
+    if(surgescript_vm_is_active(vm) && !vm->is_paused) {
         surgescript_object_t* root = surgescript_vm_root_object(vm);
         surgescript_vm_updater_t updater = { user_data, user_update, late_update };
 
+        /* update time */
+        surgescript_vmtime_update(vm->time);
+
         /* update */
         if(user_update != NULL && late_update != NULL)
             surgescript_object_traverse_tree_ex(root, &updater, call_updater3);
@@ -240,13 +237,15 @@ bool surgescript_vm_update_ex(surgescript_vm_t* vm, void* user_data, void (*user
         /* done! */
         return surgescript_vm_is_active(vm);
     }
-    else
-        return false;
+    else {
+        /* return true if the VM is still on */
+        return surgescript_vm_is_active(vm);
+    }
 }
 
 /*
  * surgescript_vm_terminate()
- * terminates the vm
+ * Terminates the vm
  */
 void surgescript_vm_terminate(surgescript_vm_t* vm)
 {
@@ -254,6 +253,45 @@ void surgescript_vm_terminate(surgescript_vm_t* vm)
     surgescript_object_kill(root);
 }
 
+/*
+ * surgescript_vm_pause()
+ * Pauses the VM, so that surgescript_vm_update_ex() does not update any objects
+ */
+void surgescript_vm_pause(surgescript_vm_t* vm)
+{
+    /* nothing to do */
+    if(vm->is_paused)
+        return;
+
+    /* pause the VM */
+    surgescript_vmtime_pause(vm->time);
+    vm->is_paused = true;
+}
+
+/*
+ * surgescript_vm_resume()
+ * Resumes a paused VM
+ */
+void surgescript_vm_resume(surgescript_vm_t* vm)
+{
+    /* nothing to do */
+    if(!vm->is_paused)
+        return;
+
+    /* pause the VM */
+    surgescript_vmtime_resume(vm->time);
+    vm->is_paused = false;
+}
+
+/*
+ * surgescript_vm_is_paused()
+ * Is the VM paused?
+ */
+bool surgescript_vm_is_paused(const surgescript_vm_t* vm)
+{
+    return vm->is_paused;
+}
+
 /*
  * surgescript_vm_programpool()
  * Gets the program pool
@@ -294,11 +332,20 @@ surgescript_parser_t* surgescript_vm_parser(const surgescript_vm_t* vm)
  * surgescript_vm_args()
  * Gets the command-line arguments
  */
-surgescript_vmargs_t* surgescript_vm_args(const surgescript_vm_t* vm)
+const surgescript_vmargs_t* surgescript_vm_args(const surgescript_vm_t* vm)
 {
     return vm->args;
 }
 
+/*
+ * surgescript_vm_time()
+ * Gets the VM time
+ */
+const surgescript_vmtime_t* surgescript_vm_time(const surgescript_vm_t* vm)
+{
+    return vm->time;
+}
+
 /*
  * surgescript_vm_root_object()
  * Gets the root object
@@ -353,32 +400,21 @@ void surgescript_vm_install_plugin(surgescript_vm_t* vm, const char* object_name
 
 /* ----- private ----- */
 
-/* creates the VM components */
-void create_vm_components(surgescript_vm_t* vm)
+/* initializes the VM */
+void init_vm(surgescript_vm_t* vm)
 {
-    vm->start_time = 0.0;
+    vm->is_paused = false;
+
+    /* create the VM components */
     vm->stack = surgescript_stack_create();
     vm->program_pool = surgescript_programpool_create();
     vm->tag_system = surgescript_tagsystem_create();
     vm->args = surgescript_vmargs_create();
-    vm->object_manager = surgescript_objectmanager_create(vm->program_pool, vm->tag_system, vm->stack, vm->args);
+    vm->time = surgescript_vmtime_create();
+    vm->object_manager = surgescript_objectmanager_create(vm->program_pool, vm->tag_system, vm->stack, vm->args, vm->time);
     vm->parser = surgescript_parser_create(vm->program_pool, vm->tag_system);
-}
-
-/* destroys the VM components */
-void destroy_vm_components(surgescript_vm_t* vm)
-{
-    surgescript_parser_destroy(vm->parser);
-    surgescript_objectmanager_destroy(vm->object_manager);
-    surgescript_vmargs_destroy(vm->args);
-    surgescript_tagsystem_destroy(vm->tag_system);
-    surgescript_programpool_destroy(vm->program_pool);
-    surgescript_stack_destroy(vm->stack);
-}
 
-/* load the SurgeScript library */
-void setup_sslib(surgescript_vm_t* vm)
-{
+    /* load the SurgeScript standard library */
     surgescript_sslib_register_object(vm);
     surgescript_sslib_register_string(vm);
     surgescript_sslib_register_number(vm);
@@ -399,6 +435,19 @@ void setup_sslib(surgescript_vm_t* vm)
     surgescript_sslib_register_system(vm);
 }
 
+/* releases the VM */
+void release_vm(surgescript_vm_t* vm)
+{
+    /* destroy the VM components */
+    surgescript_parser_destroy(vm->parser);
+    surgescript_objectmanager_destroy(vm->object_manager);
+    surgescript_vmtime_destroy(vm->time);
+    surgescript_vmargs_destroy(vm->args);
+    surgescript_tagsystem_destroy(vm->tag_system);
+    surgescript_programpool_destroy(vm->program_pool);
+    surgescript_stack_destroy(vm->stack);
+}
+
 /* these auxiliary functions help traversing the object tree */
 bool call_updater1(surgescript_object_t* object, void* updater)
 {
diff --git a/src/surgescript/runtime/vm.h b/src/surgescript/runtime/vm.h
index 4d1aa96..9c756dc 100644
--- a/src/surgescript/runtime/vm.h
+++ b/src/surgescript/runtime/vm.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018  Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022  Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,17 +22,19 @@
 #ifndef _SURGESCRIPT_RUNTIME_VM_H
 #define _SURGESCRIPT_RUNTIME_VM_H
 
+#include <stdint.h>
 #include <stdbool.h>
 #include "program.h"
 #include "object.h"
 
 /* types */
 typedef struct surgescript_vm_t surgescript_vm_t;
-struct surgescript_vmargs_t;
 struct surgescript_parser_t;
 struct surgescript_programpool_t;
-struct surgescript_tagsystem_t;
 struct surgescript_objectmanager_t;
+struct surgescript_tagsystem_t;
+struct surgescript_vmargs_t;
+struct surgescript_vmtime_t;
 
 /* api */
 surgescript_vm_t* surgescript_vm_create();
@@ -43,19 +45,24 @@ bool surgescript_vm_compile(surgescript_vm_t* vm, const char* absolute_path); /*
 bool surgescript_vm_compile_code_in_memory(surgescript_vm_t* vm, const char* code); /* compiles the given code */
 
 /* VM lifecycle */
-bool surgescript_vm_is_active(surgescript_vm_t* vm); /* is the vm active? */
+bool surgescript_vm_is_active(surgescript_vm_t* vm); /* is the vm active? (i.e., turned on) */
 void surgescript_vm_launch(surgescript_vm_t* vm); /* boots up the vm */
 void surgescript_vm_launch_ex(surgescript_vm_t* vm, int argc, char** argv); /* boots up the vm with command line arguments */
 void surgescript_vm_terminate(surgescript_vm_t* vm); /* terminates the vm */
+bool surgescript_vm_reset(surgescript_vm_t* vm); /* resets the VM, clearing up all its programs and objects */
 bool surgescript_vm_update(surgescript_vm_t* vm); /* updates the vm */
 bool surgescript_vm_update_ex(surgescript_vm_t* vm, void* user_data, void (*user_update)(surgescript_object_t*,void*), void (*late_update)(surgescript_object_t*,void*)); /* updates the vm and allows more callbacks */
+void surgescript_vm_pause(surgescript_vm_t* vm); /* pause the VM */
+void surgescript_vm_resume(surgescript_vm_t* vm); /* resume a paused VM */
+bool surgescript_vm_is_paused(const surgescript_vm_t* vm); /* is the VM paused? */
 
 /* VM components */
 struct surgescript_programpool_t* surgescript_vm_programpool(const surgescript_vm_t* vm); /* gets the program pool */
 struct surgescript_tagsystem_t* surgescript_vm_tagsystem(const surgescript_vm_t* vm); /* gets the tag system */
 struct surgescript_objectmanager_t* surgescript_vm_objectmanager(const surgescript_vm_t* vm); /* gets the object manager */
 struct surgescript_parser_t* surgescript_vm_parser(const surgescript_vm_t* vm); /* gets the parser */
-struct surgescript_vmargs_t* surgescript_vm_args(const surgescript_vm_t* vm); /* gets the command-line arguments */
+const struct surgescript_vmargs_t* surgescript_vm_args(const surgescript_vm_t* vm); /* gets the command-line arguments */
+const struct surgescript_vmtime_t* surgescript_vm_time(const surgescript_vm_t* vm); /* gets the VM time */
 
 /* utilities */
 surgescript_object_t* surgescript_vm_root_object(surgescript_vm_t* vm); /* root object */
@@ -63,6 +70,5 @@ surgescript_object_t* surgescript_vm_spawn_object(surgescript_vm_t* vm, surgescr
 surgescript_object_t* surgescript_vm_find_object(surgescript_vm_t* vm, const char* object_name); /* finds an object */
 void surgescript_vm_bind(surgescript_vm_t* vm, const char* object_name, const char* fun_name, surgescript_program_cfunction_t cfun, int num_params); /* binds a C function to an object */
 void surgescript_vm_install_plugin(surgescript_vm_t* vm, const char* object_name); /* sets a certain object as a plugin */
-bool surgescript_vm_reset(surgescript_vm_t* vm); /* resets a VM, clearing up all its programs and objects */
 
 #endif
diff --git a/src/surgescript/runtime/vm_time.c b/src/surgescript/runtime/vm_time.c
new file mode 100644
index 0000000..0408fa7
--- /dev/null
+++ b/src/surgescript/runtime/vm_time.c
@@ -0,0 +1,114 @@
+/*
+ * SurgeScript
+ * A scripting language for games
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * runtime/vm_time.c
+ * SurgeScript Virtual Machine Time - this is used to count time
+ */
+
+#include "vm_time.h"
+#include "../util/util.h"
+
+/* VM time */
+struct surgescript_vmtime_t {
+    uint64_t time; /* in ms */
+    uint64_t ticks_at_last_update;
+    bool is_paused;
+};
+
+/*
+ * surgescript_vmtime_create()
+ * Create a VM time object
+ */
+surgescript_vmtime_t* surgescript_vmtime_create()
+{
+    surgescript_vmtime_t* vmtime = ssmalloc(sizeof *vmtime);
+
+    vmtime->time = 0;
+    vmtime->ticks_at_last_update = surgescript_util_gettickcount();
+    vmtime->is_paused = false;
+
+    return vmtime;
+}
+
+/*
+ * surgescript_vmtime_destroy()
+ * Destroy a VM time object
+ */
+surgescript_vmtime_t* surgescript_vmtime_destroy(surgescript_vmtime_t* vmtime)
+{
+    ssfree(vmtime);
+    return NULL;
+}
+
+/*
+ * surgescript_vmtime_update()
+ * Update the VM time object
+ */
+void surgescript_vmtime_update(surgescript_vmtime_t* vmtime)
+{
+    uint64_t now = surgescript_util_gettickcount();
+    uint64_t delta_time = now > vmtime->ticks_at_last_update ? now - vmtime->ticks_at_last_update : 0;
+    vmtime->time += vmtime->is_paused ? 0 : delta_time;
+    vmtime->ticks_at_last_update = now;
+}
+
+/*
+ * surgescript_vmtime_pause()
+ * Pause the VM time
+ */
+void surgescript_vmtime_pause(surgescript_vmtime_t* vmtime)
+{
+    /* nothing to do */
+    if(vmtime->is_paused)
+        return;
+
+    /* pause the time */
+    vmtime->is_paused = true;
+}
+
+/*
+ * surgescript_vmtime_resume()
+ * Resume the VM time
+ */
+void surgescript_vmtime_resume(surgescript_vmtime_t* vmtime)
+{
+    /* nothing to do */
+    if(!vmtime->is_paused)
+        return;
+
+    /* resume the time */
+    vmtime->ticks_at_last_update = surgescript_util_gettickcount();
+    vmtime->is_paused = false;
+}
+
+/*
+ * surgescript_vmtime_time()
+ * Get the time, in milliseconds, at the beginning of the current update cycle
+ */
+uint64_t surgescript_vmtime_time(const surgescript_vmtime_t* vmtime)
+{
+    return vmtime->time;
+}
+
+/*
+ * surgescript_vmtime_is_paused()
+ * Is the VM time paused?
+ */
+bool surgescript_vmtime_is_paused(const surgescript_vmtime_t* vmtime)
+{
+    return vmtime->is_paused;
+}
\ No newline at end of file
diff --git a/src/surgescript/runtime/vm_time.h b/src/surgescript/runtime/vm_time.h
new file mode 100644
index 0000000..2e17962
--- /dev/null
+++ b/src/surgescript/runtime/vm_time.h
@@ -0,0 +1,40 @@
+/*
+ * SurgeScript
+ * A scripting language for games
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * runtime/vm_time.h
+ * SurgeScript Virtual Machine Time - this is used to count time
+ */
+
+#ifndef _SURGESCRIPT_RUNTIME_VM_TIME_H
+#define _SURGESCRIPT_RUNTIME_VM_TIME_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct surgescript_vmtime_t surgescript_vmtime_t;
+
+surgescript_vmtime_t* surgescript_vmtime_create(); /* create a VM time object */
+surgescript_vmtime_t* surgescript_vmtime_destroy(surgescript_vmtime_t* vmtime); /* destroy a VM time object */
+
+void surgescript_vmtime_update(surgescript_vmtime_t* vmtime); /* update the VM time object */
+void surgescript_vmtime_pause(surgescript_vmtime_t* vmtime); /* pause the VM time */
+void surgescript_vmtime_resume(surgescript_vmtime_t* vmtime); /* resume the VM time */
+
+uint64_t surgescript_vmtime_time(const surgescript_vmtime_t* vmtime); /* the time at the beginning of the current update cycle */
+bool surgescript_vmtime_is_paused(const surgescript_vmtime_t* vmtime); /* is the VM time paused? */
+
+#endif
\ No newline at end of file
diff --git a/src/surgescript/util/fasthash.c b/src/surgescript/util/fasthash.c
index f4f4550..de15bae 100644
--- a/src/surgescript/util/fasthash.c
+++ b/src/surgescript/util/fasthash.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/util/fasthash.h b/src/surgescript/util/fasthash.h
index 972b311..653368a 100644
--- a/src/surgescript/util/fasthash.h
+++ b/src/surgescript/util/fasthash.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/util/ssarray.h b/src/surgescript/util/ssarray.h
index 1aa4287..f448732 100644
--- a/src/surgescript/util/ssarray.h
+++ b/src/surgescript/util/ssarray.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2018 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
  * SSARRAY()
  * declares an array of a certain type
  */
-#define SSARRAY(type, arr)                    type* arr; size_t arr##_len, arr##_cap;
+#define SSARRAY(type, arr)                    type* arr; size_t arr##_len, arr##_cap
 
 /*
  * ssarray_init()
diff --git a/src/surgescript/util/transform.c b/src/surgescript/util/transform.c
index dad3b17..1270f3f 100644
--- a/src/surgescript/util/transform.c
+++ b/src/surgescript/util/transform.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/util/transform.h b/src/surgescript/util/transform.h
index e6e4ba6..ef14dc3 100644
--- a/src/surgescript/util/transform.h
+++ b/src/surgescript/util/transform.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2019 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/src/surgescript/util/util.c b/src/surgescript/util/util.c
index 3ea420b..83d6042 100644
--- a/src/surgescript/util/util.c
+++ b/src/surgescript/util/util.c
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
 #endif
 
 /* private stuff */
-static void mem_crash(const char* location);
+static void mem_crash(const char* file, int line);
 static void my_log(const char* message);
 static void my_fatal(const char* message);
 static void (*log_function)(const char* message) = my_log;
@@ -51,12 +51,12 @@ static void (*fatal_function)(const char* message) = my_fatal;
  * surgescript_util_malloc()
  * Memory allocation routine
  */
-void* surgescript_util_malloc(size_t bytes, const char* location)
+void* surgescript_util_malloc(size_t bytes, const char* file, int line)
 {
     void *m = malloc(bytes);
 
     if(m == NULL)
-        mem_crash(location);
+        mem_crash(file, line);
 
     return m;
 }
@@ -65,12 +65,12 @@ void* surgescript_util_malloc(size_t bytes, const char* location)
  * surgescript_util_realloc()
  * Memory reallocation routine
  */
-void* surgescript_util_realloc(void* ptr, size_t bytes, const char* location)
+void* surgescript_util_realloc(void* ptr, size_t bytes, const char* file, int line)
 {
     void *m = realloc(ptr, bytes);
 
     if(m == NULL)
-        mem_crash(location);
+        mem_crash(file, line);
 
     return m;
 }
@@ -81,7 +81,7 @@ void* surgescript_util_realloc(void* ptr, size_t bytes, const char* location)
  */
 void* surgescript_util_free(void* ptr)
 {
-    if(ptr)
+    if(ptr != NULL)
         free(ptr);
 
     return NULL;
@@ -220,9 +220,9 @@ char* surgescript_util_strncpy(char* dst, const char* src, size_t n)
  * surgescript_util_strdup()
  * Copies a string into another, allocating the required memory
  */
-char* surgescript_util_strdup(const char* src, const char* location)
+char* surgescript_util_strdup(const char* src, const char* file, int line)
 {
-    char* str = surgescript_util_malloc(sizeof(char) * (1 + strlen(src)), location);
+    char* str = surgescript_util_malloc(sizeof(char) * (1 + strlen(src)), file, line);
     return strcpy(str, src);
 }
 
@@ -378,10 +378,13 @@ void my_fatal(const char* message)
     fprintf(stderr, "%s\n", message);
 }
 
-void mem_crash(const char* location) /* out of memory error */
+void mem_crash(const char* file, int line) /* out of memory error */
 {
-    static char buf[128] = "Out of memory in ";
-    surgescript_util_strncpy(buf + 17, location, sizeof(buf) - 17);
+    static char buf[1024] = "Out of memory in ";
+    static const int prefix_len = 17;
+
+    snprintf(buf + prefix_len, sizeof(buf) - prefix_len, "%s:%d", file, line);
     fatal_function(buf);
+
     exit(1); /* just in case */
-}
+}
\ No newline at end of file
diff --git a/src/surgescript/util/util.h b/src/surgescript/util/util.h
index a505a49..df95cdb 100644
--- a/src/surgescript/util/util.h
+++ b/src/surgescript/util/util.h
@@ -1,7 +1,7 @@
 /*
  * SurgeScript
  * A scripting language for games
- * Copyright 2016-2020 Alexandre Martins <alemartf(at)gmail(dot)com>
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,15 +33,15 @@
 #define sssign(x)                   ((x) >= 0 ? 1 : -1)
 #define sstok(x)                    #x
 #define ssstr(x)                    sstok(x)
-#define ssassert(expr)              do { if(!(expr)) ssfatal("%s", "In " __FILE__ ":" ssstr(__LINE__) ": assertion `" sstok(expr) "` failed."); } while(0)
+#define ssassert(expr)              do { if(!(expr)) ssfatal("In %s:%d: %s", __FILE__, __LINE__, ": assertion `" sstok(expr) "` failed."); } while(0)
 
 /* common aliases */
-#define ssmalloc(n)                 surgescript_util_malloc((n), __FILE__ ":" ssstr(__LINE__))
-#define ssrealloc(p, n)             surgescript_util_realloc((p), (n), __FILE__ ":" ssstr(__LINE__))
+#define ssmalloc(n)                 surgescript_util_malloc((n), __FILE__, __LINE__)
+#define ssrealloc(p, n)             surgescript_util_realloc((p), (n), __FILE__, __LINE__)
 #define ssfree                      surgescript_util_free
 #define sslog                       surgescript_util_log
 #define ssfatal                     surgescript_util_fatal
-#define ssstrdup(str)               surgescript_util_strdup((str), __FILE__ ":" ssstr(__LINE__))
+#define ssstrdup(str)               surgescript_util_strdup((str), __FILE__, __LINE__)
 
 /* constants */
 #define SS_NAMEMAX                  63 /* names can't be larger than this (computes hashes quickly) */
@@ -53,8 +53,8 @@ const char* surgescript_util_year(); /* year string of the SurgeScript runtime *
 const char* surgescript_util_website(); /* project website */
 const char* surgescript_util_authors(); /* project authors */
 
-void* surgescript_util_malloc(size_t bytes, const char* location); /* memory allocation */
-void* surgescript_util_realloc(void* ptr, size_t bytes, const char* location); /* memory reallocation */
+void* surgescript_util_malloc(size_t bytes, const char* file, int line); /* memory allocation */
+void* surgescript_util_realloc(void* ptr, size_t bytes, const char* file, int line); /* memory reallocation */
 void* surgescript_util_free(void* ptr); /* memory deallocation */
 
 void surgescript_util_log(const char* fmt, ...); /* logs a message */
@@ -62,7 +62,7 @@ void surgescript_util_fatal(const char* fmt, ...); /* logs a message and kills t
 void surgescript_util_set_error_functions(void (*log)(const char*), void (*fatal)(const char*)); /* set custom error functions */
 
 char* surgescript_util_strncpy(char* dst, const char* src, size_t n); /* strcpy */
-char* surgescript_util_strdup(const char* src, const char* location); /* strdup */
+char* surgescript_util_strdup(const char* src, const char* file, int line); /* strdup */
 const char* surgescript_util_basename(const char* path); /* basename */
 char* surgescript_util_accessorfun(const char* prefix, const char* text); /* getter/setter prefixing function */
 
diff --git a/src/surgescript/util/version.h.in b/src/surgescript/util/version.h.in
new file mode 100644
index 0000000..dcb952e
--- /dev/null
+++ b/src/surgescript/util/version.h.in
@@ -0,0 +1,57 @@
+/*
+ * SurgeScript
+ * A scripting language for games
+ * Copyright 2016-2022 Alexandre Martins <alemartf(at)gmail(dot)com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * util/version.h
+ * SurgeScript version: available at compile time
+ */
+
+#ifndef _SURGESCRIPT_VERSION_H
+#define _SURGESCRIPT_VERSION_H
+
+/* Import SurgeScript version */
+#define SURGESCRIPT_VERSION_SUP @PROJECT_VERSION_MAJOR@
+#define SURGESCRIPT_VERSION_SUB @PROJECT_VERSION_MINOR@
+#define SURGESCRIPT_VERSION_WIP @PROJECT_VERSION_PATCH@
+#cmakedefine SURGESCRIPT_VERSION_FIX @PROJECT_VERSION_TWEAK@
+
+#if !defined(SURGESCRIPT_VERSION_FIX)
+#define SURGESCRIPT_VERSION_FIX 0
+#endif
+
+/* Generate version string */
+#define SURGESCRIPT_VERSION_STR "@PROJECT_VERSION@"
+
+/* Convert a version tuple into an integer */
+#define SURGESCRIPT_VERSION_CODE(x, y, z, w) \
+    (((x) << 24) | ((y) << 16) | ((z) << 8) | (w))
+
+/* Compare a version tuple to the current version of SurgeScript */
+#define SURGESCRIPT_VERSION_COMPARE(x, y, z, w) ( \
+    SURGESCRIPT_VERSION_CODE( \
+        SURGESCRIPT_VERSION_SUP, \
+        SURGESCRIPT_VERSION_SUB, \
+        SURGESCRIPT_VERSION_WIP, \
+        SURGESCRIPT_VERSION_FIX \
+    ) - \
+    SURGESCRIPT_VERSION_CODE((x), (y), (z), (w)) \
+)
+
+/* Check if the current version of SurgeScript is x.y.z.w or newer */
+#define SURGESCRIPT_VERSION_IS_AT_LEAST(x, y, z, w) \
+    (SURGESCRIPT_VERSION_COMPARE((x), (y), (z), (w)) >= 0)
+
+#endif
\ No newline at end of file

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/include/surgescript/runtime/vm_time.h
-rw-r--r--  root/root   /usr/include/surgescript/util/version.h
-rw-r--r--  root/root   /usr/lib/debug/.build-id/1c/c5ac87dd795d7116ee8e9093c0d33f0d25ed1a.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/b5/b3904f0fff6db23c15c65ec68f534d727c09f3.debug
-rw-r--r--  root/root   /usr/lib/x86_64-linux-gnu/libsurgescript.so.0.5.6.1
-rw-r--r--  root/root   /usr/share/doc/surgescript/engine/collider.md.gz
lrwxrwxrwx  root/root   /usr/lib/x86_64-linux-gnu/libsurgescript.so -> libsurgescript.so.0.5.6
lrwxrwxrwx  root/root   /usr/lib/x86_64-linux-gnu/libsurgescript.so.0.5.6 -> libsurgescript.so.0.5.6.1

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/debug/.build-id/59/c88534858a95bd3e9ea6d5529f394e046563d6.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/ca/d714267585496741239fae7ebbac0860f25f02.debug
-rw-r--r--  root/root   /usr/lib/x86_64-linux-gnu/libsurgescript.so.0.5.4.4
-rw-r--r--  root/root   /usr/share/doc/surgescript/engine/collider.md
lrwxrwxrwx  root/root   /usr/lib/x86_64-linux-gnu/libsurgescript.so -> libsurgescript.so.0.5.4.4

No differences were encountered between the control files of package libsurgescript-dev

No differences were encountered between the control files of package libsurgescript0.5.4.4

Control files of package libsurgescript0.5.4.4-dbgsym: lines which differ (wdiff format)

  • Build-Ids: cad714267585496741239fae7ebbac0860f25f02 b5b3904f0fff6db23c15c65ec68f534d727c09f3

Control files of package surgescript: lines which differ (wdiff format)

  • Depends: libc6 (>= 2.3.4), 2.34), libsurgescript0.5.4.4 (>= 0.5.4.4) 0.5.6.1)

Control files of package surgescript-dbgsym: lines which differ (wdiff format)

  • Build-Ids: 59c88534858a95bd3e9ea6d5529f394e046563d6 1cc5ac87dd795d7116ee8e9093c0d33f0d25ed1a

More details

Full run details