Jump to content

How to Create Custom Flat Rides for RCT3


wolfpaw

Recommended Posts

Creating Flat Rides
Written by Belgabor

1. Introduction

Before we start:

Quote

Warning
This tutorial is not for beginners, it requires sound knowledge of modelling, creating CS, and animating. I will not explain that here! Also, you need to do a lot of experimentation yourself!

What you’ll need:

  • As personal assets you need computer savvyness, patience and fun experimentating. A high frustration tolerance also doesn’t hurt.
  • A modeller that is able to produce animation and knowledge how to do that or a lot of patience to animate by hand. For now I suggest Blender and this tutorial will focus on that.
  • A way to create splines. Basically the same reasoning as animations. I also recommend Blender for that
  • Recent beta versions of the importer and ovlmake.
  • Recommended, but not necessary, is a good XML editor. Try XML Copy Editor.

To finish the introduction, a brief overview of what you need to do:

  • Create the model
  • Animate it. You’ll need four separate animations. This includes creation of the bone skeleton and effect bones.
  • Create at least three splines
  • Assemble the above in the importer
  • Write an XML file for ovlmake and create the ride with it

2. Creating the Model

Create your model as you would for custom scenery. Things you need to keep in mind are:

  • Things that need to be animated need to be in separate meshes. That’s not a limitation of RCT3, but of the current tool chain. Unfortunately, there is no easy way to fix that, so you’ll have to live with it.
  • Rides usually have a higher polygon count, so if you have 1000-3000, you’re well within the limits of the rides RCT3 has. But if you go that high, you need to provide LODs, otherwise your ride will disappear quite fast.
  • Model your ride in rest (not running) state. For most things that’s somewhat obvious, but I’m personally tempted to model restraints in closed state, if you’re as well, resist that temptation! Restraints need to be in open/released state.
  • Also make sure to center your ride around the origin.

3. Animating the Model

As written in the introduction, you’ll need to create four animations.

  1. The rest animation. This is pretty trivial. As you know, bones need to be ‘animated’ to their positions, and this is the animation that does it as long as the ride does not run. This animation also seems to run a full cycle after the last peep got to it’s seat, so in most cases to speed things up, make this only one frame, that’s enough. There are also rides in RCT3 where this ‘really’ animates some parts of the ride, but in my hands that didn’t work. I’m not sure what needs to be done to make it work, maybe only parts not bearing peep effect points can be animated, I don’t know.
  2. The start up animation. This brings the ride up to run. It consists of everything before the part that will be repeated if the user selects multiple circuits. Things usually done here are closing the restraints, pulling things up and speeding up rotations (you can see that in action in exactly this order on my Jumpin’ Jack ride).
  3. The loop animation. The main animation. This part is repeated if the user selects multiple circuits.
  4. The stop animation. The logic inverse to the start up animation, i.e. slow down, return to the rest position and open the restraints.

3.1 A few general notes about effect bones

In animated CS (and flat rides are in fact pretty much only an advanced form of animated CS), effect points are identical to bones, so I will call them effect bones. It is usually not recommended to have meshes moved by effect bones. I’ve seen it work and not work, so better take no chances.

Quote

Important
In contrast to most of the effect points used in CS, for all I’ll mention here not only position but also orientation matters!

There’s four effect point types I want to mention here. Lights also work, but I won’t go into detail about them. All of the following use a running number symbolized by ‘XX’.

  • peepXX. Mandatory. These represent the peeps on the ride and the number (somewhat obviously) determines the ride’s capacity. I so far only used sitting positions, and they differ from benches. The point is somewhat more intuitive and needs to be approximately at the end of the peeps bottom (butt). I don’t know where they have to be for standing positions.
  • inspectionpointXX. Mandatory. The mechanic will walk there to inspect and repair the ride. So far I’ve only seen one (inspectionpoint01).
  • cameraXX. On-ride camera, peep category.
  • staticXX. On-ride camera, static category. Despite the name, these can be animated as well.

I’ll explain how to properly set up orientation in Blender below. If you do it in a other way, you’re on your own.

3.2 Animating in Blender

Blender offers a plethora of possibilities to animate. My script only supports 2/3 of one, but as you’ll see, that’s enough.

To easily support transferral of animated models (and splines) from Blender to the Importer/ovlmake I’ve designed a special model file format and provided an export script for this format. All further explanations regarding Blender require you to use this script and format. In the script directory you’ll find a readme explaining the script options and some general notes as well as an explanation of the second script I’ll mention later. Now would be a good time to read this readme. I’ll repeat the most important information, but read it nevertheless.

Quote

Important
The export script ignores all things whose names start with a ‘_’ (underscore). You can use this for helper objects and bones needed for the animation, taking pictures or recording a
movie of your ride in Blender to ensure they don’t accidentally get exported.

RCT3-compatible animation in Blender is done by using an armature and recording Loc (Translation) and Quat (Rotation) IPOs for poses in Pose Actions. Always make sure you record all channels of a certain IPO type (XYZ for Loc and QXYZ for Quat). Other types of animation (mostly object animation and Scale IPOs for poses) are not supported. The Blender entity that is exported as an animation is an Action. There is a way to use the NLA, more on that later.

Quote

 

Warning
The export script is not very error/crap tolerant (or rather not at all). If you get an error make sure you have no

  • Meshes that are not UV-mapped
  • Actions or anything with an empty name
  • Actions without keyframes

 

