Zum Hauptinhalt springen

Version 0.2

· 15 Minuten Lesezeit
Stefan Prelle
GraphicMUD creator

It has been six months since our last release and the engine changed a lot during this time. Not so much on the player experience side, but internally. I try to give an overview here.

hinweis

The tech demo is available at eden-test.rpgframework.de 4000 . All previously existing characters have been deleted - your old account though should still exist.

Entity Component System

While extending the features of the engine, you often need to add attributes to mobiles, items or players. E.g. if you want an item to be consumable, you need to track how many portions it (still) provides and eventually a flag indicating if it can be refilled. If you add a feature that you can put items in a container, "use" the item and the content gets converted into another item, you need recipes that you can add to items. While your engine grows, you accumulate a lot of data that needs to be attached to some items or mobiles, but it is rare that you have items/mobiles that use ALL of it. So a better idea is to encapsulate such data in smaller objects that can be attached to your MUD items/objects: Enter "components".

When you start dividing data into components, the differences between objects and "living" beings eventually become blurred. A mobile may have an inventory component, but so may a bag. This led us to combine different object types (mobiles, items, players, doors, rooms, etc.) into a superclass called “MUDEntity.” - and now we have an "Entity Component System" (ECS).

We do have not only one system, but several of them - one for each area. Entities do have an Archetype, which describes which minimal components they have. You can ask an ECS which entities of a specific archetype are currently loaded, but also which entities do have a specific component ("Give me all entities with a Roamer component.")

That change was a huge gain in flexibility. Want to jump into a mobile and control it? Just move the "Player Controlled" component from the player entity to the NPC entity. Want to have a magical door that isn't necessarily always in the same place? Just add it the Roamer component.

Entity component systems are a well known established concept from game development in the last 20+ years. I did not copy any game engine API, but wrote what I needed so far, so it may feel different if you come from popular game engines.

Behavior Trees

Having the mobiles in your world do stuff, can be achieved in multiple ways. You can attach scripts to mobiles or hardcode concepts like a patrol route in a component. Here, too, we drew on game development concepts and implemented a behavior tree.

Every pulse all entities that do have a BehaviorTreeComponent get called to execute their behavior. This can be pre-written behavior or short-lived tasks that need to progress further. E.g. the behavior tree of a guard may be Combat, Patrol, Comment and Rest. If the guard is in a fight, the combat behavior gets executed. If not in a fight, it might decide to follow a patrol route and walk. If not, it eventually just throw random comments at bypassers (if there are any). If none of that applies, it simply rests.

Behavior Trees can be used to achieve more sophisticated problem solving, but we are not there yet. Right now, our only implemented use case is performing actions that require multiple pulses, like walking into the next room.

Walking to entities to interact with

The version 0.1 had this partially, but not in an easy to use fashion: Walking within a room to the entity you want to interact with. For example if you wanted to examine a chest and typed examine chest you simply got the result, while your avatar remained at the same position, possibly some steps away from the chest.

With the new version, we changed this in a way that some interactions require walking first. Opening a door, getting something from the floor, using an object ... that all requires reaching the target first now.

warnung

This change feels weird on traditional MUD clients. When you are 4 steps away from your target, you will first make 4 steps before the actual command is executed. On clients that support a dedicated map area, the map content gets refreshed and you see your avatar moving. On clients that don't (e.g. Mudlet), you don't get any updates, since we do not want to send new room descriptions with maps every time something on the map changes. On that clients you just get a delay of 4 pulses, before you get the command output. When moving from room to room, which usually involves several steps, this leads to a sluggish feeling.

For a developer - after several iterations - the interface looks like this:

 // Take something out of a container
Predicate<MUDEntity> hasContent = e ->e.hasComponent(ContainerComponent.class);
actor.prepareAction()
.walkTo(ParameterType.CONTAINER, containerName, TargetMode.ROOM_ONLY, hasContent, (i) -> "You cannot get something from "+i)
.perform(new GetFromContainerAction())
.withEntity(ParameterType.TARGET, itemName, TargetMode.CONTAINER_CONTENT)
.execute(actor);

Where "GotFromContainerAction" is the actual code to execute - the rest ensures that you walk to the container entity before the action is executed and that CONTAINER and TARGET are variables that get handed to the command and resolved in a specific way before doing so.

Refactoring actions

