Re: Building a Combat System

1,236 views
Skip to first unread message

Griatch Art

unread,
Jul 14, 2012, 1:45:51 AM7/14/12
to eve...@googlegroups.com
Hi Joel, welcome to Evennia!

What you describe does indeed remind me of the stuff I've already coded for the "Battle of Evennia" demo (apart from the readme, it's all still only only devel machine, unfinished). I like the concept of turn-based myself, especially for a MUD where people are expected to have some variation in typing speeds (twitch-based combat always struck me as a weird match for a text-based game although I know it's common).

Before establishing your combat system, you must have your resolution and rpg mechanics ready. That is, you need to know on what format you store your attributes/skills, and have a module with an API that spits back the result of "rolls" - this will not only be used by combat but by all types of success-checks in your game. If players are gaining experience with sucess, this should ideally also be handled automatically by the rpg/resolution solver - but this depends on your game. This needs no database access - it can be a normal python set of functions (or an object with access methods if you are more into OO).

You need to conveniently be able to pass data between these various systems. I personally like to store my character's rpg elements in some sort of structure (like a dictionary). I then pass this dictionary around between systems rather than the character object itself ... this means that I can store temporary stuff in there without saving to the database in-between, but to be honest it's probably not any big advantage.

With the rpg/resolve system in place, you can start building your combat mechanic. I would think the basic unit should be a combat queue object. This holds references to all objects (mobs as well as characters) who are involved in the combat. It's important that these characters should also have a back-reference to this combat queue stored on them when first added! This is necessary in order for these objects to call queue methods using commands.  The queue should have methods for adding and removing players (don't add "flee" code etc here, just stick to the pure Python work of removing chars from the queue). Each queue should also have a method to be called when it timeouts (in your case a new round should just start, allowing everyone to enter new commands).

The combat queues (all over the entire game) are handled by some sort of combat handler. This object, which should be instanced when you start the server, holds all queues and has an API for adding/removing new ones (such as when starting/ending combat). If you want combats to survive a server reboot you need to figure out a way to store these in the database (using attributes). Just remember that database objects (such as characters) mustn't be hidden away inside an arbitrary, non-iterable object (such as combat queues) - easiest way to handle this is to set up a state-script to hold all the data, and use the at_server_shutdown() hooks to store everything safely (using, say dbrefs instead of full objects).
Easiest is to assume all combats have to be re-initiated at a reboot.

Apart from the optional save-ing to database, this has so far been normal Python modules without almost any Evennia-specific interfaces. This comes now. First is to make a "Ticker script" for your combat handler. This ticks every 30 seconds or whichever timout you want, triggering the relevant method in all subscribed (currently active) combat queues. The combat handler automatically adds/removes combat queues from the ticker's subscription list. 