Quote

 

Note
Deleting actions in Blender is unfortunately very cumbersome, so I’ll explain it to you.

  • Open an action window
  • Select the action you want to delete
  • Deactivate the F-button next to the action name
  • Select a different action you do not want to delete.
  • Check the action drop list and make sure that there is a small circle left to the action you want to delete (just look, do not select it!). This circle means the action is not used anymore and therefore will not be saved.
  • Save the blend file
  • Reload the blend file

The reason for this is the way Blender manages data. Blender does not delete data when you delete it somewhere, it is just not used by anything (‘unlinked’). Blender discards unlinked things during saving, but only in the saved file. So to get rid of these things, you need to save and reload. Actions are special because of the NLA and some other things you may want to do, unlinked actions are somewhat normal. Therefore Blender by default sets the fake user property on them. This means they are always considered used/linked, even if no real using object exists. This property is manipulated with the F-button.

 

3.2.1 Creating the Armature

Create the armature as described in lots of Blender animation tutorials.

Quote

Important
After adding the armature, immediately make sure it’s origin is 0,0,0 and that rotation and scale is applied to object data. Never transform your armature object! This is the absolutely
most important thing to do! Otherwise you’ll run into a lot of problems later! Only ever use one Armature object in a blend file. More will usually lead to errors.

Now start to add bones. Make sure the rest pose (the one you see in edit mode) is also the rest pose of your ride! You can use bone constraints, but you need to keep some caveats in mind, see below.

3.2.2 Effect bones

Unfortunately there is no easy way to show a bone’s coordinate system in Blender (at least none I found). Therefore there is an alternate way to create effect bones in a more convenient and (imo) intuitive way. Three types of Blender objects will be exported as bones:

  1. Empties. An Empty is a catch-all effect bone marker. There is a special mode mostly for peep bones. Normally peeps would look in -X direction of the Empty. I found that a bit unintuitive, so if you set the Empties display size to less than 0.6 (I normally use 0.5), it will be exported as if rotated 180, so just point the X-Axis the way the peep should look. The same is necessary if you want to use the Empty as a camera, but there’s a better way.
  2. Cameras. A camera can be used like an Empty, but it is transformed in a way that if used for an on-ride camera, it will look in the correct direction (not quite trivial, as untransformed Cameras look down in Blender). You need to name the camera properly though, as the name is taken as is (e.g. ‘static01’). If you set the lens value of the camera to 15.5, you’ll have approximately the same view through the camera in Blender as in RCT3.
  3. Lamps. A lamp lamp will be exported as a light effect bone. There are basically three modes.
  • Full automatic mode. I will not discuss that here as it doesn’t work reliably in connection with armature based animation.
  • Half-automatic mode. The recommended way. You need to name your Lamp lightstartXX_ (note the underscore!). XX is the running number of your light effect bones. The exact light effect bone type is determined from the Lamp’s settings. The Ray Shadow button determines whether the light shines (activated) or not (not activated). The OnlyShadow button determines whether the bulb is shown (not activated) or not (activated). So combinations of these two select lightstart, nblightstart and simplelightstart. Ray Shadow off and OnlyShadow on would mean a non-shining light without a bulb and is not supported. The lamps distance value is taken as the radius (0.5 will give 50cm). Normally, the lights colour is taken from the Lamp's colour. If you want to have a recolourable light, activate the Sphere button. The Energy value will then determine which colour option will be taken (rounded to full value: <=1: first, 2: second, >=3 third).
Quote

Note
To keep those from interfering with lighting set up for rendering in Blender, put your Lamps for light effect bones on a separate layer and activate the Layer button for them.

  • Manual mode: just name your Lamps as you would in the importer. Refer to the script readme if Blenders name lengh limit kicks in, but make sure the Object name doesn’t end with an underscore in that case!

3.2.3 Digression: bone constraints

Bone constraints are tremendously useful in some cases, but you need to keep a few caveats in mind.

  • Never record keyframes on bones affected by bone constraints, Blender does not do that correctly. As soon as you use bone constraints, you need to bake your actions (See the script readme).
  • This also affects your rest state! You need to manually edit your bones into their constrained states in rest state. So if your pose is in rest state, no bones may move when you switch in and out of edit mode.
  • The TrackTo constraint is one of the most useful ones, but may need special attention to work flawlessly. For the helper bones you might need, remember to start their names with an underscore so they don’t get exported.
  1. Do not track objects whose position or orientation is influenced by constraints on other bones. If you need to do that, place a helper bone there and track that.
  2. Do not have two bones track each-other. Make one track the other and place a helper bone at the first ones position and have the other bone track that (if you wonder why you may want to do that, think pneumatic pistons).
  3. In general it’s less error-prone to generally track only bones.

3.2.4 Assigning Objects to Your Armature

The easiest and least error-prone way to assign your objects (be they meshes or other objects for effect bones) to your Armature is to use bone parenting. Put the Armature in pose mode, then first select the object you want to assign and Shift-select the bone you want to assign it to. Then press Ctrl-P and select Bone.

3.2.5 Animating