When you closely look at stuff you can do in a MUD, you identify different kind of actions.

  1. Raw Actions Sending text to the player that acted, sending text to all players that can observe the acting player, moving an entity into a different room,... these are all atomic building blocks you need when you want to model a change. Raw Actions don't have any checks - they just expect to be called correctly.

  2. Cooked Actions

    A cooked action is something that consists of one or more raw actions. It is called parameterized and checks specific for this action - e.g. an action to pick up an item, will expect a valid target item and won't check if it really is in the room, but it will make sure the item does not have a NO_TAKE flag and that the inventory has enough room for the item. Cooked actions are finished within a pulse. The concept already existed in V0.1, but with more sanity checks and some actions eventually required more than one pulse.

  3. MUDActions MUDActions are a composite of cooked actions, where each pulse another cooked action is executed. Mostly used to make as many single "Step" cooked actions required to reach a target, where than the actual cooked action is executed. The action is interruptable, e.g. if you get attacked, you stop walking. A concept to be explored with a later release are pre-conditions and post-outcomes. The idea is that when the NPC (or player) wants to attack with a sword, it has to wield it first. If thats not given yet, an "EquipAction" may be required first, which in turn may require a "GetAction", which may require several "StepAction" to reach the sword, but once this works an "Attack enemy with sword" might automatically let the NPC walk to the weapon, pick it up, wield it and than walk to the enemy and hit it. And with that we reach ...

  4. TODO: Goals This is NPC AI territory. A NPC may have a goal to kill all intruders and pick the best moves to do that. At 8 o'clock PM, the shopkeepers goal may be "Sleep", so it makes several steps to its home room, lock doors and use a bed. Goals are ordered by priority. A goal for combat behavior may have a higher priority than the daily routine goals. We don't have that yet, but I have it in my mind to get there.

Instance areas

In v0.1 all areas/zones were shared among all players. In v0.2 support was added to have player instances of areas, where you get to be alone (or with a group) in the area and can experience everything without disturbances.

The main driver for this option was the following feature:

Procedural generated areas

We now have an architecture in place that allows us to have a procedurally generated area map and combine it with a handcrafted list of rooms. After generating an area map, the algorithm detects all possible areas for rooms, sorts them by descending size and associates the pre-written rooms.

ANSI

BeipMu

And while that sounds good, it is still in very early stages. The algorithm still generates divided maps where you cannot reach everything and detected room exits are sometimes very weird. (E.g. if two areas are more or less next to each other, but separated by a wall, it can happen that you leave room A to the north and enter room B from the north, just because the way between both rooms was U-shaped on the 2D map.) But at least it is a working foundation for improvements.

You can test this yourself, by descending from the cellar even deeper.

Transformer

You know the Horadrim cube from Diablo, right? A container in which you put items, activate it and all contents are replaced by another item. We added a "TransformComponent" to which you can configure "Recipes". If the component is activated by a "use" command, it walks through all recipes and for those that all ingredients exist in the container, the entities of the recipe are removed and a result item is placed.

Books

A "ReadableComponent" allows presenting a player a long text. Books may come with RPG data (like a language or skill requirement).

Of course there is a "read" command which accompanies this feature.

Minor stuff

  1. Color definitions are added to markup
  2. You can now add hyperlinks in markup
  3. Pulses and Ticks are configurable by config file

Plugin Changes

[!CAUTION]

Some of the following plugins are new. For more complex plugins this usually means that we provide the first how this could be implemented, to have something to experiment with and learn from it - and eventually come up with a better idea. Some of this plugins are therefore not fully implemented - e.g. the dialogue plugin may miss support for TUI or WebView frontends.

Consumables

The "Consumables" plugin was rewritten to the new action architecture. In addition to the existing Eat and Drink feature, we also added a LiquidContainer and Fountain component, as well as a "Fill" command, to have fillable entities you can drink from and an "Empty" command to ... well empty your bottles.

Combat

A new combat plugin provides a way to handle resources, like Health. Resources to get a color and can be consumed in three ways:

  • Long term damage (e.g. physical wounds)
  • Short term damage (e.g. exhaustion)
  • Blocked (e.g. your Mana pool may be blocked by sustained spells)

In a VT100 enabled client, you do get colored "health" bars for your resources.

Combat

The BeipMu client also has resource gauges

Resources in BeipMu

We also added some kind of combat handling architecture, but I think that API will change "soonish".

Dialogue

This plugin is new. It provides the possibility to talk to NPCs and have some kind of dialogue tree (in an XML format). A preliminary support for state changes (like Quest flags) is already implemented. Dialogues are started with a "Talk" command.

<?xml version="1.0" encoding="UTF-8"?>
<dialogue>
<greet>
<first>
<say>Good morning! Did you sleep well? I hope so, because there are some things I need you to take care of today.</say>
</first>
<later>
<say>Do you have any more questions?</say>
</later>
</greet>
<root topics="yes">
<choice option="Tasks for today" jump="cheese" />
<choice option="Things to take care of" jump="today">
<say>What would that be?</say>
</choice>
</root>
<nodes>
<node id="today">
<say>I will be visiting the town today. And before you ask: No, you cannot accompany me this time - I will need to meet someone and I need to do that alone.</say>
<say>But I promise I'll be back before night. We need to talk about your future tomorrow.</say>
<emote>looks slightly worried, but quickly regains his composure.</emote>
<say>In the meantime you could make yourself useful here and tend to the henhouse to get us some fresh eggs. Make sure there are two eggs left for the hens to hatch. Since the fox has killed a few hens, we need a few chicks again. If only we could slay that damn fox - maybe you could search for him in the forest?</say>
<say>I also promised Griselda some honey. So spin a honeycomb and pour the honey into a container. Griselda will give you some money for it. If there is honey left, you can store it in the cellar.</say>
<say>And of course your studies ... I leave my room unlocked.</say>
</node>
<node id="cthulhu">
<say>Ah, I see - you know the parole!</say>
</node>
</nodes>
<outro>
<emote>waves you good bye.</emote>
</outro>
</dialogue>

