Refactoring ideas

So, i’ve realised there’s no sense in releasing a prototype which contains no game at all. And it turned out there’s a bit more underlying work ahead, and suddenly i’ve got buried under the ideas which aren’t quite fit together as they were.

Four days i’ve been writing and drawing diagrams, trying to fit every piece together, with some success. If you’d want, you could have a look at what’s my design documentation looks like. (poor man’s english ahead)


Abilities are scripted actions tied to the battlescape object. Abilities can change bso data and must have access to the field.

Ability must inherit IGSScript, contain a name, connected bso and a variety of service methods.

– result RunAbility(string name) method, which redirect ability name calls to the variety of scripted methods.

– int CheckAbility(string name) returns ability score. It’s an abstract number (relative to 0 or 1 or 50, i’m not sure yet), which indicates chances of performing successfully. Mostly it’s intended to serve as medium betwen the bso state and actor perks

(for example, weapon can be in a bad shape (score 30 of 100), but actor know this weapon well (score +10) has a perk which allows using of weapons in such a bad condition (check bound -15), which gives us 40 versus 35. We can use it.)

– some way of obtaining the input data, for example, calling the GUI scripts. It cannot be built in the RunAbility as the GUI response is asynchronous (humans must think) and AI doesn’t use the graphic interface at all.

– FailAbility(), or what to do in case of failing. Also should be some way to perform checks on ability to stand, for example. It must be stored somewhere as active, and in case of bso destruction it must fail correctly. But complete leg destruction will not allow us to use its scripts. The body must use legs integrity and actor stats to check ability of standing straight. How we should organise this?

Abilities also have a timer to prevent spamming during a time warp.

Abilities can be used directly as well as from the other abilities, but player will mostly interact with them through the orders. Input arguments are possible in an object params[] form.

When called, abilities are placed into the bso.Mechanics active task field. Ability always return one of three results: finished, failed, running. First two will free the active task slot, last one will indicate that the ability will be launched again on the next update. This allows using continuous abilities with a delayed task, such as movement.

Movement ability must move the object in a small fractions of a distance between tiles as it either can be tied to a single engine frame (and we can’t jump from tile to tile every engine frame, as it would be 60 tiles per second) or can run in a separate thread, but then we will have no simple way of controlling it, as it wouldn’t be running inside the game time. Running mini-gameloop for each script seems to be a waste.

Therefore system disallows using of the thread synchronisation inside the ability body as the abilities are run inside of the main thread. This isn’t perfect from the performance perspective, so the time consuming scripts should be avoided or spreaded over several update calls via returning “running”. Whether the script will throw an exception, internal bso mechanics will handle that and we will consider the ability as failed (with thorough logging of the event, of course).

Actor obtains the list of abilities through the bso construction and attachment points which allow ability transfer. It’s used in the order creation.

 Distributed ability structurization. Responsibility distribution.


Orders are classes between ability and AI/player. Orders run from the actor’s perspective.

Order scripts can be used to call various abilities depending on timing and input.

Order classes are scripted and stored in the battlescape/library special dictionary. From there they can be instantiated and assigned to actors.

Orders are stored in the actor order queue.

If the game is in the classic or move mode, last order in the stack is executed every tick.

Every update last order of the queue is checked. If its state is ready, order is started. Its state changed to the abilityCalled and order calls the ability (puts it into the bso active task).

If the state is abilityCalled , order checks the bso active task for the completion/failure. If it’s finished, order could be considered executed and removed from the queue or it could be continued. In this period actor can do some meantime tasks, for example, update its sensors.

If the state is continued, as previously said, the order can call its next ability.

Then it either proceeds to the other code parts while putting (complex orders) or

Orders in the actor’s stack are performed until cancelled or successfully executed, after that next order is calculated.

Orders are depending on data available in it, so as to obtain a kind of autonomy. For shooting, it may be target object or the absolute position.

For movement, it may be smart order (move to the closest cover) or absolute (Move two tiles up).

Simplest order just calls the ability and then reports as executed if the ability was successful.


Shooting is ability of some objects. It’s controlled by the custom GUI scripts. They allow selection of the shooting mode and target (close distance/scope allows selection of the exact parts of the body). Scripts provide the data for the order.

After providing the data, order requests the aiming vector from the actor sensors. After that order calls the provided ability (fire). The weapon checks the ammunition and generate projectiles at the end of the barrel directed to the target and pre-accelerated. In case of failure (actor can’t see the target anymore, or there is no ammo) order is cancelled.

Multiple shooting order: Order of three shots is created. It’s ready and last in the queue, so in a next update it’s set to started. Fire ability is set as the active BSO task. Ability using time is 300ms. dt is normal, so in a few updates timer is spent and ability is called. Every such update actor order checks active task and sees it’s not finished yet. Ability creates a bullet, which warps time by making gdt smaller (or the game automatically detects fast-moving objects and calculates gdt). Ability set active task as done, and the actor is ready to perform next step in the order. Order calls fire again, but since the gdt is now way smaller (time runs much more slowly) the ability isn’t going to be called until much updates later, when the first bullet will fly farther away or even hit (thus increasing the gdt).

*dt= delta time. Amount of real time (hopefully) passed from the last update. Renders the speed of in-game things independent from the machine performance.

*gdt = game delta time, modified by the time warp coefficient. Useful in tracing bullets and waiting days in the geoscape mode.


Movement is the special ability, called by GUI.

Left button sets a point. A* finds a path. If the object is flying, the modified A* is used. Stairways are treated accordingly. Second click puts an order into the actor’s order queue.

Order created, then started. It caclulates the TU cost for the next tile in route, rotate object if needed, then calls move ability with a timer and sends a gdt as a parameter, as the movement speed is dependent on time. Probably some kind of multiplication coefficient too, to decrease waiting times during the AI turn.

Move ability works as described in abilities.

If the next tile isn’t accessible, order is reassigned/cancelled.

Ability sets state as “Walking”. Viewport sprites library respond in using animated sprites.

Multitiled units use special versions of the A*

And the list goes on. For almost every planned aspect of the game, even if i could immediately mentally trace the rough way of doing it, i still try to write it down. Sometimes a missed detail stands to be impossible to achieve without overhauling half of the engine because your planning wasn’t good enough. And it’s never good enough.

TLDR: Write your brilliant ideas down and don’t forget the details, for god’s sake.