First I would suggest you to record all bones for your rest animation in rest state. I also suggest to switch Blender to 30 frames per second, that’s what RCT3 uses. You need to keep in mind that RCT3’s animations are time based and start from zero. Blender uses frames and starts at frame 1. So if you want to have a keyframe after 1 second in RCT3, you need to place it on frame 31. Then you have two options.

3.2.5.1 Make separate actions ‘by hand’

Record start up, loop and stop animations as separate actions. I’d suggest to always record LocRot frames. The start up action should start with a keyframe in rest state. The loop actions should start with the identical pose keyframe that the start up action ends with and return to that pose with the last keyframe. The stop action starts again with that pose and returns to the rest pose.

3.2.5.2 Use the NLA

Create a couple of micro-actions for move patterns of your ride (remember to start their names with an underscore) and assemble the full program in the NLA. For these micro-actions I suggest only recording what you really need (Loc/Rot/LocRot) so they mix flawlessly. Use frame markers to mark the first frame of the start up part (usually that will be frame 1), the loop part and the stop part. Give those markers names (e.g. ‘start’, ‘loop’ and ‘stop’). Then use my Bake NLA to Action script to create actions from these parts (don’t forget to activate Auto Split). The script can be found in the Animation menu of a script window.

3.2.6 Export

Export options are explained in the readme, make sure you understand them. If you use grouping, don’t forget to include your armature in all groups. Use one group per LOD. But before you actually export, you should create the required splines.

Quote

Important
Make sure your object/armature is in rest state when exporting!

4. Creating Splines

A flat ride requires at least three splines. RCT3 splines are cubic bézier curves, that means each spline node has two control points that affect the curve shape. They mark paths for peeps to follow. We need:

  1. A circular loop spline. This defines a round course around the perimeter of the ride. It is used by the peeps to travel from the entrance to the seat splines and back from the seat splines to the exit. The same holds true for the mechanic and the mechanic spline.
  2. One or more seat splines. Those are used by the peeps to travel from the loop spline to their effect bones and back.
  3. A mechanic spline. Used by the mechanic to travel from the loop spline to the inspectionpoint effect bone.

In general, peeps can only enter and leave splines at spline nodes! This has the following consequences:

  1. The loop spline should have spline nodes where entrances and exits can be placed. Imagine the RCT3 grid below your ride and place nodes near the center of outward facing square edges.
  2. The loop spline must have a spline node where a seat or the mechanic spline branches off. Additionally only one spline can branch off at the same spline node!
  3. If a seat spline leads to more than one peep effect bone, it needs a spline node near each. If a peep effect bone has no spline node next to it, the peep can beam to it but will not be able to get out.

Finally, do not move the control points too far away from their node. I recommend going no farther away than half the distance between the spline node and the next spline node in the direction of the control point along the curve. To be safe, I’d keep it around 1/3 the distance.

4.1 Splines in Blender

Use a Bézier Circle for the loop and Bézier Curves for the seat and mech splines.

5 The Importer

5.1 General notes

This section gives a basic rundown on things new in the importer for you to keep in mind if you want to experiment.

All changes affect only the creation of scenery ovls

The importer now saves scenery settings in a entirely new format, so old versions are not able to open them.

It can still open old (scn) files. It can also open ovlcompiler/ovlmake files and 3D model files. This is an ‘import’ in the sense other applications use the word. In the case of model files, it tries to make the best of the information provided. This mainly affects ms3d and my own modxml format. The latter is a special 3D model file format I created for the purpose of importing. So far there is only a Blender export script for it (provided in the attached archive).

You (in some cases obviously) cannot save back into these formats, only the new xml format is supported for that. Also most information is not automatically updated (by that I mean if you change the original file, it will not be updated in the scenery file if you reload it). Only the ‘classic’ thing, the 3D data of model files, is automatically updated.

Quote

Important
I repeat, animations, splines and bones are not automatically reloaded from thier source files if you change them there!

  • The format of the configuration file changed as well, so you need to reenter it. Currently I recommend putting this version in a new directory and not overwriting the stable version anyways.
  • Adding animation made changing the coordinate system support necessary (the Fix Orientation matrix).
  • Animations need to be fixed as well, but cannot easily support a full matrix for transformation, so I took this out of the model transformation matrix. Just choose the correct one in the respective dropdown list. The default is saved with the matrix, so press the button as in the old version (but don’t edit the matrix). The correct choice is usually Right-Handed, Z-Up (or Y-Up for MilkShape).
  • The ovl-name was split up into a choice box for the directory, an edit box for the name itself and a prefix edit box. The ’A’ button next to the name tries to automatically name it as was done in the old importer version.

The prefix box needs a bit of more explanation. I chose to prefix more internal things, as I’m not sure whether splines and animations may become troublesome if names collide. Therefore every name written into the ovls is prefixed. Normally you don’t need to care about that, but if you reference ovls either from the game itself or created by earlier versions of the importer, you need to put texture names into square brackets when assigning them in the mesh settings.

  • You might have noticed that you cannot really edit animation in the importer. That’s a decision I made to keep the work I need to do on the UI minimal. Any way that would have fulfilled my own quality standards would have taken a lot of work. Therefore animation has to come from other sources:

Model files. Supported are MS3D and MODXML. The first might not work fully correctly. People are testing this for me and I hope to get it working as good as possible.

ovlcompiler style xml files. Note that the syntax changed a bit. Refer to the readmes.