Next we need a suitable command set. This contains all the commands you want a character to execute as part of their turn. These commands are what calls your rpg resolution mechanics for you (It's a good idea to stick to this standard since likely more "stand alone" commands will be using the same mechanics too - makes things consistent if you know where things are called). Commands like "flee" or similar thus first checks of flight succeeds, then calls their current combat queue to remove themselves from the queue.

The final entry point is a way to enter combat an start the whole thing off. This command (something like attack <target>) goes into the default cmdset. When called, this maybe first checks if the target can be attacked, then calls the handler with the involved characters. The handler then creates the queue, add the character to it and starts the timer. It also assigns each character the combat cmdset. Combat continues from there until an exit condition (flee/death etc) is met, at which point the handler cleans the queue and deletes the combat cmdset from all involved characters (this will automatically restore whichever cmdset they last used before combat).

Of course, this is just one way of doing it, but it's how I would go about it at least. I hope this was any help, good luck with the development!
.
Griatch


On Saturday, July 14, 2012 4:14:38 AM UTC+2, Joel Smith wrote:
Hi Everyone! (I should I just say, "Hi, Griatch"?)

A couple of my friends and I are interested in creating a small MUD using Evennia.
We are still in the planning stage now, and I will have a lot of work ahead of me, as I'm the only Python programmer on the team. (Which consists of about 3 people...)

I have read through most of the Evennia documentation, and have looked through some of the source code. (A massive thank you to everyone who helps with Evennia! You all have created beautifully written documentation and source code!)
I am trying to work out the best way to implement some sort of combat system, and I've come here to ask for some direction on this.

These are the features I want this combat system to have:
  • Turn based.
  • Easily extendable. New commands and features should be easy to implement without rewriting the system.
  • Characters in battle should have a limited "combat command set".
  • If a mob or character attacks someone in battle, the combat system should register them as also in combat, and bring them into the battle too.
  • Characters should have a limited amount of time to choose who to attack and how they will attack. If they run out of time their turn should be skipped.
  • Having multiple battles across the world, and even in the same location simultaneously should be possible.
In theory, a bare bones version of this without any cool features, just the underlying system, shouldn't be too hard to create. The coding might take a while though.

Do you have any idea's for how I would go about implementing this? Would I use a global ticker, or scripts that are destroyed as soon as the battle is over? Should their be one script per battle, or something else? Would the same script that handles characters, also handle mobs? Or would there be different scripts for each?

I'm not very confident in my skills with the Evennia system yet, and I don't want to start on something in the completely wrong direction and have to rewrite it later. 

Thank you for any input that you give.

P.S. I read through Griatch's notes on a combat system for Battle for Evennia. The upcoming tutorial describes how to create your own hack 'n' slash MUD. A queue holding the commands of the players in combat sounds very interesting, and would definitely allow for some advanced features if it was extended.


Joel Smith

unread,
Jul 14, 2012, 5:32:58 AM7/14/12
to eve...@googlegroups.com
Thank you, Griatch, for such a comprehensive and in depth answer!

You have given me a lot to think about!

This sounds like a very good system to me, everything's all nicely split up, and it would be very simple to extend with more complex functionality like more advanced attacks. The last two paragraphs in your answer are similar to what I was thinking.

I will attempt to code this system, but my inexperienced mind will probably come up against more problems while I'm doing that, so you can expect to see me back here soon.

Thank you again for helping me so much with that answer.
Message has been deleted

Raymond Luong

unread,
Mar 11, 2014, 3:15:54 AM3/11/14
to eve...@googlegroups.com
My friend and I were stuck on how to code a combat system.  Awesome thread.

I'd like to continue by asking how to actually go about creating a python module like this?  As Python and programming newbies, how hard is this? What prerequisite programming knowledge do we need to know first, for example what kind of syntax and or programming structures would we need to know and learn first before creating a combat handler module?  

We'd like to use this as a hands on project to get our feet wet with Python, but are a little in over our heads.

Thanks again!

Griatch Art

unread,
Mar 11, 2014, 4:19:32 AM3/11/14
to eve...@googlegroups.com
Hi there,

If you are not familiar with Python, programming the full thing described in this thread is likely a little too much to chew on as a first project. I would suggest carefully going through the Basic MUSH-like game tutorial from start to finish. Even if you are not interested in MUSHes per se, this make for a good starting point and also shows you the full process of creating custom Python modules and linking them into your game. There are a multitude of tutorials in the tutorial section to go through for further details.
.
Griatch

Raymond Luong

unread,
Mar 11, 2014, 11:29:43 AM3/11/14
to eve...@googlegroups.com
Thanks for pointing us in the right direction. Well definitely go through the tutorials.

Faye Yao

unread,
Jul 28, 2014, 10:07:58 PM7/28/14
to eve...@googlegroups.com
Hi Griatch,

Yay for necroing old threads. I was browsing the group trying to find more information about how to do a combat system, particularly a round-based system which ticks every 40 seconds (or some arbitrary amount) that allows players to input in moves. Once the round times out, it runs everyone's commands. According to your advice about a combat handler object, I'm a bit confused on what kind of "object" this would be. Would this be a typeclass Object (with an in-game presence) that actually handles holding all of the information with combat queues? 

I've currently settled for a global "script" called CombatHandler, and then a RoundHandler script which is subscribed to the global script which is locally based, so players aren't waiting an arbitrary number of seconds before they can engage in combat. All players in a battle have a combat queue object attached to them that does all the fun battle logic stuff. I did two scripts because, to my understanding, you can't add scripts to non-Typeclassed custom objects in Evennia. Am I on the right track? 

Thanks for your time, and I really do dig the Evennia code-base so far. 

Faye

Faye Yao

unread,
Jul 28, 2014, 10:45:26 PM7/28/14
to eve...@googlegroups.com
The combat queues (all over the entire game) are handled by some sort of combat handler. This object, which should be instanced when you start the server, holds all queues and has an API for adding/removing new ones (such as when starting/ending combat). If you want combats to survive a server reboot you need to figure out a way to store these in the database (using attributes). Just remember that database objects (such as characters) mustn't be hidden away inside an arbitrary, non-iterable object (such as combat queues) - easiest way to handle this is to set up a state-script to hold all the data, and use the at_server_shutdown() hooks to store everything safely (using, say dbrefs instead of full objects).

Hi Griatch!

I've been heeding your advice on how to go about designing a combat system on Evennia. My combat system is currently designed to be round-based, where each round lasts an arbitrary number of seconds in which players can enter in all of their commands. Once the round times out, all the commands are executed. But I was a bit confused when it came to your suggestion about a CombatHandler object. Is this another custom Python container that holds all the queues, or perhaps a global script that'd do that? I've been leaning towards using a global script as my Combat Handler, which would actually store local scripts (that a queue would subscribe to) to simulate local round-based combat. Since custom objects shouldn't be holding database objects, I figured a global script would be the best way to hold all of those scripts. Am I on the right track?

Thanks for your time, and all the work you've done on Evennia! I really do dig the system and how open-ended it is.

Faye

On Friday, July 13, 2012 10:45:51 PM UTC-7, Griatch Art wrote:
Message has been deleted

Jason Conner

unread,
Jul 29, 2014, 9:45:02 PM7/29/14
to eve...@googlegroups.com
Hah, that will teach me to show the quoted text and lose track of replies. Here's a direct reply to you, rather than a poster further up:

Hey there Faye,

I'm not Griatch, but I think you're looking for this:


It's essentially exactly what you're talking about. There's not really a need for local scripts though - the global script has a hook method that is configurable upon subscription to the ticker handler, so the global script is really quite flexible.

Faye Yao

unread,
Jul 30, 2014, 2:42:35 AM7/30/14
to eve...@googlegroups.com
Ack, I apparently double-posted not realizing that the posts are moderated. Woops. Thanks a lot Jason! I didn't know the TickerHandler could do that. That's very helpful. That saves me the worry of having too many local scripts at once! I was scratching my head trying to handle it.

Griatch Art

unread,
Jul 31, 2014, 6:15:21 AM7/31/14
to eve...@googlegroups.com
Hi folks,

I'm still away on vacations for a little longer but reading the questions of this thread I put together a quick tutorial that describes my thoughts on a turn-based combat system in the current code base. You can find it here: https://github.com/evennia/evennia/wiki/Turn-based-Combat-System

Whereas the TickerHandler is an easy way to add flexible timers, it is built around the concept of triggering a given hook on a character/object at a regular interval. When it comes to a combat system the combat handler should usually not be tied to a given character or in-game entity (the initiator of the combat may after all leave it or get killed while the others keep on fighting). So I use a dynamically created/destroyed global Script for this in the tutorial, it seems to be the most straight-forward solution for this particular problem. As usual in Evennia you could come up with at least a handful ways to go about it depending on your requirements.

I don't have time to actually test out my tutorial code at this point (it's written off the top of my head) so it would be appreciated if people tried it and corrected it in the wiki or at least reported what doesn't work in practice. At any rate it should give some practical ideas to support the concepts mentioned in this thread.
.
Griatch (back to holidays)


