TurnAbout:
How to Play
History &
Future
Decks and Cards
New Deck Styles
Writing a Game
TurnAbout API
More About Lua
Included Games
Toy StatusArcadia:
Home
Download
Community:
Forums
Java Chat
Firewall/Router Help
Arcadia Toys:
Collaboration
DomiNation
Empyrion
synChess
synJam
synJet
synPool
synSpades
synSpace
synVille
synVideo
Turnabout
Other
Games:
Well of Souls
Warpath
Rocket Club
Developers:
TurnAbout API
New Parks
New WoS Worlds
Rocket Club
Models
Company:
Synthetic Reality
Golden Souls
Donate $
Contact Us
|
TurnAbout is one of the toys which
can be played in Arcadia. It is a
user-programmable, cards-metaphor, open-API, Lua-scripted, board game engine..
thing.
|
How To Play:TurnAbout is a work in
progress, but you can check out the TurnAbout
development notes in the Arcadia forum of the synthetic reality
bulletin board. If you want to try it after all
that, download and install Arcadia, then 'fetch new toys'
and pick up a copy to explore.
The general idea
is:
- Start or
enter an Arcadia Server where TurnAbout
is the selected Toy
- Be the
Moderator
- Select one
of your Decks from your Toy Options
dialog
- Press START
GAME
If you are
interested in crafting your own TurnAbout
mini-games, you might enjoy reading about the TurnAbout API
|
How Did We Get
Here And Where Are We Going? Since starting the game
around 4 years ago, the concept sort of grew and
flew off in different directions, so I am as
interested as anyone in seeing what actually
lands, so to speak. Originally I was thinking:
"Hmm, I ought to do some sort of board
game." And by that, I was thinking of
rolling a die, moving your marker along a path on a board and performing the
action demanded by the spot you landed.
At that time,
some people in the forums were asking for a
Magic-The-Gathering-Like engine, and that got me
thinking about 'cards' games. (as opposed to
'card games' like synSpades)
Anyway, so that
got me thinking about a hierarchical card
editor (letting you define a card
which 'inherited' named properties from other
cards - "rent=50" for example) and a deck
editor to assemble a collection of
cards.
Then, I thought,
you could form the board by
dealing a bunch of cards 'in a circle' and then
have little tokens which walked around the circle
in response to rolling a die. Wow. a 'card' game
and a 'board' game all in one. I thought of
Stonehenge about that time and decided the cards
should be vertical and why not use your Arcadia
Face as your token.... and make it semi-3D.
OK, great, so
then I had a hierarchical card/deck editor so
that individuals could craft their own game
styles and have cards with custom properties like
"name" and "type" and "hit
points"
and "gold earned when your token lands
on one"
etc.
But I knew I
would need a scripting language to make truly
interesting games. (My goal is to empower the end
user to extend TurnAbout by making new Deck
Styles). My experience with Well
of Souls
taught me how scripting languages could grow and
grow and grow, and I didn't really like the
thought of a very complicated bunch of script on
the cards themselves, but less than that was
going to be too simplistic and restrictive.
Finally I opted
to go with 'lua' because it was
powerful, popular and free. And once I got over
the hump of moving the text of the script from
the cards themselves to a single file (one per
deck style), I could finally see how all the
pieces should fit together.
At this point,
it's all about the API.. supporting the lua script with
TurnAbout functions, events, and services which
make the script's job as easy as possible, while
facilitating a huge range of potential game
styles.
TurnAbout ships
currently with several decks: Uncle
Dan's MiniMUD
which is a simple text adventure, which can be
extended by adding cards to the deck. Also
included are an implementation of Blackjack, a
ballistic Battle game, and a sailboat simulator.
All are functioning multiplayer games with full
source included.
|
So, What is TurnAbout Then? TurnAbout is, in fact, an
unlimited number of different games. Let's
PRETEND that all the TurnAbout games are
cards-based board games, though you will see that
is not actually true.
To start a
TurnAbout game, you need a moderator with a deck
of cards. All the cards in that
deck inherit from the same deck style. Therefore,
we will use the term deck style
when we mean: 'one of the mini games you
can play in TurnAbout.' (I would say
"Toylet" but that has an unfortunate
homonym.)
A deck style
might be, for example monopoly. (Um, I mean
something similar to, but not legally the same
as, of course!). While there is only a single
monopoly deck style, you might
have 5 monopoly decks, each with
different mixes of cards, resulting in, perhaps,
different real estate on the board,
or different chance cards. For Example: "The
Simpsons Edition of Monopoly" is a 'deck'
while "monopoly" is a style.
So, to show
where this fits into the overall umbrella of
Arcadia, you have:
Arcadia (handles chat,
voice, servers, player management, sound,
graphics... on behalf of its toys)
TurnAbout (one of the Toys
in Arcadia) Provides an interface between
Arcadia Services and the Deck's Style
Script. Offers several Render options to
the stye script
Deck
Style: Monopoly (a style of
TurnAbout board game), has its own
script file (monopoly.script) which
controls how the game is played.
Players can make up new deck styles
and share them.
Card:
A collection of named properties
("name=park
place", "rent=180",
..) Players can make new cards.
Each card belongs to a single
Deck Style.
Deck:
A deck of cards from a given
deck style (is used to start
up a game session with other
players). Players can make
new decks. Only the cards in
the selected deck are
available during the game.
To start the
game, the moderator picks one of his or her
decks, and presses Start Game. The deck's style
defines the lua game script which is to be
loaded. The deck's cards provide assets like...
locations, monsters, items, random events to be
used by the script. It is up to the Deck Style
itself to determine how cards are used in that
game.
What happens
after that is entirely up to the script. TurnAbout
regularly informs the style script when
interesting events happen (like someone enters
the game, or clicks on a user interface panel).
The script then asks TurnAbout
to perform various jobs on its behalf, like send
packets of data to other players.
|
More About Decks Decks are pretty
straightfoward. To select a deck, just pick it
from the Deck Selection dialog, and press the
START GAME button. Your deck will then be copied
to the other players and the COPY will be
used/modified over the course of the game, while
your original deck will be unharmed
To make a NEW
deck, you open the Toy Options dialog and click
the Deck Editor button. This
lets you create a new deck or modify an existing
one. If you create a new deck, you must indicate
which 'deck style' it will use. Then you just
drag cards in and out of your deck from the cards
you have defined in the past.
|
More
About Cards The Toy Options Dialog has a
button which opens the Card Editor,
which lets you make new cards, or modify the
properties of existing cards.
Every
card has a parent (or base) from which it
inherits properties. It may then add additional
properties which will then be inherited by its
own children. All cards ultimate inherit from the
'root' card which defines the basic properties
which ALL cards must have.
Every
card has a unique 'cardId' (which is
a number like: 0000001_23) (the 23rd card created by
the player with serial number 00000001 (that's
me!))
Any
card which has the root card as its direct
parent, is a 'style card' and defines a deck
style. It
would not, for example, be 'dealt out' in a game.
It just defines the basic properties you need for
decks to be used by your style script.
For Example, the
inheritance tree for monopoly cards might look
something like this (card id numbers shown are
just pretend, yours would vary):
root
(#00000001_1)
style:
monopoly (#00000001_3)
property CardType=unknown
name:
Real Estate (#00000001_7)
CardType=RealEstate
property Rent=0
name:
Park Place (#00000001_21)
Rent=100
name: Montgomery
Ave (#00000001_22)
Rent=50
...
name:
Rail Road (#00000001_8)
CardType=RailRoad
property Tax=50%
name:
Reading Railroad (#00000001_25)
Tax=35%
name: Caltrans
(#00000001_27)
Tax=12%
...
name:
Utility (#00000001_9)
CardType=Utility
property Fee=50
name:
gas (#00000001_28)
name: electric
(#00000001_35)
...
name:
Chance (#00000001_10)
CardType=Chance
property MoneyEarned
name:
You go to jail, pay bail!
(#00000001_32)
MoneyEarned = -1000
name: You win
lottery! (#00000001_99)
MoneyEarned =
1000000
...
See how the
Style Card defines a property called "CardType" which is inherited
by all cards of this style and has the default
value of 'unknown'. Four cards
are defined with the Style Card as their parent,
and they each set the value of this property (to:
RealEstate, RailRoad, Utility,
and Chance) creating four basic
'flavors' of card within the monopoly 'style'
In this example,
the RealEstate card defines a property 'Rent' which only applies to
Real Estate cards (not to Railroads, utilities,
or chance cards). it has a default value of 0,
but every Real Estate card overrides this with a
unique rent value (Montgomery Avenue charges 50).
But as a counter
example, the utility cards do not
override their parent's "Fee" property, so they
all charge the same fee (50) (they inherit both
the 'Fee' property AND its default value)
I Hope that
gives you the idea. You design cards for the
purposes your game needs, then later your script
can ask for the current value of property X on
cardId Y. For monopoly, only the RealEstate,
RailRoad, and Utility cards would form the game
board (making the circle through which the player
tokens move), while the Chance cards would be
just data used by the script to punish/reward you
when you landed on an appropriate board spot.
(The actual 'chance' spot on the game board might
be a special RealEstate card, named
"Chance" with a custom property "WhenUserLandsHereDrawARandomChanceCard = yes")
You could, in
fact, define a deck of 52 playing cards, and use
TurnAbout to implement multiplayer poker,
cribbage, etc. (Someday, I mean, not actually
today, since the API does not yet allow the
script to actually render anything beyond that
stonehenge of cards!!)
|
More About Lua Lua refers to itself as
an extensible extension language (read about it
here: http://www.lua.org/pil/). It extends TurnAbout,
but then is itself extended by TurnAbout. Now
that's synergy!
If you have
never programmed before, here's your chance to
learn something totally new! At no cost!
If you are a
veteran programmer, you might find lua a bit odd.
Here is a little example function which I think
recaps the bits which I personally find
sufficiently odd that I have to watch myself to
not make mistakes:
-- This is a
comment. Two dashes. Do NOT use semicolons or
pound signs or slashes!
function SortTwoNumbers( x, y
)
-- no
curly braces! use 'end' to mark the end
of a block of code
-- Don't forget the 'then' after your
'if'
-- Don't use semicolons at the end of
lines! (it won't burn you until much
later)
if ( x > y ) then
return y, x
else -- or
"elseif( condition
) then" if you have additional cases
to check
return x, y
end
end
Note those
returns! Lua can return more than one thing at a
time!
local a
local b
a, b = SortTwoNumbers( 3, 4 )
So, what's in
'a' now?
Other random
factoids
- not-equal
is tested like this: "if ( a ~= 3 )
then" Do NOT use "!="
- equality is
tested like this: "if ( a == 3 )
then" (i.e. pretty normal) and this
also works for strings
- the string
concatenation operator is TWO dots. str =
"b= " .. b .. ", and a =
" .. a
- 1
and 0 are BOTH TRUE. for false
you use 'nil' (an undefined value)
- Array
indices start at ONE, not ZERO
- Initialize
an array like this: "array =
{}"
- Add
something to an array like this:
"array[ 12 ] = 44"
- Add an
array into an array like this:
"array[ 7 ] = {1, 2, 3, 4}"
- so,
"Array[ 7 ][ 3 ]" has
"3" in it
- but Array[
99 ] has 'nil' in it (we haven't put
anything there, all arrays are 'sparse')
- Arrays are
really hashes: array[ "bill" ]
= { "male", "friend",
"april" }
- so: array[
"bill" ][ 2 ] =
"friend"
- to turn a
string into a number, do this: local
numberValue = 0 + stringValue
- if you
don't say local, then your
variable is GLOBAL, no matter what scope
it is defined in.
- Lua is all
about references, so:
local temp = array[ "bill" ]
does not COPY the entry from 'array', it
sets 'temp' to a reference to
the original. So if you modify temp, you
are also modifying the original.
|
Security
Issues So.. why not just let you compose
Toys directly in C++ and skip this whole
TurnAbout/lua thing? Well, Toys are written in an
environment which has full access to the player's
computer, which means they could potentially read
or modify any file on the player's disk drive.
This means they could, in fact, be nasty viruses
or spyware.
I have spent
years establishing my credibility as a
programmer/publisher who does NOT do nasty
things. I ultimately felt I could not expose my
reputation to the risk of allowing 3rd parties to
make Arcadia Toys directly. This made me sad
since that was my original intention.
TurnAbout/Lua,
however, is an environment where I can impose
certain sandbox controls upon the fledgling game
designer. For example, I do not provide lua any
direct io access at all. You cannot read/write
files directly. (A pain to be sure!) You have to
beg TurnAbout to do that sort of thing for you,
and TurnAbout is very stingy as to what it will
grant you access to.
|
But
isn't Lua Slow? I heard it was reeeeeally slow! It's slower than the GPU
of your 3D card, yes, so I do not recommend
trying to do any ray-tracing 3D graphics within
lua itself. But how much speed do you need to do
a board/card game?
In fact, Lua is
suprisingly peppy:
- 450
nanoseconds for TurnAbout to call a
function in your Lua script and get a
value back
- 50
microseconds for lua to do "for
var=1,1000 do" with no work in the
loop (50 nanoseconds per loop)
- 15
nanoseconds to do a floating point
multiply
- 200
nanoseconds to write to a hash: data[ var
] = "test"
- 100
nanoseconds to read value back and
compare it: if( data[ var ] ~=
"test")
All values
measured on a 2.6GHz Pentium IV, with each action
performed thousands of times (first execution
might be slower than subsequent since lua is a
'just in time compiled' language)
So.. in one
second, lua could 'count to' about twenty
million. Not as fast as 'C' running on the same
computer, but still probably a hundred or
thousand times faster than your dad's Apple ][.
|
How Do I Start a
New Deck Style From Scratch? It's probably silly of me
to document this at this time, but something like
this:
- Open the
Card Editor and press "Make New Deck
Style"
- Give it a
nice name, say: "Parchesi" (it
must only use legal windows file name
characters, and be brief). This will
create the 'style card' for your new deck
style. All your cards will ultimately
need to be children or grand-children of
this style card.
- Notice your
current serial number, since you will
need to provide a style script with a
matching name, as in: c:/arcadia/toys/toy10/assets/styles/<my
serial number in
hex>/parchesi/parchesi.script
- An initial
script file is created for you
automatically (as a copy of
00000001/blank/blank.script)
- Think about
any cards you absolutely must have, to
achieve your vision, and the 'family
tree' organizing them
- Make some
cards to flesh out your family tree. Just
enough to get started.
- Use the
Deck Editor to create a new deck "My
Parchesi Deck," based on your
Parchesi style card, and drag some
parchesi style cards into it. (Your deck
must include at least one card)
- Use the
text editor of your choice to edit your
script file (parchesi.script) and be sure
to periodically back it up in some safe
place. You never know when your cat will
decide to walk across the keyboard while
you're asleep.
- Use lots of
TA_Log( "I made it this far!" )
messages to help you debug your code
during development
- Error
messages will appear either in the chat
window or in the Debug/FunPack window.
|
How Do I Write a
Multiplayer Game? You might want to start
out with solo games if you're a total newbie, but
the trick to multiplayer games is to send packets
between the players. My generic example script
shows one way to organize this. But in general
you need to work out what sort of information
needs to be shared by packet, versus information
which can just be baked into the script or the
card deck, which everyone will have a copy of at
the beginning of the game. Send as few, small,
packets as you can. Don't send the bullets, send
the trigger.
Once you have
that thought out, ask if you want each player to
shout to everyone else "Hey, I rolled a
six!" or if you want the chaos controlled by
the moderator. You generally want the latter. In
that model, for a game action to take place, the
initiator sends a 'request' packet to the
moderator. the moderator validates it, then sends
a 'doAction' packet to all players (including
himself). All players then execute the actual
action specified, which changes the 'state' of
the game. In theory, after that everyone agrees
on the new state of the game.
As much state as
possible should be configured by the script
automatically at the start of the game. This
might include the moderator sending a packet with
a random seed value in it (TurnAbout will do this
for you, actually) so that the same random number
sequence can be used by all players, leading to
random, but synchronized, starting game state.
Newcomers in
mid-game will need some extra synchronization,
but if your game uses a lot of state, you might
just refuse to allow newcomers, rather than
sending them hundreds of packets.
For example:
- Player One
rolls a three (let's pretend that is
synched)
- Player one
sends a packet to the moderator
'requesting' his token be moved 3 spaces
- Moderator
receives request, checks that it is
player one's turn, accepts that '3' is
correct
- Moderator
sends packet to all (including himself
and player one) "move player one 3
spaces"
- Everyone
receives that packet, and executes a
function like:
function MovePlayerForward(
playerSerNum, numSpaces )
local oldPosition =
playerPositionArray[ playerSerNum ]
local newPosition =
oldPosition + numSpaces
newPosition = math.mod(newPosition,
numPositionsOnBoard) -- wrap it around
playerPositionArray[ playerSerNum ] =
newPosition
end
- And then the script makes
magical TA_xxx calls into TurnAbout to
actually animate the player token walking
around the game board. Since this happens
on every player's machine, they all see
the player walking. But the script might
sometimes check if the player serial
number from the packet matches the local
player's own serial number and do
something different, say, inject a
different message "YOU walk to the
7-11" Instead of "Bill walks to
the 7-11"
|
OK, How
Do I Send a Packet? Um, er, OK... well, at
present, to send a packet from your lua script,
you use this:
TA_SendPacket(
serNum, num1, num2, num3, num4, string1,
string2, string3 )
That is to say,
your lua packet can contain no more than 4
numbers and 3 strings. Though it can contain
less. And the numbers in the packet are integers
(max range -2 billion to +2 billion)
'serNum'
determines who will receive the packet. You can
send it to a single player (their serNum), to all
players (use serNum 0) or just the moderator (use
either the moderator's serNum or -1). For
example, this might be a packet that requests the
moderator MOVE (command = 3) playerSerNum 1234, 5
spaces forward. um, and play sound effect
"butterfly.wav"
TA_SendPacket(
-1, 3, 1234, 5, 0, "butterfly.wav",
"", "" )
That's just an
example, how you organize your packet is up to
you. Only you will ever see these packets, since
once you send them, they go only to the lua
scripts running on the other player's machine.
Your script must define an event handler function
to 'receive' incoming packets
function TA_OnPacket(
serNumSender, num1, num2, num3, num4, str1,
str2, str3 )
--
serNumSender is the origin of the packet
(the guy who called TA_SendPacket)
-- This is probably different from serNum
of the player being moved
-- (in num2 of our example)
-- Often serNumSender is the moderator,
and you probably want to ignore certain
-- packets UNLESS they came from the
moderator.
-- num1
is the COMMAND
-- num2 is the player who is being moved
-- num3 is how many spaces he is moving
-- str1 is the name of the WAVE file to
be played
if ( num1 == 3 )
then
--
Command 3 is the MOVE command, we
have decided
MovePlayerForward(
num2, num3 )
PlaySoundEffect(
str1 ) -- assuming you have a
function like this
end
end
The
thing to watch out for are packets which
stimulate the sending of the same packets that
stimulated the sending of the packets that
stimulated.... And if you feel you need more than
4 numbers and 3 strings in your packet, ask
yourself if there is anything in your packet
which could be stored in a lookup table in your
script? I mean, rather than sending "Order
of the Butter Knife" in the packet, send
"1" where "1" is the index of
the appropriate entry in an array.
Also,
you can cram multiple bits of info into a single
string, like this:
A5Z2/butter/2.34/
Where
both the sender and receiver of the packet know
that the FIRST letter indicates what month the
player was born, the SECOND character is an index
into an array of six weaponIds indicating which
gun he has selected, the Z is his currently
selected radio channel, the 2 is how many spare
clips he has, the slashes help you find the
boundaries of variable length fields (lua has a
boatload of regular expression parsing stuff) and
the 2.34 is a floating point value that you
couldn't stick in one of the four num fields.
(Though you COULD have sent it in a num field as
"234" and just known to divide it by
100 after receiving it. Which is also a nice way
to control the precision of arguments)
|
Mandatory
TA_OnXXX Event Handlers Your Style Script MUST
define the following functions. They are how
TurnAbout tells your script that important things
have happened. Your script does NOT have the
luxury of running a continuous game loop. Your
script is event-driven and must return from each
event as rapidly as it can, or the game will feel
laggy to the player.
Go here.
|
Available
TA_xxxx Requests Your event handlers can then call
external functions provided by TurnAbout to carry
out various actions on your behalf. Feel free to
suggest additional functions if you feel there is
a compelling need.
Go here.
|
Turnabout API /
Tutorial This is a work in progress, but
the reference material will be kept here.
This can be
expected to change without warning, so stay
flexible!
|
Included Deck Styles These are mini-games
which come with TurnAbout.
TurnAbout
Blackjack
The classic game of 21
TurnAbout
Feud
A multiplayer ballistic battle
TurnAbout
WindChasers
A Multiplayer sailboat simulation
|
Toy Status:The game is somewhat
incomplete, but the API is robust enough now that
it is possible to craft multiplayer mini games.
(See examples above, of functional multiplayer
games written in TurnAbout)
To
Install Toys:
- Run Arcadia
- Select your
'channel'
- Push the
"Check for New Toys" button
- Follow
Instructions.
|
|
|