Other scenery xml files.

  • You can use the L button beside the animation list to load animations from these files. A right double click on the button will clear the list before opening the load window (in case you want to update changed animations). Of course the L button does the same for all other lists where it is present (the right double click is not yet implemented everywhere).
  • You can edit on animations the animation name, bone names and the coordinate system.
  • Basically the same holds true for splines.
  • Animations need to be assigned to a lod to affect the model. Animated scenery only uses one animation per lod, so the importer will warn you if you add more. Some things need more animations. These are for example toilets, rides, ride entrances and exits. The assignment can be done in the lod edit window or with the respective button next to the animation list (ˆ). A right double click on the C button next to the LOD list will remove all animation assignments from all LODs. A right double on the list will copy the animation assignment from the first LOD to the currently selected one (you need to select it first, the right double click does not do that).
  • All in all, animations and bone positions are very strange in RCT3. I currently recommend to always add translation and rotation keyframes if you want to animate a bone. Strictly speaking this is not necessary, but RCT3 can do some strange things if not both are given. Basically as soon as you animate a bone or one of it’s parents you need to animate the bone into place at the start, the bone position is not enough. If neither it nor one of it’s parents is animated, you do not need to do this. That may sound strange at first, but is relevant for effect bones, especially if they require a rotation (peeps, particles or fireworks for example).
  • Parent-child relationships and bone positions are read from ms3d and modxml files. If you need to update these, press the A button next to the bone list in the model window.
  • If you need to manually edit bone positions, you should be aware of the roles of pos1 and pos2.

- pos1 is relative to the parent bone.

- pos2 is absolute in model space.

So usually if a bone has no parent, edit pos1 and deactivate pos2. If it has a parent, edit pos2 and calculate pos1 from it (Q button, Calculate). This requires correct setup of the pos2 matrix of the parent bone.

5.2 Flat rides

I assume you’ve read the previous section, so I’ll just go through the things you need to know for flat rides.

Quote

Important
If you want to create a flat ride, you do not need to set up prefix, name and path. Those will be superseded later anyways.

  1. Open your modxml file as if it were a scenery file. This will give you a good starting point. If you used groups in Blender, an ever better one.
  2. Assign your textures as normal.
  3. Create LODs as normal.
Quote

Important
Do not assign the same animated model to more than one LOD! Copy it if necessary.

Assign your animations to your LODs. The first one is the rest animation, followed by the startup, loop and stop animations.

Save the scenery file. No need to create ovls.

5.3 Decimating keyframes

The process as I described it so far has an unfortunate consequence: lots of keyframes of which many are not really necessary to create the correct animation. Unfortunately it’s not easy to detect and remove them, especially for rotational keyframes. To counteract this, I added the possibility to try to remove them within certain constraints.

Quote

Important
Before you do that, don’t do it and test your ride to see how it looks ‘normally’.

In the importer, open an animation by double-clicking it (or selecting and pressing the E button). The Blender script already removes identical keyframes when they don’t have a purpose (i.e. it keeps only the first and the last of a constant range). By pressing the Decimate button you can do some advanced keyframe removal. It tries to remove keyframes that are obvious in linear interpolation. As there is always an inherent inexactness in computer floating numbers, you need to give a threshold value. The default in the window works well for translational keyframes, but will hardly remove rotational ones. So you need to go higher and experiment how high you can go before your animation suffers. For my jumpin’ jack ride I saw no change up to 0.0005, but I didn’t go higher so that might not be the end.

6 Ovlmake

Ovlmake has a special input format I called ‘raw xml’. It’s basically ovl files in xml form and therefore almost as user-unfriendly as it can get. Fortunately we’ll only need a small subset of available xml elements for flat rides and I’ll walk you through it with an example.

Calling ovlmake is very simple. I assume you already used ovlmake for scenery creation or at least read the readme files. So you just need to call ovlmake with the xml file you’ll create as an argument. There is also an install mode if you give the command line option ‘--install’. This will directly create the ovl files in your RCT3 directory.

Whenever I talk about an ‘ovl file’ in this chapter, I refer to an common/unique pair.

Ovlmake now verifies xml input files, a process called ‘validation’. This validation can spawn three types of messages:

  • errors: Showstoppers meaning the file cannot be processed or would surely lead to RCT3 crashes.
  • warnings: Things ovlmake is pretty sure are an error, but should compile fine and may be even wanted in some rare cases. If you follow theses instructions, a warning means you did something wrong.
  • notes: Things not easy to validate and that might be an error or not. If you follow these instructions including those things I marked as for validation only, a note usually also means you did something wrong.
Quote

Important
Validation can only catch most semantic errors and some logic errors. Neither does successful validation guarantee a working flat ride, nor do warnings or notes always mean it will not work (that’s why they are not errors ;-)). Still, never ignore them without good reason.

6.1 Complete Example

Here is the complete example for reference:

