views:

85

answers:

2
public static class Global
{
    public static List <Faction> Factions;
    public static Dictionary <Faction, FactionRelation> FactionRelationships;
}    

public class Faction
{
    public Faction Parent;
}

public class FactionRelation
{
    public Faction ForeignFaction; //The enemy or friend of this faction
    public decimal Multiplier; //Standings gain/loss with the faction get applied to ForeignFaction by this percentage. Ex: I get +100 with NativeFaction. If multiplier is -0.1M, then I'll get -10 with ForeignFaction
}
public class Player
{
    public Dictionary <Faction, decimal> Standings;

    public void SetStandings(decimal mod)
    {
        foreach (Faction f in Global.Factions)
        {

        }
    }
}

The above code represents a rudimentary faction system for a game. The Global class initializes the game, creating the Faction objects from some data definition. It also contains a collection of Factions and decimal values which represent the relationships between factions.

Factions Let's say we have 2 main factions: DirectionFolk and ColorFolk. These have no Parent factions.

We also have UpFolk and DownFolk factions whose Parent Faction are both DirectionFolk. Finally, we have RedFolk and BlueFolk factions whose Parent Faction are both ColorFolk.

So,

  • DirectionFolk
    • UpFolk
    • DownFolk
  • ColorFolk
    • RedFolk
      • PinkFolk
      • ScarletFolk
    • BlueFolk

This nesting could go much deeper; for example, RedFolk might have PinkFolk and ScarletFolk as subfactions.

Any standing gain with any faction should propagate up and down the tree until it reaches the nesting limit in either extremity. The nesting depth of the set determines that amount of standing modification that should occur at any level. To make it simpler, let's say it is divided evenly. So if I gain 100 standing directly with ColorFolk, I should get 50 standing with both RedFolk and BlueFolk, and 25 with both PinkFolk and ScarletFolk. If instead I gained 100 standing directly with RedFolk, I should get 50 with BlueFolk, and 25 with both PinkFolk and ScarletFolk. The idea is that faction modification directly with the lowest tiers would be more pronounced than that at the highest tiers. So, it's easier to become friends or enemies with PinkFolk than it is with ColorFolk.

If this algorithm has a name, I don't know what it is.

Furthermore, any faction which does not have a parent can have a relationship with another parentless faction. So, let's say that DirectionFolk and ColorFolk are enemies. Any faction standing gain with ColorFolk should result in a proportionate standing lost with DirectionFolk. Since only parentless factions can have cross-faction relationships (positive or negative multipliers), this standing loss would always occur at the top of the related faction's chain, and trickle down.

Ideally, I'd like to be able to assign more interesting values to the faction relationships, both cross-parent-faction, and among subfactions of a parent. So a standing modification to some subfaction might propagate through some other factions depending on multipliers, and standings gain with the parent faction in the tree would be more granular.

Players The Player object has a list which represents their standings with each faction. Pretty simple - a Faction and a decimal value. This is the value I will be modifying, as each player holds the data representing their standings with a faction. (The factions themselves are unaware of players in that sense).

--

My question is, should I be directly setting a collection of values in the Player object through some hoary recursive process, or is there an elegant way to simply calculate this based on very few values stored by the player?

Also, would making this a little more interesting - say, having some factions be neutral to each other, even in the same tree (like, RedFolk and BlueFolk don't care about each other) - or mixing up the distribution so that it's not strictly even - would these kinds of things seriously complicate the matter, or is it possible to design an algorithm which is more agnostic to all that?

I'm really just looking for a start on how to wrap my head around this. If anyone else has designed something similar, I'd love to hear your thoughts on it.

+2  A: 

I don't know if this has a name, but I would use an iterative approach, giving to the faction class, which knows the factions structure, the task to update the player's standings.
Something like this:

using System;
using System.Collections.Generic;

class Faction { 
    // the parent faction
    protected Faction _parent;
    // list of children factions
    protected List<Faction> _children = new List<Faction>();

    // the Faction knows the structure, so this approach is iterative but not
    // for this difficult to maintain.
    public void ModifyStanding(Player p,Decimal amount) {
        p[this] += amount;
        foreach (Faction child in _children) {
            child.ModifyStanding(p, amount / 2);
        }
    }
}

class Player {
    // standings
    Dictionary<Faction, Decimal> _standings = new Dictionary<Faction,decimal>();
    // get / set standing indexing on faction
    public Decimal this[Faction f] {
        get { return _standings[f]; }
        set { _standings[f] = value; }
    }
}
Paolo Tedesco
This was the Other Way that I had considered. I ended up feeling like pushing the task off to the player would be a better use of resources. Ultimately, the factions don't *need* to know the player exists, so long as whenever the player encounters one, they can poll him for this information. I was thinking of a multiplayer environment in which I'd want to lessen the strain on the faction process, but I'd never "trust" the player on that, so the server would have to verify all that work anyway.
Superstringcheese
+3  A: 

The problem as I see it is that you haven't defined exactly what you want the propagation rules to be. In fact, you give 2 examples which contradict each other:

"So if I gain 100 standing directly with ColorFolk, I should get 50 standing with both RedFolk and BlueFolk [etc]" - this example only propagates down the tree, not to siblings. (No gain to DirectionFolk is mentioned.)

"If instead I gained 100 standing directly with RedFolk, I should get 50 with BlueFolk, and 25 with both PinkFolk and ScarletFolk." - whereas this example does propagate to siblings.

Obviously these both can't be true, without there being at least one more rule to decide why it propagates to some siblings and not others. In particular, it makes it very awkward to write a recursive algorithm to do this if the standing propagates sideways to siblings because your 2 child factions will get updated twice, once from their parent and once from their sibling.

You can run a generic propagation algorithm instead, if you want:

void propagateStanding(amount)
{
    // calculate relative weightings
    totalWeighting = 0.0;
    for (relationship in faction.relationships)
    {
        totalWeighting += relationship.weight;
    }

    // propagate value    
    for (relationship in faction.relationships)
    {
        amountToGive = amount * relationship.weight / totalWeighting;
        relationship.otherFaction.standing += amountToGive;
    }
}

Note that this only covers the propagation, not the increase to the original faction.

Kylotan
You're right, that's the problem. I tried to simplify the rules for the sake of the example, but in practice, I'd want the values to be granular, not evenly distributed; on further consideration, I can see that there is a distinct difference in the two approaches. Your example does not seem to handle the recursion, but I'll take a look at modifying it to do so. Thanks for the stab at it.
Superstringcheese
In the first example, it *does* propagate to siblings (red and blue both get 50) - further on, recall that increases to Color result in decreases to Direction, because Color and Direction are enemies (Color, at least, has a "FactionRelationships[ColorFolk].ForeignFaction=DirectionFolk; FactionRelationships[ColorFolk].Multiplier=-1M;" kind of value - I'll update the code to make this a little clearer). But perhaps it should not - would it be simpler to not be able to gain standing directly with a parentless faction, only with children?
Superstringcheese
You've misunderstood what I meant by siblings - I don't just mean any pair of siblings, I mean explicitly the ones that are siblings to the initial faction whose standing you're altering. In your first example, it all goes to children recursively. In your second example, it goes 'sideways' to BlueFolk. You can't use the same recursive solution for both.
Kylotan
Additionally, if you're going to have arbitrary links where raising one faction score lowers another, I suggest removing the hierarchy entirely and constructing it as a graph, setting the weights to whatever you need, and forgetting about recursion too. It doesn't appear that the situation you're trying to model maps well to a tree structure at all, and hacks such as treating parent-less factions differently just complicate matters. A simple graph of factions and their standing propagation weights should do fine.
Kylotan