AnotherWorld
playable game in RThis is the full code for a playable R version of the 16-bit game AnotherWorld
The game plays in realtime using 2 key packages:
{nara}
for manipulation of
nativeRaster images as a fast in-memory drawing canvas{eventloop}
for event-driven
interaction - allowing for keyboard feedback while rendering the game with
sound.However, there are still some graphics bugs etc to work out.
Please file an issue on github if you think something is definitely wrong!
A live capture of playing the game in R
library(remotes)
# Install dependencies
remotes::install_github('coolbutuseless/eventloop')
remotes::install_github('coolbutuseless/nara')
# Install packages
install.packages('audio')
install.packages('beepr')
# Grab a copy of the code (this is not distributed as a package)
system("git clone https://github.com/coolbutuseless/anotherworld")
setwd('anotherworld')
After your experiment on Earth goes awry, you are transported to another world where you must survive and, hopefully, escape!
source('game.R')
PART
variable first to the section you want to play.PART <- 16001 # <------- select the part of the game to play
source('game.R')
This looks pretty similar to the R version as it's running on the same in-game virtual machine.
Click the image below to watch it on youtube.
{audio}
package working correctlymacOS
Running this on macOS requires XQuartz to have been
installed in order to support the x11()
device.
Linux
R needs to have been compiled with x11()
and Cairo support.
The WindowsOS version of R does not currently have support for the onIdle
callback and
therefore does not support event-driven interactive graphics as implemented
in the {eventloop}
package.
If you are a windows developer capable of adding support for an onIdle
callback
to R itself, please get in touch!
The cursor icon in an {eventloop}
window will flicker because of some hard-coded
behaviour in R's double-buffered x11()
graphics device.
{eventloop}
makes use of the x11()
graphics device with a double-buffered
backend based on Cairo (x11(type = 'dbcairo')
).
The double-buffering within the graphics device is coordinated through use
of dev.hold()
and dev.flush()
calls.
Whenever dev.hold()
is called, the cursor will be set to the busy cursor
(usually a stopwatch on macOS), and when dev.flush()
is called the
cursor reverts to a normal pointer arrow.
Since dev.hold()
and dev.flush()
are called when drawing every single
frame, the cursor will flicker between these two images.
The interim solution is to not have the cursor over the window when interacting with the app. This only really works when the app solely relies on keyboard feedback (like this game of AnotherWorld).
The more complicated solution will involve crafting a patch to R itself to make the cursor change behaviour user-configurable rather than hard-coded.
If you are are an R developer capable of crafting such a patch to R , please get in touch with me!
The reference code for this port was the JavaScript code from github/cyxx/another_js.
Further details have been extracted from the C code from Fabien's C++ Interpreter.
Javascript and C code used for reference both use 0-based indexing, whereas R uses 1-based indexing.
To try and avoid editing all references to array indexint to +1
, I created
a new class cindex
which has 1-based indexing in R.
There still a lot of refacoring to do, and many structures are still indexed by a character string representation of the hexadecimal version of the index or address i.e. ugly.
JS and C both use &
and |
for bitwise operations, whereas R uses them
for logical operations.
I created a set of infix operators (%|%
etc) to mimic the bitwise operators
in C.
Data in the original game is compressed with 'bytekiller' which is a very very old skool data compression algorithm.