This is work in progress. Works needs to be done to have different dialogue experiences in VT100 enabled clients, clients with HTML/JavaScript pages and plain old MUD clients.

The current implementation for Terminal UI clients looks like this:

Of course, clients that don't support VT100 cursor control will get a simpler version. Same for NPC images: For non-graphical clients like Mudlet, we use chafa to convert images to colored UTF8 characters.

Farming

This plugin is new. It allows some items to be farming nodes with an associated probability list for items you can farm. So far we just provide harvesting bushes, mining ore and fishing.

This is also work an progress, as it is expected to have closer interaction with RPG rules in the future. For now it is working without any skill checks.

Intermud

This plugin is new. There are several "Intermud" networks out there that allow to communicate with admins and players from other MUD servers. We try to implement all protocols that existed.

So far we have very basic support for

  • AberChat
  • IMC2
  • Intermud 2
  • Intermud 3
  • Merentha Intermud Services
  • MUDVault Mesh
  • Zebedee (also dubbed "Intermud 2", but different)

At the moment we just support querying Who-lists. For communication we need to settle on an architecture for a communication channel plugin.

Trainer

This plugin is new ... so new, it does not even really exist yet. It provides a TrainerComponent you can attach to entities, which allows you learn skills or whatever. The "whatever" part is ultimately up to the RPG to handle - the component just provides a list of strings to handle by the RPG.

A "train" command in a room with an entity with a TrainerComponent will either list all things you can train as a table or try to learn a specific option.

Quests

This plugin is new. You can now write quest files in XML that define several stages of a quest. You can also attach the quest to events in the MUD.

Example: When you examine the wardrobe in your starting room, the quest is started. Talking to the mentor NPC ("DIALOGUE" event) and triggering the "today" dialogue node, finishes stage 2, and unlocks all options of stage 3. (Yes, the progression from stage 1 to stage 2 is missing)

<?xml version="1.0" encoding="UTF-8"?>
<quests>
<quest id="quest1" type="QUEST" name="All beginnings ..." rejectable="n">
<register>
<!-- When the statue entity is examined, the quest is started -->
<onEvent entType="ITEM" entity="1/1/wardrobe" type="EXAMINE" action="start"/>
<!-- After having the "today" node id of the dialogue, phase-1 is considered done -->
<onEvent entType="MOBILE" entity="1/1/mentor" type="DIALOGUE" subid="today" action="finish:2,unlock:3"/>
</register>
<stages>
<stage id="1">
<title>Get dressed</title>
<desc>Put on some cloth. You surely have something in your wardrobe.</desc>
</stage>
<stage id="2" requires="1">
<title>Talk to your mentor.</title>
<desc>Talk to your mentor about what you will do today.</desc>
</stage>
<stage id="3" next="7">
<title>Do the chores</title>
<desc>
Not fair! Your mentor leaves for the town and you have to
stay here and do the chores. And while this is annoying, you
wonder what is troubling him, that he won't let you come along.
</desc>
<substages>
<stage id="4">
<title>Kill the fox in the forest</title>
<desc>The fox has killed several hens. Find and kill the fox in the forest.</desc>
</stage>
<stage id="5">
<title>Produce a jar of honey and sell it</title>
<desc>Spin a honeycomb and pour the honey into a jar. Sell the jar to Griselda.</desc>
</stage>
<stage id="6">
<title>Keep learning</title>
<desc>There are books in the study and there also is a trainings dummy in the garden.</desc>
</stage>
</substages>
</stage>
<stage id="7" requires="4,5,6">
<title>Time for yourself</title>
<desc>
Your mentor will likely be back from the town in some hours earliest,
so you have some time for yourself.
</desc>
</stage>
</stages>
</quest>
</quests>

Steam Arcana changes

"Steam Arcana" is our test server to test the engine features in a real MUD. Ignoring one exception, it is a single area that does not aim to provide a special playing experience, but is meant to be a demo site. Of course we added examples for all the engine changes in the start zone.

We changed attributes and added skills and implemented a system to make dice tests. We also added very minimal combat rules and some kind of auto-combat to test it - it even grants experience.

But: We are still experimenting with what sticks and what doesn't, so don't judge based on this. For now it is more for engine test and no game design we are proud of.