News

Simple rules combined to complex gameplay

Posted by admin on June 17, 2020

A while ago I was asked about an example of how the Worldforge rules work. The focus was especially on how a series of simple rules allows for more complex game mechanics. Since I wrote down an example of how consumption works I thought I might as well post it here, since it provides a good example of how we think about game rules.

One example could be the "bread" entity type.
Breads can be eaten, so the bread class includes a "usage" of "consume".

<map name="usages">
<map name="default">
<map name="consume">
<string name="name">Eat</string>
<string name="handler">world.objects.Consumable.consume</string>
<string name="constraint">actor can_reach tool</string>
</map>
</map>
</map>

There's a simple constraint on the usage that the actor which performs it must be able to reach the bread. The script for the "consume" action is then handled by "world.objects.Consumable.consume", found here.

The script will check if there's a conversion property set ("_consume_mass_conversion") as well as a property specifying the kind of consumption ("consumable_type"). For the bread the consumption type is "plant". The result is that the bread is destroyed, and a "Nourish" message is sent to the actor (the one which consumed the bread). The Nourish message (or "operation" as we call them) will contain information both of the consumption type (plant) as well as how much nourishment it contains (depending on the size of the entity being consumed as well as any conversion property.
The Nourish op then needs to be handled by the entity that ate the bread. Consider if a human ate the bread. The "human" class will inherit from the "creature" class, which is defined here.
The "__scripts" property contains scripts that define the behaviour and can install "operation handlers". One of the scripts of "creature" is "world.traits.Nourishable.Nourishable", which is defined here.

This script will perform a couple of checks and apply modifications, and then increase the "nutrients" property of the entity. The "nutrients" property specifies how much nutrients an entity contains. For a creature it would be how much food it has eaten. Another script, "world.traits.Metabolizing.Metabolizing" then acts on this, and allows the entity to grow or heal if there's enough nutrients.
By default all "creatures" are omnivores, i.e. they will convert both "plant" and "meat" nourishment to nutrients. But take the wolf for example, as found here
It has this property, which makes it not receive any nutrients from "plant" nourishments, thus making it a carnivore:
<map name="_modifier_consume_type_plant">
  <float name="default">0</float>
</map>
Conversely a "cow", as found here, is an herbivore as specified by
<map name="_modifier_consume_type_meat">
  <float name="default">0</float>
</map>

But even plants can be nourished. So plants, also use the same world.traits.Nourishable.Nourishable script. However, they obtain nourishment by consuming from the entity into which they are planted (if the are planted). Therefore they use a script at world.traits.PlantFeeding.PlantFeeding which will at a regular interval send a "Consume" operation to their parent entity, for the "soil" type of consumption.

A typical entity which then would be able to nourish a plant would be the "land". Other entities would just ignore the Consume op sent from the plant. The "land" contains the "world.traits.PlantNourishing.PlantNourishing" trait/script which allows it to send back Nourish operation to any plant.
So in this way a couple of shared scripts can be reused in many different entities, for nourishment both for entities that eat things (creatures) as well as those that suck nourishment from the ground.
It doesn't stop there though. The "fire" entity, uses the Fire.py script to at regular interval consume other entities by sending Consume operation of the "fire" type.

This kind of Consume operation will be ignored by entities, unless they implement the "Flammable.py" trait. So a rock wouldn't be consumed by fire, whereas a log would be, as the latter has the Flammable trait. The speed of fire consumption is further governed by any "_burn_speed" property.
And so on. All game play if defined by having these smaller scripts which interact with properties and send messages/operations to other entities.

0 Comments Read full post »

Movement constraints

Posted by admin on June 14, 2020

Worldforge _finally_ got "movement constraints" implemented.
This feature basically allows world creators to declare rules for which entities the players are allowed to move.
Previously there were no constraints, so any random player could pick up any random other entity (such as a house, or another player).
As with so many other things the main reason why such a seemingly basic feature took such a long time to implement is that we want to make the rules generic and dynamic enough to allow for many different kinds of games.
We now provide multiple interception points where the world rules can make decisions on what kind of movements are allowed.
A movement operation typically involves four entities: the mover, the entity being moved, its current location and its new location. The new properties "mover_constraint", "move_constraint", "contain_constraint" and "destination_constraint" allows world authors to declare Entity Filters which determine what entities are allowed to move, for each step in the movement chain.
The default ruleset uses multiple definitions.
The "land" entity has a rule which restricts movement of child entities that are both "planted" and are marked as "rooted" (which basically translates to "all plants that are planted") in land.xml
<map name="contain_constraint">
    <string name="default">describe("Entity is planted and can't be moved.", not (entity.mode = "planted" and entity.rooted = 1))</string>
</map>
All creatures have a restriction which stops them from moving too heavy objects in creature.xml
<map name="mover_constraint">
    <string name="default">entity.mass = none or describe('Entity is too heavy.', entity.mass = none or entity.mass &lt; actor._mover_mass_max)</string>