<?xml version="1.0" encoding="UTF-8"?>
<rawovl basedir="output" installdir="" xmlns="http://rct3.sourceforge.  -
net/rct3xml/raw">
<rawovl basedir="Style\Custom\Belgabor\jumpjack" prefix="belgabor-">
<rawovl file="JumpJack">
<import file="jump.xml" name="JumpJack" id="mainimport"/>
</rawovl>
<rawovl file="Style">
<tex name="JumpJackTexture" format="20">
<texture><data type="file">icon.png</data></texture>
</tex>
<gsi name="JumpJack" tex="JumpJackTexture" left="0" top="0"  -
right="79" bottom="63" />
<txt name="JumpJack" type="text">Jumpin’ Jack</txt>
<txt name="JumpJackDesc" type="text">Ride inspired by an early  -
walking robot model.</txt>
<anr name="JumpJack" nametxt="JumpJack" description="  -
JumpJackDesc" sid="JumpJack">
<attraction type="4" loopSpline="jumpSplineLoop" impref="  -
mainimport">
<attractionPath spline="jumpSplinePeep1" impref="mainimport  -
"/>
<attractionPath spline="jumpSplineMech01" impref="mainimport  -
"/>
</attraction>
<ride attractivity="50" entryFee="10" seating="10">
<rideOption type="12">
<parameter>60</parameter>
<parameter>20</parameter>
<parameter>10</parameter>
</rideOption>
<rideOption type="10">
<parameter>10</parameter>
</rideOption>
<rideOption type="0"/>
<rideOption type="8" suboptionValue="2">
<parameter>0.8</parameter>
<parameter>0.4</parameter>
<parameter>0.3</parameter>
<parameter>0.33</parameter>
</rideOption>
<rideOption type="0"/>
<rideOption type="8" suboptionValue="5">
<parameter>0.8</parameter>
<parameter>0</parameter>
<parameter>0.04</parameter>
<parameter>1</parameter>
</rideOption>
<rideOption type="8">
<parameter>5</parameter>
<parameter>5</parameter>
<parameter>5</parameter>
<parameter>1</parameter>
</rideOption>
<rideOption type="0"/>
<rideOption type="11">
<parameter>0</parameter>
</rideOption>
</ride>
</anr>
<sid name="JumpJack" nametxt="JumpJack" icon="JumpJack"
ovlpath="Style\Custom\Belgabor\jumpjack\JumpJack" svd="  -
JumpJack">
<type scenerytype="23" />
<position xsize="20" ysize="10" zsize="20" xsquares="5"  -
zsquares="5"/>
<colours choice1="21" choice2="11" choice3="6"/>
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>
</sid>
<symbol name="StyleIndex" target="common" type="int" data="0" />
</rawovl>
</rawovl>
</rawovl>

As you’ll probably guess, this is from my Jumpin’ Jack ride.

6.2 Line-by-line Explanation

Now I’ll explain line by line what they mean and what you need to change or can customize for your own ride. This assumes you know xml, so I won’t list closing tags. I will also give some optional elements that do not appear in the example. If not mentioned otherwise, their attributes are optional as well so you can give all or any. The values I give fol those attributes are usually their default values, i.e. the ones ovlmake uses if you omit them.

<rawovl basedir="output\" installdir="" xmlns="http://rct3.sourceforge  -
.net/rct3xml/raw">

A rawovl element defines an execution environment for ovlmake. That may sound strange to you, but it has two simple meanings:

  1. It defines (an) output directory(-ies) and/or
  2. Creates an ovl file

The basedir attribute defines the general output directory and is relative to the current execution environment’s path. The path starts out in the directory the xml file is in in normal mode and RCT3’s installation directory in install mode. The installdir attribute supercedes basedir in install mode. If this is not given, install mode uses the basedir attribute. So basically the line above tells ovlmake: In normal mode, put everything within in the ‘output’ subdirectory; In install mode, put everything in RCT3’s installation directory.

Quote

Note
To allow ovlmake to do a thorough validation, always end and never start paths (basedir, installdir) with a path separator (‘\’) if they are not empty. Strictly speaking it’s not necessary for a successful import, but helps you detect errors.

The xmlns attribute is standard XML and defines the namespace (you know XML, don’t you).

<rawovl basedir="Style\Custom\Belgabor\jumpjack\" prefix="belgabor  -
-">

Now we step into the ‘Style\Custom\Belgabor\jumpjack’ subdirectory. Of course you should replace ‘Belgabor’ with your own nick and ‘jumpjack’ with your flat’s name. The latter is equivalent to the theme name in the importer and should not contain any non-ascii characters.

The prefix attribute defines the prefix for everything in the opened execution environment. It is used on every internal name. Use your nick with an optional separator.

<rawovl file="JumpJack">

The file attribute tells ovlmake that everything in this rawovl tag should go into an ovl file, in this case named ‘JumpJack’. Of course you should choose something that fits your ride. This ovl file will contain the model, spline and animation data.

<import file="jump.xml" name="JumpJack" id="mainimport"/>

The import element reads data from files the importer can open as scenery files into an ovl. The file attribute gives the filename of the file to fetch data from. It needs to be relative to the location of XML file we are writing. You need to give the file name you saved your scenery data in the importer to here.

The execution environment of ovlmake supersedes the name, path and prefix inside the scenery file, that’s why we didn’t need to give them in the last chapter.

The name attribute gives the internal name for the svd structure ovlmake generates from the scenery file. An svd structure ties up all 3D model data that belongs together (models and LODs in the importer). You should give your ride name here.

Quote

Important
For all internal names, use only ASCII characters. In fact try to use only letters, numbers, underscores and dashes.

You can import more than one file into the same ovl, but names may not clash. I also recommend putting only one svd structure into one ovl. If you import files without model data (i.e. animation only modxml files), do not give a name attribute to prevent errors. But if you followed this tutorial exactly, you do not need to do this, the one line shown in the example is enough.

