In our geometry library, we have a Polygon class, containing (amongst many others, of course) a method which checks if this polygon contains another polygon:
public bool Contains(Polygon other) {
//complex containment checking code goes here
}
Other important things to note are that the polygon is mutable, and that there is no way to be notified of when its shape changes.
Now, in one of our projects, we have a validation function, which checks for a huge number of polygons whether they are all contained within a single outer boundary.
Polygon boundary = ...;
foreach (var poly in _myMassiveListOfPolygons)
if (!boundary.Contains(poly))
//error handling code goes here
Profiling has determined this to be quite a bottleneck. Fortunately there is a way to vastly speed up the calculation. We know that the boundary polygon is always convex, so instead of using the general Contains method, we can write a specialized method that assumes the outer polygon is convex.
Initially I added something like this to the client code:
Func<Polygon, bool> containsFunc;
// Keep both options, in case my assumption about being always convex is wrong
if (boundary.IsConvex())
{
containsFunc = p => { ... }; //efficient code for convex boundary goes here
}
else
{
containsFunc = p => boundary.Contains(p);
}
foreach (var poly in _myMassiveListOfPolygons)
if (!containsFunc(poly))
//error handling code goes here
Joy all around! Performance has increased tenfold!
However, it doesn't seem right that the code for this alternative Contains method is located in the client project, where it can't be reused by others.
So I tried to move it to the Polygon class itself.
public bool Contains(Polygon other) {
if (this.IsConvex())
return ConvexContains(other);
else
return GenericContains(other);
}
private bool GenericContains(Polygon other) { ...}
private bool ConvexContains(Polygon other) { ...}
With this method, the client code returns back to the original. Unfortunately, there is one big difference compared to the previous code: Before there was one call to boundary.IsConvex() to select the correct function to use, where now this method is called for every invocation of Contains! Although this is still faster than using the generic Contains method, most of the performance improvements are lost again (not to mention performance decreases for concave polygons).
A third possibility would be to have two public methods to check containment, where one of them assumes that the polygon is convex (without doing the check itself, of course, otherwise we're back to the previous overhead problem). This doesn't seem like a good idea, since calling it by accident with a concave polygon could give unexpected results, making for hard to find bugs.
So finally, my question is, is there any good way to deal with this situation? Where should we put the alternative Contains method, and how should we call it?
Edit
I like the idea of caching whether or not a polygon is convex. But I cannot change the interface nor the behavior of the class, which is problematic in a case such as this:
//relevant part of the polygon interface
public List<Point> Points { get; set; }
//client code
var poly = new Polygon();
var list = new List<Point>
{
new Point(0,0),
new Point(10,0),
new Point(0,10)
};
poly.Points = list;
list.Add(new Point(1, 1));
This last line of code is executed on a regular List<Point>, there is nothing I can do about that. However, there would now be 2 requirements:
The polygon must now contain the added point. This is to ensure existing code doesn't break. This means that we cannot copy the Points into our own, observable list.
We must get notified of this change to the list of points (because our polygon has turned from convex to concave). This means that we cannot wrap the list by our own, observable list, because only changes made through the wrapper would cause a notification.
One option is to create another concept in the geometry library, say FixedBoundary, which could encapsulate that logic. It would be immutable so that you could safely cache the convexity. An example:
public class FixedBoundary
{
private Polygon boundary;
private bool isConvex;
public FixedBoundary(Polygon boundary)
{
// deep clone so we don't have to worry about the boundary being modified later.
this.boundary = boundary.Clone();
this.isConvex = this.boundary.IsConvex();
}
public bool Contains(Polygon p)
{
if (isConvex)
{
// efficient convex logic here
}
else
{
return this.boundary.Contains(p);
}
}
}
This of course adds some conceptual weight to the geometry library. Another option would be to add an ImmutablePolygon (and by extension ImmutablePoint) which could do the same, however conversions may be cumbersome and affect performance as well.
You could replicate what you've done already with the Func<T, bool> delegate.. internal to the Polygon.. perhaps like this?
private Func<Polygon, bool> _containsFunc;
// ...
public bool Contains(Polygon other) {
if (_containsFunc == null) {
if (this.IsConvex())
_containsFunc = ConvexContains;
else
_containsFunc = GenericContains;
}
return _containsFunc(other);
}
Each call to Contains after the first will not call IsConvex. Unless I have misunderstood you.. that sounds like what you're after.
Okay...you guys really shot yourselves in the foot by using List in your public interface. Here is how you can try to solve it (there is a small non-zero chance that this will incorrectly cache, but its a 1 in (1 <<32 ) chance.
As you have noted, we could cache the polygon's Convexness...but the problem then becomes a case of Cache invalidation.
Without being able invalid cache on object change. You would have to check for cache coherence on each call. This is where hashing comes in.
By default the hash for list is quite useless, you want to use a this solution for getting a usable hash.
So what you want to do is to add a nullable<int> _cachedHashCode on your polygon. When you call Polygon.IsConvex, you hash your List<Point>, compare with your _cachedHashCode, and if they don't equal, recompute your IsConvex.
Related
I am a C#-Newbie.
I have a function that is supposed to return all values from a List, that have the matching time-stamp:
static public PointCloud getPointsByTime (float time)
{
PointCloud returnList = new List<PointData> ();
for (int i = 0; i < _pointCloud.Count; i++) {
if (_pointCloud [i].time == time) {
returnList.Add (_pointCloud [i]);
}
}
return returnList;
}
Where
public class PointData
{
public float time;
// and some other members
}
and
// let's call a list of PointData-objects a PointCloud
using PointCloud = System.Collections.Generic.List<PointData>;
Does my function do what I want it to do? Or do I have to create a new PointData-object? Am I able to use my returned PointCloud or will it be out of scope and deleted?
This may be not the best example to explain so feel free to link me to something better. I think you get what my basic quastions are.
As #Patrick suggested inheriting for List seems more reasonable, but I would go further and I would just use a List so you don't create an unnecessary class if it is not going to add anything extra.
Also I suggest you to have a look to LINQ which makes the code more readable and is a very powerful feature that you would like to master as soon as possible. :)
Your method could look then like this:
_pointCloud.Where(p => p.time == time).ToList();
Also try to get familiar with properties:
public class PointData
{
public float Time { get; set; }
}
And you may want to follow the more standard C# coding style (although this is completely personal) of using PascalCase for public members instead of camelCase.
Your code is correct. You can use your function like so:
var someTime = 0.0f;
var pointsAtTime = getPointsByTime(someTime);
DoSomethingWith(pointsAtTime);
The return value from the function remains in scope if you assign it to some local variable (e.g. pointsAtTime here).
EDIT: As Peter Schneider correctly notes in the comments, you need to be aware that this function creates a new list with references to the matching points, and does not create new points. This might or might not be what you want.
However, if you're new to C#, here are some things you might want to keep in mind:
Methods in C# are conventionally named in TitleCase, e.g. GetPointsByTime and not getPointsByTime.
Assigning names to generic types like using PointCloud = List<PointData>, while technically allowed, is not very idiomatic and might confuse other readers of your code. If you think a list of PointData is special enough to have its own type, create a type for it (either by inheriting from List<PointData> or, preferably, using a IList<PointData> as a member in a new PointCloud class). Or just use using System.Collections.Generic and use List<PointData> throughout your code, which is what most people would do.
Comparing floating-point numbers for equality is sometimes discouraged as this might fail in some cases due to representation errors; if the time is truly a continuous value, you might want to look for points in a specific time period (e.g. points who fall within some range around your desired time). You don't really have to worry about this for now, though.
I am writing my own 'Rubik's cube' application. The main class Cube has 18 rotation methods:
RotateAxisXClockWise, RotateAxisXAntiClockWise
RotateAxisYClockWise, RotateAxisYAntiClockWise
RotateAxisZClockWise, RotateAxisZAntiClockWise
RotateUpperFaceClockWise, RotateUpperFaceAntiClockWise
RotateFrontFaceClockWise, RotateFrontFaceAntiClockWise
RotateRightFaceClockWise, RotateRightFaceAntiClockWise
RotateBackFaceClockWise, RotateBackFAceAntiClockWise
RotateLeftFaceClockWise, RotateLeftFaceAntiClockWise
RotateDownFaceClockWise, RotateDownFaceAntiClockWise
Yes, they could be joined in pairs with a parameter Direction (for example RotateFrontFace(Direction direction)) but for now this seems appropriately.
I would like to implement undo/redo functionality and because all methods have the same signature (no input parameters, void return type) they could be saved in a LinkedList data structure. So every time one of the rotation methods is called, it is added to the linked list.
This would work pretty well if we start on the beginning of the LinkedList (haven't tried it out yet though) and advance toward the end, so each rotation would be performed exactly as it was in the first place.
But what about undo? If I traverse the list from the end to the beginnig, then the opposite method should be called (for example instead of RotateFrontFaceClockWise, RotateFrontFaceAntiClockWise should be called). Any ideas how to implement this? Elegantly? :)
I would not use delegate references as the way to model the rotations, if one of the main purposes is to be able to perform redo/undo. I would consider creating a data-model for each rotation, and store a list of these rotation steps. Each step could then have it's own associated Redo/Undo delegate, which allows someone traversing the list (from either end) to understand what operations took place, and either repeat or reverse them.
One additional benefit of a data-oriented approach to model such transformations, is that it could potentially reduce the number of similar (but slightly different) versions of your RotateXXX( ) methods.
EDIT: Addressing the your question about what shape such a solution might take.
The simplest thing to do may be to store a Tuple<Action,Action> representing each pair of rotate/unrotate operations as paired delegates. However, I would consider using an explicit data structure that describes the rotation operation, perhaps eventually including things like a descriptive name, direction/face attributes, and so on. I would also change your RotateXXX methods so that they are static methods of Cube, and accept an instance of cube as a parameter. This would allow modeling the rotation operations externally to the instance of Cube.
public sealed class Rotation
{
private readonly Action<Cube> _RotateAction;
private readonly Action<Cube> _UnrotateAction; // used for undo or backtracking
private Rotation( Action<Cube> rotateAction, Action<Cube> unrotateAction )
{
_RotateAction = rotateAction;
_UnrotateAction = unrotateAction;
}
public void Rotate( Cube cube ) { _RotateAction( cube ); }
public void Unrotate( Cube cube ) { _Unrotate( cube ); }
public static readonly RotateFrontFaceClockswise =
new Rotation( Cube.RotateFrontFaceClockwise
Cube.RotateFrontFaceCounterClockwise );
public static readonly RotateFrontFaceCounterClockwise =
new Rotation( Cube.RotateFrontFaceCounterClockwise,
Cube.RotateFrontFaceClockwise );
public static readonly RotateLeftFaceClockwise =
new Rotation( Cube.RotateLeftFaceClockwise,
Cube.RotateLeftFaceCounterClockwise );
public static readonly RotateLeftFaceCounterClockwise =
new Rotation( Cube.RotateLeftFaceCounterClockwise,
Cube.RotateLeftFaceClockwise );
// etc..
}
// now we can keep track of the state changes of a cube using:
List<Rotation> cubeRotations = new List<Rotation>();
cubeRotations.Add( Rotation.RotateFrontFaceCounterClockwise );
cubeRotations.Add( Rotation.RotateBackFaceClockwise );
cubeRotations.Add( Rotation.RotateLeftFaceCounterClockwise );
// to apply the rotations to a cube, you simple walk through the data structure
// calling the Rotate( ) method on each:
Cube someCube = new Cube( ... )
foreach( Rotation r in cubeRotations )
{
r.Rotate( someCube );
}
// to undo these rotations you can walk the like in reverse:
foreach( Rotation r in cubeRotations.Reverse() )
{
r.Unrotate( someCube );
}
I know you wanted to avoid parameterizing your methods, but you're probably best off pursuing something along those lines:
enum Face
{
Top,
Bottom,
Left,
Right,
Front,
Back
}
enum Direction
{
Clockwise,
CounterClockwise
}
struct Rotation
{
public Face Face;
public Direction Direction;
}
LinkedList<Rotation> actions;
You can make a choice whether to push the direct or inverse action onto the list, but once you store the actions in this way, its very easy to write some quick switch statements to reverse an action as it gets removed from the list.
On a side note, consider replacing LinkedList with Stack, it will server you just as well and its suited for the purpose perfectly.
EDIT:
Noticed that I don't have support for the axis rotations in my enumerations, but I'm sure you could add them as additional entries in the Face enumeration (although you might want to rename it at that point)
18 methods seems like a lot to keep up with, especially when you consider implementing the undo/redo functionality. Have you considered using a single, more generic method? If you did so, you could store what was passed to that method in a very uniform manner, and easily perform the opposite actions. The opposite actions could be looked up from a dictionary.
Assuming you really want to stick to your model...
You could try by entering in the list every name of the method executed and then, with reflection, call the counter parts by iterating over the list. This assumes that your methods will have matching names (as you proposed).
You could also create a HybridDictionary into which you use the method names as the id and addressof the counter method as the value. And so when you iterate over the list you could have a delegate handle the method at the given value.
For instance, along the lines of:
public bool Intersect (Ray ray, out float distance, out Vector3 normal)
{
}
vs
public IntersectResult Intersect (Ray ray)
{
}
public class IntersectResult
{
public bool Intersects {get;set;}
public float Distance {get;set;}
public Vector3 Normal {get;set;}
}
Which is better both for clarity, ease of use, and most importantly performance.
I would use a combined type and I'll tell you why: because computation of a value should return the value, not mutate a bunch of variables. Mutating a bunch of variables doesn't scale once you need more than one of them mutated. Suppose you want a thousand of these things:
IEnumerable<Ray> rays = GetAThousandRays();
var intersections = from ray in rays
where Intersect(ray, out distance, out normal)
orderby distance ...
Executing the query is now repeatedly mutating the same two variables. You're ordering based on a value that is being mutated. This is a mess. Don't make queries that mutate things; it is very confusing.
What you want is:
var intersections = from ray in rays
let intersection = Intersect(ray)
where intersection.Intersects
orderby intersection.Distance ...
No mutation; manipulate a sequence of values as values not as variables.
I also would be inclined to maybe get rid of that Boolean flag, and make the value an immutable struct:
// returns null if there is no intersection
Intersection? Intersect(Ray ray) { ... }
struct Intersection
{
public double Distance { get; private set; }
public Vector3 Normal { get; private set; }
public Intersection(double distance, Vector3 normal) : this()
{
this.Normal = normal;
this.Distance = distance;
}
}
I would use a combined type.
With an object you can attach behaviour, and return an arbitrarily complex object. You may wish to refactor your method in the future, and change the return values. By wrapping them in a return object and adding behaviour to that object, this refactoring can become largely transparent.
It's tempting to use tuples and the like. However the refactoring effort becomes a headache after a while (I'm speaking from experience here, having just made this mistake again)
It's best to return a combined type. At the very least your method signature is cleaner and there's less work for the programmer to do when calling it. EG: you don't have to declare and initialize variables, which clutters the calling code.
Some people say it's a matter of preference, but I think that returning a complex object is better not only for clarity, but for maintenance as well.
If you ever needed to add another bit of data as output, you would have to change the method signature in addition to adding the code for the extra response; not so with a complex output type. In addition, it's harder to mock (for unit testing) output parameters. It also seems to break the 'simple' philosophy - you're having to go through the trouble of getting the output parameters set up when you only need one piece of input. The strength of an OOP language is making types - go with that route in my opinion.
The performance difference between the two is negligible.
You could use a Tuple instead of creating the IntersectResult class as an alternative.
Reference:
http://msdn.microsoft.com/en-us/library/system.tuple.aspx
However, I would definitely lean toward returning a complex type over out parameters.
Just for the record:
It's hard to find a good example of the magic trio (clarity, ease of use, and performance).
Obviously the second example provides the first two, while the first one provides better performance. Whether this is negligible, I do not know. Maybe run a benchmark test and find out.
What I can tell you is that if you make your result type a small-sized struct, you can still save some performance points, since allocating on the heap is more expensive than on the stack. Again, copying the data from a value type can outweigh the heap-allocation penalty, but maybe not if you keep it small enough.
Furthermore, you probably want to make that type immutable.
Option 2 is probably the best solution if it best captures your domain. In which case your new type will get used in many other places as it represents a logical entity.
That depends entirely on how coherent the concept of an IntersectResult is.
Technically there is no reason to prefer one over the other.
I'm working on a hobby project of the game Baroque Chess. For those that haven't played it, it has the same basic rules as chess, but the methods for movement and capturing are different.
Naturally I created standard classes for the game: GameState, Board, Square, and a class designated for each piece that inherit from a BasePiece.
Each piece has 2 main virtual methods, GetPossibleMoves(Board board) and GetCapturedSquares(Board board, Square toSquare).
Now, one of the pieces, the Imitator, captures pieces by "imitating" the piece that it captures. For example, a Long Leaper can capture pieces by jumping over them. This means that the Imitator can jump over enemy Long Leapers to capture them (but cannot jump anything else).
I completed the GetCapturedSquares() functionality for all the pieces except the Imitator (which is definitely the trickiest of the pieces).
My basic algorithm for the Imitator was:
find all the squares with enemy pieces on them
for each enemy piece...
create a simulated piece in the same location as the Imitator
find the valid captures if it were the simulated piece moving to a chosen square
verify the enemy square is in that list of captured squares
Since I had already written the code for the other pieces' movements, I figured I would just instantiate new pieces and use their GetCapturedSquares() methods depending on which type of piece the enemy was. To do this, I setup up a Dictionary as you can see here that maps a System.Type to an instantiated object of said type:
var typeToPiece = new Dictionary<Type, BasePiece>()
{
{typeof(Pincer), new Pincer() { Color = this.Color, CurrentSquare = this.CurrentSquare}},
{typeof(Withdrawer), new Withdrawer() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(Coordinator), new Coordinator() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(LongLeaper), new LongLeaper() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
{typeof(King), new King() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
};
//...
var possibleMoves = typeToPiece[enemySquare.Occupant.GetType()].GetPossibleMoves(board, toSquare);
Doing this makes me feel dirty inside. Is it more appropriate to create an enum or string that represents the piece type as the dictionary key, or does it really not matter? Is there a different way to handle this? I am of the opinion that it's fine just the way it is but I am interested in hearing your thoughts.
I think you should add an abstract method to BasePiece that "clones" the current piece and returns the simulated piece.
You'd override this method in every piece type. To share code between them, you can add a protected method to the base class that copies the shared properties to the instance passed to it:
abstract class BasePiece {
protected BasePiece(BasePiece pieceToClone) {
this.Color = pieceToClone.Color;
this.CurrentSquare = pieceToClone.CurrentSquare;
}
public abstract BasePiece GetSimulatedClone();
}
class King : BasePiece {
protected King(King pieceToClone) : base(pieceToClone) { }
public King() { }
public override BasePiece GetSimulatedClone() {
return new King(this);
}
}
In general, whenever you are switching based on the type of a variable, you should think twice and see if you can polymorphism instead.
This is a little more elegant to my mind:
private abstract class Piece {}
private class King : Piece { }
private class Imitator : Piece { }
private void main(object sender, EventArgs e) {
Piece x;
x = CreateNewPiece(new King());
x = CreateNewPiece(new Imitator());
}
private T CreateNewPiece<T>(T piece) where T : Piece, new() {
return new T();
}
It relies on the new() generic constraint to instantiate a type variable.
I personally agree that you're okay to do what you're doing since it's a small hobby project. If you really want to get around the problem you're describing, though, you could create a separate tier of Mover objects that handles the logic surrounding the actual movement of different pieces. Then, rather than asking a piece what moves it can do, you pass that piece's position information, along with the board state information, to the Mover, which tells you what moves that piece could make. The Mover associated with an Imitator could then be a combination of all the other movers in the way you describe, but without the need to create fake "pieces" on the fly.
Another suggestion I'd make, which is more related to logic than to your model, is to change your logic like so:
for each piece type (or mover type)
create a simulated piece in the same location as the Imitator
find the valid captures if it were the simulated piece moving to a chosen square
only keep the captures where the occupant of the enemy square is of the piece type you're checking
This is a subtle difference, but will significantly reduce the amount of calculation required.
Update
Recursive's comment made me realize I may not have been clear enough about how the Mover tier would work. The idea is to have a KingMover, PincerMover, and so forth, which know about the moves for a specific piece type. Since they're tied to a piece's type rather than the piece itself, they could even be singletons. Each piece type could have a Mover field that points to its mover, and then either your business logic could call that Mover's GetPossibleMoves method directly, or your piece's GetPossibleMoves method could simply call the Mover's method, passing itself in as an argument. The ImitatorMover would know to ask each other type of mover for its possible moves, and then filter those moves based on whether they would be attacking a piece of the type associated with that mover.
You'd have almost the same code as in the current system, but the code for each Piece could really just focus on representing that Piece's information (position, color, etc.), whereas the code for actually determining how a piece moves would be moved into a separate tier of classes. Each class would have a single purpose.
Without touching this specific problem, there's nothing inherently wrong with the idea of a dictionary that lets you look up objects by their type. In fact, .NET FCL provides such a type already - it's called KeyedByTypeCollection<T>. It's probably better for such things, because it ensures that key of an object is its type as returned by GetType() (and not some other random type), and won't let you add two objects of the same type.
I am programming a simple role playing game (to learn and for fun) and I'm at the point where I'm trying to come up with a way for game objects to interact with each other. There are two things I am trying to avoid.
Creating a gigantic game object that can be anything and do everything
Complexity - so I am staying away from a component based design like you see here
So with those parameters in mind I need advice on a good way for game objects to perform actions on each other.
For example
Creatures (Characters, Monsters, NPCs) can perform actions on Creatures or Items (weapons, potions, traps, doors)
Items can perform actions on Creatures or Items as well. An example would be a trap going off when a character tries to open a chest
What I've come up with is a PerformAction method that can take Creatures or Items as parameters. Like this
PerformAction(Creature sourceC, Item sourceI, Creature targetC, Item targetI)
// this will usually end up with 2 null params since
// only 1 source and 1 target will be valid
Or should I do this instead?
PerformAction(Object source, Object target)
// cast to correct types and continue
Or is there a completely different way I should be thinking about this?
This is a "double dispatch" problem. In regular OO programming, you "dispatch" the operation of a virtual method call to the concrete type of the class implementing the object instance you call against. A client doesn't need to know the actual implementation type, it is simply making a method call against an abstract type description. That's "single dispatch".
Most OO languages don't implement anything but single-dispatch. Double-dispatch is when the operation that needs to be called depends on two different objects. The standard mechanism for implementing double dispatch in OO languages without direct double-dispatch support is the "Visitor" design pattern. See the link for how to use this pattern.
This sounds like a case for polymorphism. Instead of taking Item or Creature as an argument, make both of them derive (or implement) from ActionTarget or ActionSource. Let the implementation of Creature or Item determine which way to go from there.
You very rarely want to leave it so open by just taking Object. Even a little information is better than none.
You can try mixing the Command pattern with some clever use of interfaces to solve this:
// everything in the game (creature, item, hero, etc.) derives from this
public class Entity {}
// every action that can be performed derives from this
public abstract class Command
{
public abstract void Perform(Entity source, Entity target);
}
// these are the capabilities an entity may have. these are how the Commands
// interact with entities:
public interface IDamageable
{
void TakeDamage(int amount);
}
public interface IOpenable
{
void Open();
}
public interface IMoveable
{
void Move(int x, int y);
}
Then a derived Command downcasts to see if it can do what it needs to the target:
public class FireBallCommand : Command
{
public override void Perform(Entity source, Entity target)
{
// a fireball hurts the target and blows it back
var damageTarget = target as IDamageable;
if (damageTarget != null)
{
damageTarget.TakeDamage(234);
}
var moveTarget = target as IMoveable;
if (moveTarget != null)
{
moveTarget.Move(1, 1);
}
}
}
Note that:
A derived Entity only has to implement the capabilities that are appropriate for it.
The base Entity class doesn't have code for any capability. It's nice and simple.
Commands can gracefully do nothing if an entity is unaffected by it.
I think you're examining too small a part of the problem; how do you even determine the arguments to the PerformAction function in the first place? Something outside of the PerformAction function already knows (or somehow must find out) whether the action it wants to invoke requires a target or not, and how many targets, and which item or character it's operating upon. Crucially, some part of the code must decide what operation is taking place. You've omitted that from the post but I think that is the absolute most important aspect, because it's the action that determines the required arguments. And once you know those arguments, you know the form of the function or method to invoke.
Say a character has opened a chest, and a trap goes off. You presumably already have code which is an event handler for the chest being opened, and you can easily pass in the character that did it. You also presumably already ascertained that the object was a trapped chest. So you have the information you need already:
// pseudocode
function on_opened(Character opener)
{
this.triggerTrap(opener)
}
If you have a single Item class, the base implementation of triggerTrap will be empty, and you'll need to insert some sort of checks, eg. is_chest and is_trapped. If you have a derived Chest class, you'll probably just need is_trapped. But really, it's only as difficult as you make it.
Same goes for opening the chest in the first place: your input code will know who is acting (eg. the current player, or the current AI character), can determine what the target is (by finding an item under the mouse, or on the command line), and can determine the required action based on the input. It then simply becomes a case of looking up the right objects and calling the right method with those arguments.
item = get_object_under_cursor()
if item is not None:
if currently_held_item is not None:
player_use_item_on_other_item(currently_held_item, item)
else
player.use_item(item)
return
character = get_character_under_cursor()
if character is not None:
if character.is_friendly_to(player):
player.talk_to(character)
else
player.attack(character)
return
Keep it simple. :)
in the Zork model, each action one can do to an object is expressed as a method of that object, e.g.
door.Open()
monster.Attack()
something generic like PerformAction will end up being a big ball of mud...
What about having a method on your Actors (creatures, items) that Perform the action on a target(s). That way each item can act differently and you won't have one big massive method to deal with all the individual items/creatures.
example:
public abstract bool PerformAction(Object target); //returns if object is a valid target and action was performed
I've had a similar situation to this, although mine wasn't Role playing, but devices that sometimes had similar characteristics to other devices, but also some characteristics that are unique. The key is to use Interfaces to define a class of actions, such as ICanAttack and then implement the particular method on the objects. If you need common code to handle this across multiple objects and there's no clear way to derive one from the other then you simply use a utility class with a static method to do the implementation:
public interface ICanAttack { void Attack(Character attackee); }
public class Character { ... }
public class Warrior : Character, ICanAttack
{
public void Attack(Character attackee) { CharacterUtils.Attack(this, attackee); }
}
public static class CharacterUtils
{
public static void Attack(Character attacker, Character attackee) { ... }
}
Then if you have code that needs to determine whether a character can or can't do something:
public void Process(Character myCharacter)
{
...
ICanAttack attacker = null;
if ((attacker = (myCharacter as ICanAttack)) != null) attacker.Attack(anotherCharacter);
}
This way, you explicitly know what capabilities any particular type of character has, you get good code reuse, and the code is relatively self-documenting. The main drawback to this is that it is easy to end up with objects that implement a LOT of interfaces, depending on how complex your game is.
This might not be something that many would agree upon, but I'm not a team and it works for me (in most cases).
Instead of thinking of every Object as a collection of stuff, think of it as a collection of references to stuff. Basically, instead of one huge list of many
Object
- Position
- Legs
- [..n]
You would have something like this (with values stripped, leaving only relationships):
Whenever your player (or creature, or [..n]) wants to open a box, simply call
Player.Open(Something Target); //or
Creature.Open(Something Target); //or
[..n].Open(Something Target);
Where "Something" can be a set of rules, or just an integer which identifies the target (or even better, the target itself), if the target exists and indeed can be opened, open it.
All this can (quite) easily be implemented through a series of, say interfaces, like this:
interface IDraggable
{
void DragTo(
int X,
int Y
);
}
interface IDamageable
{
void Damage(
int A
);
}
With clever usage of these interfaces you might even ending up using stuff like delegates to make an abstraction between top-level
IDamageable
and the sub-level
IBurnable
Hope it helped :)
EDIT: This was embarassing, but it seems I hijacked #munificent's answer! I'm sorry #munificent! Anyway, look at his example if you want an actual example instead of an explanation of how the concept works.
EDIT 2: Oh crap. I just saw that you clearly stated you didn't want any of the stuff that was contained in the article you linked, which clearly is exactly the same as I have written about here! Disregard this answer if you like and sorry for it!