Michael Elford

unread,
Aug 22, 2014, 8:46:30 PM8/22/14
to eve...@googlegroups.com
Hi, I thought I'd add my system to this same topic as it is where I'm currently at.  And see if there's any feedback/suggestions...

attack <target> - initiates combat with a target, this target may already be in combat with someone else

There are then 3 parts to a combat for my system that I'm currently thinking through:
  • How Often do I attack?  (different based on character stats)
  • Do I hit? (dependant on player stats vs enemy stats & random)
  • How much damage do I do? (dependant on player stats vs enemy stats & random)
So my thought is when I initiate combat...
Create 2 tickers - 1 on the attacker and one on the defender (auto targeting the initial attacker) based on their attack speed - essentially the auto timeout in the tutorial
If the target is already in combat...
Create 1 ticker on the attacker with the defender as the target.

The ticker essentially calls the combat ruleset to determine if it hits/how much damage as well as outputs any results to the room/chars involved
A death brings an end to all combat targeting the killed combatant (the defender notifies all characters currently targetting it?)
EXP/Gold/Loot falls from the body as appropriate, creates a corpse, PC/NPC death occurs

((This is basic melee combat, there will be additional skills/magic/equipment etc. added later which modify all/any of the above))

Does this make sense in terms of the functionality of Evennia?  Any suggestions for easy/good ways to implement this?

Faye Yao

unread,
Aug 24, 2014, 12:29:10 AM8/24/14
to eve...@googlegroups.com
Hi Michael,

Employing the global ticker seems like a good way you could go about queuing up commands, especially when you determine attack speed. It seems like the way you've gone about it would be asynchronous, which the global ticker's good at doing, since everyone can subscribe to it with a set interval at different times (firing off attack every however many seconds). If you haven't already, take a look at this for some thoughts about how to go about doing it:


Specifically what Griatch says here:

You don't specify exactly what you really mean by a "Queue". I will assume you mean that you can enter a list of combat commands and those will execute automatically in order as they complete. There are a few different ways to do this.

  • A subscription script (to which all combatants in the same battle subscribes) could make queued actions happen simultaneously (each tick a command is triggered or progresses according to a fixed time measure, a given command would take a certain number of ticks to complete). 
  • Each command triggers a callback chain and sets a state while it is executing. This makes all commands in combat independent from each other. This is an advanced functionality described here.
  • In a turn-based combat system queuing requires no timer beyond a timeout for taking too long. For example - players enter their commands (such as parry, feint, attack etc) then these are played out simultaneously and results are shown. Next round they enter new commands and so on. This can be done completely on-demand.  
Reply all
Reply to author
Forward
0 new messages