The id attribute is not necessary for a successful import but helps ovlmake detect errors in the XML validation stage (See the impref attribute below).

<rawovl file="Style">

We create the Style ovl.

<tex name="JumpJackTexture" format="20">

The tex element creates an icon texture. The name attribute gives the internal name, so use something of your own.

The format attribute determines which compression format ovlmake will use. Valid options are 18, 19 and 20. If your icon texture does not use transparency or uses binary transparency (fully transparent or not at all), use 18 as it uses less memory. If you have gradual transparency, try 19 and 20 to decide which looks better. 19 chooses colour over transparency, 20 vice versa (fyi, the importer always uses 19). For those who know about 3D texture compression formats, 18, 19 and 20 correspond to dxt1, dxt3 and dxt5 respectively.

<texture><data type="file">icon.png</data></texture>

This line gives the bitmap file of your icon texture. Keep everything as is, only replace ‘icon.png’ with the path to your icon texture bitmap, relative to the XML file we’re writing.

<gsi name="JumpJack" tex="JumpJackTexture" left="0" top="0"  -
right="79" bottom="63" />

The gsi element creates an icon reference. The name attribute gives the internal name of the icon reference, the tex attribute the internal name of the icon texture it uses.

The left, top, right and bottom attributes define the area of the icon on the texture. As you can see from these numbers, an icon for a flat ride has to be 80x64 pixels big.

<txt name="JumpJack" type="text">Jumpin’ Jack</txt>
<txt name="JumpJackDesc" type="text">Ride inspired by an early  -
walking robot model.</txt>

A txt element creates a text string. We need two, one for the name in the menu and one for the description. The name attribute (again) gives the internal name of the text string. Keep the type attribute as ‘text’ as given in the example. Put your text string inside the txt element.

<anr name="JumpJack" nametxt="JumpJack" description="  -
JumpJackDesc" sid="JumpJack">

The anr element assembles all data that makes for flat ride a flat ride and not a scenery object, so this is where things get interesting. The name attribute is the internal name of the anr structure. The nametxt and description attributes give the internal names of the names strings for your ride’s name in the menu and description respectively. The sid attribute gives the internal name of the accompanying sid structure. We will define that below.

<attraction type="4" loopSpline="jumpSplineLoop" impref="  -
mainimport">

The attraction element contains data common to attractions, i.e. objects peeps interact with. The type attribute determines the type of the attraction and with that the menu your ride appears in. For flat rides the following values are sensible:

Attraction Element Table.png

The loopSpline attribute gives the name of your loop spline as it appears in the importer (as you named it in Blender).

The impref attribute tells ovlmake during the validation step which import is supposed to contain the spline so it can check whether it is actually there.

<attractionPath spline="jumpSplinePeep1" impref="mainimport  -
"/>

The attractionPath elements with their spline attribute list the other splines for your ride. Give the peep splines first and the mech spline last.

Quote

Note
Optional, on the same nesting level as the attractionPath elements.

<attractionMisc baseUpkeep="4960" flags="0" maxHeight="16"  -
/>

The baseUpkeep attribute gives the basic ride upkeep. How it works exactly for flats I don’t know, but I suspect it’s per year and rounded up per month.

The flags attribute sets certain options. To get your value, add up the value of all options you want to set. Only a few are known, here is a list:

Base Upkeep Table.png

The maxHeight attribute gives the maximal Height over ground in height steps the ride can be build at.

<ride attractivity="50" entryFee="10" seating="10">

The ride element contains data common to objects peeps can ride.

The attractivity attribute determines how many peeps will enter the park when the ride is built by the user.

The entryFee attribute determines the preset for the entry fee.

The seating attribute determines the peeps’ positions/animation on-ride. I’ve only tested a couple of sitting positions, see above for placement of the peep effect bones. Here is a list of allowed values (note: the explanation is from debugging strings in the RCT3 exe, so in some cases I do not know what exactly it means):

Seating Attribute Table.jpg

Quote

Note
There are two optional attributes you may want to give (in fact the above are also optional, but those you likely want to give).

The minCircuits and maxCircuits attributes determine how many circuits the user can set. The defaults are ‘1’ and ‘-1’ respectively, the -1 telling RCT3 ho use its internal default, which is 6 for flat rides.

Then a couple of rideOption elements follow. These set a couple of further options. The type attribute determines what is set. Keep the option types in the order displayed in the example, it’s the most common order for normal flat rides in the game. Each option has zero to four parameters, depending on the type. I’ll try to explain the types in the order it makes sense to explain them, not in the order they appear.

<rideOption type="0"/>

The type 0 option has no parameters and acts as a separator. Flat rides theoretically shouldn’t need them, but most of the original ones have them so put them in.

<rideOption type="12">
<parameter>60</parameter>
<parameter>20</parameter>
<parameter>10</parameter>
</rideOption>

The type 12 option is not fully understood. As you can see, it requires three parameters. It somehow influences how much peeps are willing to pay for the ride.

<rideOption type="10">
<parameter>10</parameter>
</rideOption>

The type 10 option determines how fast the ride breaks. The higher the parameter, the faster the ride durability decreases. Set this high to have it break down fast if you want to test your mechanic spline/effect point setup. Setting this to 0 makes it not break down at all. If this works forever or similar to the no-repair cheat is currently unknown.