</map>
And mobile creatures (i.e. most larger ones) have a constraint preventing others from moving them in mobile.xml
<map name="move_constraint">
<string name="default">describe("Creature can't be moved.", false)</string>
</map>
If even more complex rules are required there's always the possibility to add Python scripting code which directly intercepts the Move operation, at any stage of the move chain. This is done for the "effects" for example, to make them delete themselves when their parent is deleted.
See Immobile.py for example. 
 
These rules serve as an example; it's easy for any world author to declare rules more fitting to their specific world.
 
Together with the newly added "container" functionality the engine is now in a much nicer state.
 
0 Comments Read full post »

Installable packages for Windows and Linux

Posted by admin on December 30, 2019

We've extended our infrastructure for building installable packages and now provide a wider variety.

Since before we have used the Open Build Service to automatically build packages for a wide selection of Linux distros. This setup can be viewed here.

This setup has now been expanded with both TravisCI, AppVeyor and Snapcraft.

On TravisCI we have a setup where we build packages for both Linux and OSX, using both GCC and Clang. While we do build for OSX we don't yet provide any installable packages. The result is a both an AppImage and a Snap package of the Ember client.

On AppVeyor we have a setup where we build Ember for Windows using MSVC. This results in an installable package.

On Snapcraft we have a setup where we build the Cyphesis server.

The end result of this is that we now automatically build our code for Linux, Windows and OSX, on both x86, amd64 and aarch64. And we use GCC, Clang and MSVC as compilers. This makes sure that we catch most code incompatibilities rather quick.

In order to make this happened we've embraced the Conan package manager. All packages that we depend on are provided as Conan packages. The process of building Ember is now as simple as just doing "conan install". The sources contain more information on how to use it.

The new binary packages can be downloaded from

1 Comment Read full post »

Adding fishing gameplay

Posted by admin on September 23, 2019

Recently I've performed a major refactoring of how entity interaction happens in the Worldforge system. The basis of it is the new "usages" property which declares the actions that are available for each entity. As an example of how this can be used I've implemented a simple "fishing" task. When fishing you need a "fishing rod", and you need worms (or "annelids" as they are called in the system). You get these by digging in the ground with a shovel and then sifting through the resulting piles of earth with a sieve.

The "fishing rod" type is defined here. It has a "Fish" usage, which has a constraint of "get_entity(actor.attached_hand_primary) = tool and contains(actor.contains, child instance_of types.annelid)". The constraint checks that the rod when activated is wielded by the user, and that there's at least one worm in the user's inventory.
It declares on parameter, a "target" which is of type "entity_location" (i.e. a position on another entity) with a constraint of "entity instance_of types.ocean && actor can_reach entity_location with tool". The target constraints requires that the target is of type "ocean" and that the user can reach the position with the fishing pole (the fishing rod has a reach of 5 meters, as defined in the "reach" property).

By declaring these rules using constraints we can remove a lot of boilerplate code from the actual rule, since the simulation engine will take care of checking these constraints before the rule code is invoked. It's also now possible for clients to check the constraints themselves (and disable the usage if the constraints doesn't match).

The usage defers the rule handling to the handler "world.objects.tools.FishingRod.fish". This is a reference to a Python script, to be found here.
The usage handler will immediately create a Fishing task instance, with a "tick interval" of two seconds. The task inherits from StoppableTask, which just means that it will by default have a "stop" usage attached to it. It will otherwise run indefinitely.
Every two seconds the “tick” method is called. It will do a random check to see if there’s a fish on the line. If so, an Imaginary op about this is sent to the client.
If the client stops the task while there’s a fish on the hook a new Fish entity will be spawned, and the worm entity will be consumed.

The gameplay is fairly simple, but it serves as a good example of having the Entity Filter, the Usages and the Task systems all working together to provide gameplay. Since all of the rules are handled by either Python scripts or Type properties the feature can be altered and iterated on in real time without having to restart the server.

0 Comments Read full post »

Autoreload of rules and scripts in Cyphesis

Posted by admin on May 21, 2018

I've done some work in Cyphesis to allow for much more powerful features for live editing of the world, mainly editing rules and scripts.

Cyphesis have since long before had support for live editing of rules. It has been possible to send Atlas operations from clients for editing or installing new rules.
However, this is not really the workflow you would want, since any rule change would be detached from the source code.
Instead you want a workflow where rules are stored in the source tree, using Git, and then updated in the running server when they are installed into the server installation location.
 
To achieve this I've worked on these areas:
1) Rules are no longer stored in the database (which used to be PostgreSQL, but now is SQLite). Only accounts and entities are stored in the database. The rules are instead stored on disk, as Atlas files (xml).
2) The Cyphesis server process will observe all file changes in the rules directory, and automatically reload any changed files.
3) The server will also observe all file changes for the Python scripts directory, and reload any Python scripts that changes.
 
This setup works nicely with CMake, since it will by default only install files that have changed.
The workflow is now instead
1) Make edits in the Cyphesis source directory for the rule or script you want to change.
2) Execute "make install" which will CMake copy only the changed files into the Cyphesis installation location.
3) Have Cyphesis automatically pick up on the change and reload or add the changes.
 
Having tried this workflow I can say that it's a huge improvement compared to the previous one. It's now much more easier add or alter entity types and alter their behaviour.
11 Comments Read full post »