Battle Bazaar Blog

Battle Bazaar.net Developer / Designer Blog

How we're handling mob "con's"

clock October 4, 2010 15:05 by author DaveB

OK -- so there's a problem with this no class system, and that is that you can't easily implement a consider system.  Traditionally in MUD, there was a consider command that you used on a mobile before fighting it to see if it would be too easy, too tough or just right.

The first thing is I've never seen a "con" system that actually works.  The one in EverQuest and EverQuest 2, for example, doesn't take into account archtype, class, subclass or AA trees -- all of which are factors in how difficult or easy a fight is going to be.  The one in WoW and the one in Warhammer are, similarly, broken.  It's difficult any time you allow player customization at all -- so we need another approach other than looking at a flat level.

First, we're going to keep three scores.  One is the number of wins or losses against easier oponents.  Each time you win against an easy con, we add a point (saying we were right to con it easy).  Each time you lose against an easy con, we subtract a point (saying we were wrong). 

The second number is the number of wins against a medium con.  Again, each win adds a point and each loss subtracts a point.

The third number is the number of wins against a hard con.  Again, each win adds a point -- and each loss subtracts a point.

This forms an index that tells us how to modify cons.  If you have a ton of points against hard con mobs, then we ramp down what we're conning the difficulty at.  If you have lost a ton of points against easy con mobs, then we ramp the con down.  If we're getting things perfect, we know it.  Ideally, medium would be at zero, easy would be positive and hard would be negative -- and so we adjust the cons to try to achieve that, so that things that con as "even" give you about a 50/50 chance.

But how do you determine the initial con?

The answer is we do things differently from everyone else.  Normally a stat -- lets call it strength -- feeds into a skill.  You determine the stat at character creation, and maybe there's a way to move it through game play (gear, etc.).  There's some magic stat that for your archtype you want to be maxed, and the other stats aren't so important.

So we flip the problem around -- the skills determine the stat, and the stat determines the archtype.  But that's not sufficient -- there's a world of difference between an item you get easily, and an item you have to work for.  Each item has skill requirements to equip.

Only skills that have the necessary gear are useable -- so you take the total of all those skills, and that's the "effective" stat.  You total the "effective" stats, and that gets you a general power level.  But again, that's not enough to get a good con.  Someone who had only support abilities would con the same as someone who could instantly kill anything, and that would be bad.  So the con has to be based on the rock, paper and scissors -- the strongest stat has to be compared to the stat it dominates, and the weakest stat needs to be compared to the stat it is dominated by.  That makes it so that if the mob is really good against, say, ranged then it will con a lot higher versus ranged players than versus melee players.

This, then, helps the LFG system.  A balanced group would have the stats equal, so that no one stat dominated.  So if you're looking for more, the system can try to balance out the stats.

The key is the consider system must consider more than just the character level, and the consider system must consider more than just the points.  Every single piece of equipment and the skill of the player sitting in the seat are factors, and if you're going to ramp the difficulty level to an appropriate level, you're going to have to take all of that into account.  Otherwise there is no way to achieve a balanced game.

I want to emphasize -- all this is in generalities because I don't have specifics yet.  But my personal opinion is that if you look at "Lord of the Rings" as an example -- each member of the Fellowship has their own way of doing things, but when they're fighting they are all equally successful.  It shouldn't be about one guy repeatedly mashing the taunt button, while the rest very carefully avoid grabbing the mobs off of him.  It should rarely be "just one guy," and boss fights should be relatively involved.

I have some more ideas -- some pulled from D&D -- that I'm going to share in a couple of days.  The basic thought is -- the basic buckets should be "ranged," "melee" and "support."  Stealth is applicable to all of those, but in different ways -- a melee stealth would be the backstabber, a ranged stealth a sniper, and a hacking stealth more of a spy.  But I'm pretty sure the three buckets should be ranged, melee and support -- the reason being I can see a clear way to set up the rock, scissors and paper there that is fair to all of them.  Obviously, certain choices will involve more strategy and thought, while other choices will be plow through -- but both choices should be equally effective.  And that's really the key.