<rideOption type="8">
<parameter>5</parameter>
<parameter>5</parameter>
<parameter>5</parameter>
<parameter>1</parameter>
</rideOption>

The type 8 option sets excitement, intensity and nausea values for the ride. As you can see, the option can also take a subOption attribute, which in this case determines when those ratings apply. The option without subOption attribute sets the base values. Suboption 5 sets the maximal influence of surrounding scenery. Suboption 2 sets the values added per circuit.

The first three parameters correspond to excitement, intensity and nausea respectively. The fourth parameter is a factor applied to all of the above to get the final value added. What this is good for is open for speculation, but I guess it was added to be able to set the first three parameters to the maximal influence and used the factor to calculate that down per instance (e.g. for a circuit of 6 allowed, there are lots of other such settings applying to other input values, but those are only used on tracked rides).

<rideOption type="11">
<parameter>0</parameter>
</rideOption>

Completely unknown. I saw parameters of mostly 0 or 1, most have 0.

<sid name="JumpJack" nametxt="JumpJack" icon="JumpJack"
ovlpath="Style\Custom\Belgabor\jumpjack\JumpJack" svd="  -
JumpJack">

The sid element creates a sid structure necessary for all objects you can place in RCT. The name attribute gives the internal name of the structure (the one you used in the sid attribute of the anr element). The nametxt attribute is again the name text string’s internal name. The icon attribute gives the internal name of the respective icon reference.

The ovlpath attribute is the path of the ovl that contains your ride’s 3D object relative to RCT3’s installation directory. The svd attribute is the internal name of the svd structure in it (the one you set via the name attribute of the import element above). An error in one of these two causes the well-known "no :sid for :svd"-error.

Note
The ovlpath attribute is not easy to validate. Successful validation (and a meaningful note that something went wrong) depends on two things: You followed the instructions about using path separators in path attributes given above and you only use an installdir attribute in the root rawovl element and this installdir attribute is empty (as in this example).

If you do not get a note that something is wrong, it’s pretty sure correct. Usually you only get false positives.

<type scenerytype="23" />

Do not change. The value 23 means the sid structure describes a (flat) ride.

<position xsize="20" ysize="10" zsize="20" xsquares="5"  -
zsquares="5"/>

The sidposition element describes the size and (optionally) position of your object. I’ll only explain the size aspect here.

The xsize, ysize and zsize attributes give the size of your object in modeller units in the game coordinate system. That means coming from Blender, you should enter the size in y-direction, z-direction and x-direction respectively. Round up xsize and zsize to the next full RCT3 square, ysize to the next full number.

The xsquares and zsquares attributes give the size of the object in RCT3 grid squares. Enter xsize / 4 into xsquares and zsize / 4 into ysquares. If you don’t get round numbers, you did something wrong. These are extremely important as they define the size of the base of your ride.

Optionally you can add a supports attribute to get supports, but you need to activate collision detection to really get them (see below). Valid values are: adventure, crazygolf, crazygolfholes, girder, ornate, pathasphalt, pathcrazy, pathdirt, pathleafy, marble, pathtarmac, flatqueueset1, romanpillar, scaffolding, space, spooky, steel, ts1, ts2, ts3, ts4, ts5, flatwildwest, aquarium, aquariumcorner, defaultpool, defaultpoolpath, defaultpoolsmall, sand, pathunderwater, ts1a, ts6, ts7 and ts8. Usually this is ‘scaffolding’ for flat rides.

<colours choice1="21" choice2="11" choice3="6"/>

Optional, gives the default colours for recolourable textures and lights.

<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>

Optional. You can leave this out if you do not want collision detection for your ride. All attributes are optional. Personally I found rides hard to place as RCT3 only shows the base size shadow if collision detection is enabled. Actually you have three choices. Do not give this element, give it once (as in the example) or give it once for every square of your ride (yes, that would mean you’d have to give 25 in the example). The latter option sets collision detection for each individual square (see Section 6.3).

The flags attribute sets flags 33-64 known from the importer. The example shows what you minimally need to set for working collision detection. These are bitflags, so add up 2FlagNo - 33 for every flag. If you want supports, you need to activate Flag 34 as well, so you would need to set flags to 3 (1 + 2).

The minHeight and maxHeight attributes correspond to unknown 6 and 7 in the importer and define from where to where the square is considered ‘filled’. You need to use rounded modeller units. The supports attribute is unknown 9 in the importer and needs to be set to 1 along setting flags to 3 to have supports (of course you also need to name supports in the position element).

In case you miss unknown 8, it is (and needs to be) automatically calculated from minHeight and maxHeight.

<symbol name="StyleIndex" target="common" type="int" data="0" />

Determines the theme type of your ride. Leave everything as it is, only change the data attribute if necessary. This is rather more important for rides than for CS as it determines the default entry and exit of your ride. Here is a list of available options, I omitted those that don’t have a themed ride entry/exit:

Theme Table.png

6.3 Advanced topics

This section will explain how you can do some nifty things regarding collision detection and entries and exits. Figure 1 visualizes an example 3x3 ride and helps you to find out the correct settings for the result you want to achieve.

Advanced Topics.png