Anyway, I'll talk about it some more in a couple of days. 



MessageBus Pattern

clock June 4, 2010 12:53 by author DaveB

I was looking at various implementations of the Message Bus pattern, but I was not liking what I was finding, and so I rolled my own. 

There are four significant differences between this implementation and the others that I've seen.

1.  Subscribing, Unsubscribing and Publishing are threadsafe.  This is important because I want publishers on multiple threads (specifically the ThreadPool, responding to XMPP events).

2.  There is a WeakSubscribe model that uses a WeakReference to help with Garbage Collection (the WeakSubscribe method cannot hold an object alive).

3.  Subscribing to a base class -- inclusive of object -- subscribes to all descendent classes as well.  If I subscribe to Message, I get events when anything derived from Message is raised as well.  If I subscribe to object, I get all events that are raised (because of how .NET operates).

4.  Subscribers are notified via BeginInvoke, on a ThreadPool thread.  This means that the delegate handling the event needs to be thread neutral.

Note:  Because Publish is thread safe, the subscribers have to be thread neutral anyway.  So the fact I'm using the pool is secondary.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Net.BattleBazaar.AppServices
{
    public class MessageBus
    {
        private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        private Dictionary<Type, List<object>> _subs = new Dictionary<Type,List<object>>();
        public void Subscribe<T>(MessageBusCallback<T> callback)
        {
            _lock.EnterWriteLock();
            try
            {
                List<object> Handlers;
                if (_subs.ContainsKey(typeof(T)))
                {
                    Handlers = _subs[typeof(T)];
                }
                else
                {
                    Handlers = new List<object>();
                    _subs.Add(typeof(T), Handlers);
                }
                Handlers.Add(callback);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
        public void WeakSubscribe<T>(MessageBusCallback<T> callback)
        {
            // no lock needed here.
            var x = new WeakMessageBusCallback<T>(callback);
            Subscribe<T>(x.Callback);
        }
        public void Unsubscribe<T>(MessageBusCallback<T> callback)
        {
            _lock.EnterUpgradeableReadLock();
            try
            {
                if (!_subs.ContainsKey(typeof(T)))
                    return;
                _lock.EnterWriteLock();
                try
                {
                    var Handlers = _subs[typeof(T)];
                    Handlers.Remove(callback);
                    if (Handlers.Count == 0)
                        _subs.Remove(typeof(T));
                }
                finally
                {
                    _lock.ExitWriteLock();
                }
            }
            finally
            {
                _lock.ExitUpgradeableReadLock();
            }
        }
        public void Publish<T>(T msg)
        {
            _lock.EnterReadLock();
            try
            {
                var Q = typeof(T);
                var done = false;
                while (!done)
                {
                    if (Q == typeof(object))
                        done = true;
                    if (_subs.ContainsKey(Q))
                    {
                        var Handlers = _subs[Q];
                        Handlers.ForEach(delegate(object h)
                        {
                            var i = h.GetType().GetMethod("BeginInvoke"); // , new Type[] { Q, typeof(WaitCallback), typeof(object) });
                            i.Invoke(h, new object[] { msg, null, null });
                        });
                    }
                    Q = Q.BaseType;
                }
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
    }
    public class WeakMessageBusCallback<T>
    {
        private WeakReference x;
        public WeakMessageBusCallback(MessageBusCallback<T> cb)
        {
            x = new WeakReference(cb, true);
        }
        public void Callback(T msg)
        {
            if (x.IsAlive)
            {
                ((MessageBusCallback<T>)x.Target)(msg);
            }
        }
    }
    public delegate void MessageBusCallback<T>(T msg);

}

So anyway -- that's my take on the problem.  You use this pattern when you have an event source (in my case a XMPP stream) that you want to be loosely coupled with some services (in my case, the MUC group chat object).



Move of Blog to Windows Azure

clock May 21, 2010 11:39 by author DaveB

As part of testing with Windows Azure, I will be moving the blog from Blog Engine to Azure sometime this week most likely.  I will be bringing the account management services up around the same time.



TextBox

Tag cloud

Calendar

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar

Sign in