Figure 1: Picture visualizing the orientation of a 3x3 ride in game when not rotated by the player. Four squares have been marked to illustrate the index for squareUnknown elements and xPos/zPos for rideStationLimit elements.

The first thing we’ll need to discuss are flags. As you’ve learned above, Flags 33-64 from the importer are actually per square. The others, 1-32, are as presented in the importer per object. To set them, add a importerUnknowns element and set its flags attribute.

<importerUnknowns flags="0x00200000"/>

It needs to be at the same level as the squareUnknowns elements, so I suggest adding it just above the first one. Here I used the hexadecimal notation for the value which is an alternative to the one presented above (integer number). It sets Flag 22 (the integer representation would be ‘2097152’, which looks a lot less orderly, doesn’t it?) which activates exact ride bases (we’ll come to that next, basically everything I’ll say about squareUnknowns elements depends on this setting), if it’s not activated, the ride base will always encompass all squares. Another flag interesting for flat rides is Flag 17, which removes th9e base completely and makes it placeable on water only, like the whale and dolphin shows (to set, use ‘0x00010000’).

To make use of exact ride bases, we need to give one squareUnknowns element per square. To keep things manageable, I’ll switch to a 3x3 example as seen in the picture above

<squareUnknowns flags="0" minHeight="0" maxHeight="0" supports  -
="0"/> <!-- Index 0 -->
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>
<squareUnknowns flags="1" minHeight="0" maxHeight="0" supports  -
="0"/> <!-- Index 2 -->
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/> <!-- Index 3 -->
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>
<squareUnknowns flags="1" minHeight="1" maxHeight="7" supports  -
="0"/>
<squareUnknowns flags="1" minHeight="0" maxHeight="7" supports  -
="0"/>
<squareUnknowns flags="1" minHeight="1" maxHeight="1" supports  -
="0"/> <!-- Index 8 -->

As you can see the index mentioned in the picture is a zero-based list index, so the first element you give corresponds to the top square in the picture and the last one to the bottom one. Mostly I’ve set up the ride to be 7 units high with collision detection, but the corner squares have special settings.

The top corner (index 0) has collision detection switched off. It will not have a base and not highlight while placing.

The left corner (index 2) has collision detection, but both minHeight and maxHeight are 0, therefore it does not have a ‘blocked volume’. The part will have a base and highlight while placing. It will block paths on the ground, but not scenery.

The right corner (index 6, not indicated) also has collision detection and has a ‘blocked volume’, but it starts above ground. It will not have a base, but highlight during placing. It blocks both paths and (collision-detection enabled) scenery overlapping it’s volume.

The bottom corner (index 8) has collision detection, starts above ground and does not have a ‘blocked volume’. As the previous one, it will not have a base, but highlight during placing. It does not block scenery, but blocks paths if it ‘cuts’ their volume (usually 4h above the path).

To summarize, if a square

has collision detection enabled, it will highlight during placing.

has height 0 inside its min/max range, it will show a base.

has an actual volume blocked, it will block collision-detection enabled scenery.

will block paths, if the path’s volume (4h above it) overlaps minHeight and/or maxHeight.

rides act like scenery.

The second topic is limiting where the player can place the ride entrance and exit. You can see this in action for example on the sky swing, the dolphin show and the whale show. This feature was introduced in Soaked!, so first we need to tell ovlmake to write Soaked! stuctures. To do that, add a version attribute to the attraction element and set it to 2 (note that if you want to keep the version attribute and go back to vanilla structures, you need to set it to 0).

<attraction type="4" version="2" loopSpline="jumpSplineLoop"  -
impref="mainimport">

Now to add station limits to your ride, you need to add rideStationLimit elements to your ride element, on the same level as the rideOption elements. I suggest adding them after those.

Important
As soon as you add one or more rideStationLimit elements, RCT3 deactivates all default placement possibilities (i.e. everywhere around the square base of the ride).

So if you wanted to restore the default, you’d need to add:

<rideStationLimit xPos="0" zPos="0" flags="0x00000039"/>
<rideStationLimit xPos="1" zPos="0" flags="0x00000038"/>
<rideStationLimit xPos="2" zPos="0" flags="0x0000003C"/>
<rideStationLimit xPos="0" zPos="1" flags="0x00000031"/>
<rideStationLimit xPos="2" zPos="1" flags="0x00000034"/>
<rideStationLimit xPos="0" zPos="2" flags="0x00000033"/>
<rideStationLimit xPos="1" zPos="2" flags="0x00000032"/>
<rideStationLimit xPos="2" zPos="2" flags="0x00000036"/>

The xPos and zPos attributes select the square as illustrated in the picture above. The flags attribute selects what (entrance and/or exit) can be placed on which edge of the square. You need to add up the values for the edges (see Figure 1) and for the entrance (16 or 0x10 hex) and/or exit (32 or 0x20 hex) you want enabled.

The nifty thing is that you can also enable placement within the ride square if your collision detection settings allow it. If you allow an entrance/exit to be placed on the same square from different ride squares, there seems to be a bug that in some orientations RCT3 shows the red ‘cannot place’ shadow. Nevertheless if you actually try to do it, it works.

This guide was written by Belgabor. A PDF version of this guide is available to download below.

How to Create Flat Rides.pdf

there's this illusion of the reality, but it's not really really real, like it's beside and inside and inside and beside, but never on top.. nevermind, just kidding but not really

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...