Jump to content

  • Log In with Google      Sign In   
  • Create Account

Object Management?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
27 replies to this topic

#1 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 22 May 2011 - 07:08 PM

Hey everyone. I've been messing around with C# and XNA to try and make a fairly simple game just to start, but a problem I've run into, which I've run into before and had trouble with, is managing objects that are created and destroyed on the fly, and specifically how you would tell the engine to draw all of those objects. For example, right now I'm just trying to make a simple spaceship that shoots bullets when you hit the spacebar. I've got the ship moving around just fine, but I can't figure out how to make the newly created Bullet objects draw and stop drawing themselves correctly. I'm sure there must be some standard way that this is done, but I've been unable to find anything, probably mostly because I have no idea what to call such a system in the first place!

I've tried making an array that stores all the objects that need to be rendered, but I can't seem to make it work, and I don't think my solution is ideal anyways. If someone could share some example code of how this type of thing is managed in an existing game, that would be extremely helpful.

Sponsor:

#2 DementedCarrot   Members   -  Reputation: 495

Like
1Likes
Like

Posted 22 May 2011 - 08:03 PM

You need to define an abstract "GameObject" class that has Update() and Draw() virtual functions that have to be overloaded. You can also define position/orientation/scale stuff inside a game object class. Every object in your game (players, bullets, etc) needs to derive from the GameObject.

The object manager should deal with GameObject's. It should also have an Update() and Draw() function that loops through every GameObject the object manager manages and individually updates and draws them. Be sure update comes before draw though.

I generally store my game objects in lists, but usually thats just because I'm too lazy to write fixed array resizing code. Its more efficient to write one that works with fixed arrays (for static stuff that doesn't change often) and linked lists (for the dynamic stuff like those bullets, because things can be added to and removed from linked lists without re-sorting or re-sizing an array internally). If you don't know the differences between linked lists and array storage, I recomend you look into them!

Further more.. I usually keep "lists of lists" to keep separate types of objects together. This way you can update certain GameObject's that need to loop through specific other types of GameObject without having to search through -everything- for the correct type. This way you can pull lists of players or enemies separately. For that you define a generic function "public GetTypeList<T>()" so you can get a list of a certain type back. If you dont know about generics you should read up on those too. This comes in handy for things like bullets especially. If you're calling update on individual bullets that need to check enemies for collision, you dont have to search through every type of object blindly. You can do something like this in the update function of the bullet:

List<Enemy> enemies = ObjectManager.GetTypeList<Enemy>(); // Grab the list of enemies.

foreach(Enemy e in enemies)

{

  if(bulletCollidesWithEnemy)
  {


    Enemy.Kill() // Kill the enemy, he's been shot!
    this.Kill() // Kill the bullet because it's hit an enemy.
  }


}


You have to implement a function in the ObjectManager that removes GameObjects though. Something like ObjectManager.Remove(GameObject obj). It really depends on the type of game and the data structures you use to determine the best way to remove objects. If you call list.remove(), it will search the list sequentially until it finds the object to remove. This could get really inefficient the larger your game is, so it might be better to add a removal bool value to your GameObjects and add another loop to search for and delete those later. You have to be VERY careful about things that get removed as other objects could still be referring to the object you are "removing". If you remove something from the object manager and another GameObject has a reference to the object that you are removing, the object wont be removed. C#'s garbage collector wont get rid of it because it still has a reference, and since its not managed by the object manager anymore it could cause problems. If you remove an object you have to do checks to make sure all of its references are gone, OR add checks inside the functions that use those references "if(object.isDead())".

I would give you a copy of my ObjectManager and GameObject classes if the power supply on my programming computer didn't recently die. :P

But yeah.. Back to your original post. I dont think your game objects had separate Update() and Draw() functions. Your objects need to be able to kill themselves or kill others inside update functions. Please ask more questions; I've just uber-ranted.

#3 ryan mccabe   Members   -  Reputation: 161

Like
0Likes
Like

Posted 22 May 2011 - 11:18 PM

Small tip. Rather than continually creating and destroying something like a bullet, create your objects at level load. Have a pool of bullets large enough to ensure that no one can hit space enough to fill the screen with all of them. When space is hit the bullet is set to the fire location and given velocity, when the bullet hits something or moves off screen reset it (null position and not set to render). If the game is simple enough you can do this with enemies as well. It gets out of constant resource creation and deletion, simplifying your task to set, event, reset.

if (bullet.location.y < screenHieght && bullet.location.x < screenWidth)
{
	if (bulletCollision())
	{
		reset(bullet);
		reset(whatsBeenHit);
		update(scoreEct);
	}
	bulletUpdate(bullet);
}
else { reset(bullet); }



Its pseudo code but I did something similar in Unity with C#.

#4 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 22 May 2011 - 11:36 PM

At the moment, I've actually already got my objects seperated into a good number of classes. I've got RenderedObject, which is anything that's visible, and only has a position, rotation, and sprite. I also have MobileObject, which inherits from RenderedObject, which also has a velocity and acceleration. And so on down the line for DestructibleObject (has HP), ShipObject (has weapons and such), and then finally my actual SimpleFighter, which has the actual stats for the specific ship. I also have a SimpleBullet that inherits from ProjectileObject, which itself inherits from MobileObject. So I think my main problem was just that I don't know the syntax for making a list of objects, or a list of lists for that matter.

#5 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 22 May 2011 - 11:51 PM

Small tip. Rather than continually creating and destroying something like a bullet, create your objects at level load. Have a pool of bullets large enough to ensure that no one can hit space enough to fill the screen with all of them. When space is hit the bullet is set to the fire location and given velocity, when the bullet hits something or moves off screen reset it (null position and not set to render). If the game is simple enough you can do this with enemies as well. It gets out of constant resource creation and deletion, simplifying your task to set, event, reset.

if (bullet.location.y < screenHieght && bullet.location.x < screenWidth)
{
	if (bulletCollision())
	{
		reset(bullet);
		reset(whatsBeenHit);
		update(scoreEct);
	}
	bulletUpdate(bullet);
}
else { reset(bullet); }



Its pseudo code but I did something similar in Unity with C#.


I don't think that would really work for what I'm doing. There's going to be a whole lot of bullets of many different types flying around at any given time, after all. And does it really save that much processing power or anything? Doesn't seem like it would all that much. I guess I could simply not destroy bullets after they're created, and if a bullet of that type is fired again, it'll re-use the old bullet before making a new one. That's really an optimization thing though, so I don't think I'll worry about that quite yet.

#6 haegarr   Crossbones+   -  Reputation: 4310

Like
1Likes
Like

Posted 23 May 2011 - 02:05 AM

Object management is an broad issue though. The trend of the last years in larger projects (and I explicitly stress the term "larger" here) is to avoid deep class hierarchies as described by the OP, because they tend to either cause an explosion of types or else migrating nearly all stuff into base classes, thus violating the "single responsibility" design principle (from this point of view also having draw() and update() in the same class is questionable). Similarly, scene management is gone away from the omnipotent scene graph to a variety of sub-systems, each one responsible for a single or at most a couple of closely related tasks. This had led to the development of the nowadays famous entity-component systems.

It is usual to split updating and drawing of game object. But looking at more complicated examples, one can see that even updating can often not be handled in a single step. Animation and physics and especially the reaction of game objects on other (updated) game objects needs to be considered here. And also drawing is not that easy as soon as batching, transparency, avoiding of state switches and similar stuff come into play (what is another point of not having game objects draw themselves). Not to mention that e.g. collision detection isn't something that can be done locally by a game object itself.

IMHO the principle of object management should consider to use more aggregation than inheritance, to out-source tasks to dedicated sub-systems, to limit a scene graph to just a logical scene description, and to link the belonging single aggregates of game objects into the sub-systems dealing with the appropriate functionality. Although this need not be strictly followed in small projects, IMHO it would be a great practice just to start with, preparing the ground to more complex projects coming in the future.

Coming to more concrete statements: Projectiles may be implemented as game objects with separated behavioral representation and graphical representation. This way would probably allow to fetch a shared graphical representation from resource management, and to use unified behavior aggregates fetched from a pool. The graphical representation needs to be linked into the rendering sub-system, the bounding volume needs to be linked into the collision sub-system, the movement needs to be linked into the animation sub-system. There need to be several collision handlers. E.g. one handler that destructs projectiles that leave the bounding volume of the screen, another that handles collision with enemy ships, ...

Just my 2 EUR-Cents

#7 rip-off   Moderators   -  Reputation: 8216

Like
0Likes
Like

Posted 23 May 2011 - 03:28 AM

At the moment, I've actually already got my objects seperated into a good number of classes. I've got RenderedObject, which is anything that's visible, and only has a position, rotation, and sprite. I also have MobileObject, which inherits from RenderedObject, which also has a velocity and acceleration. And so on down the line for DestructibleObject (has HP), ShipObject (has weapons and such), and then finally my actual SimpleFighter, which has the actual stats for the specific ship. I also have a SimpleBullet that inherits from ProjectileObject, which itself inherits from MobileObject. So I think my main problem was just that I don't know the syntax for making a list of objects, or a list of lists for that matter.

This sounds quite overengineered for your self-described "simple game", where a ship shoots bullets (a lot of the solutions suggested are similarly complex, IMO). KISS and YAGNI are important principles to keep in mind. Maybe you're writing this game as a simple way to test a more complex "engine" that you are developing, in which case this class cluster might make a little more sense.

You need to define an abstract "GameObject" class that has Update() and Draw() virtual functions that have to be overloaded. You can also define position/orientation/scale stuff inside a game object class. Every object in your game (players, bullets, etc) needs to derive from the GameObject.
Emphasis added

It is perfectly possible to do this without such a class or design. I would recommend it as one starting point for simple games, but you certainly don't need to do these things. For some games, having a bunch of lists of concrete classes makes more sense and is actually simpler.

The object manager ....

Beware of "manager" classes. These are often poorly defined. The class that holds my game objects is usually called a "World" or similar. Functionality outside the scope of the world is moved to other classes.

Small tip. Rather than continually creating and destroying something like a bullet, create your objects at level load. Have a pool of bullets large enough to ensure that no one can hit space enough to fill the screen with all of them.

Object pooling is an optimisation, I wouldn't recommend it (yet) until you know you need it. It can add complexity to the object, because it makes it harder to enforce invariants. In particular, a Reset() function for complex objects can be an easy place for bugs to slip in.

That said, for languages with garbage collection it has a certain justification because it reduces the amount of collection pauses. This isn't the only solution - using a "structure of arrays" approach for frequently allocated and deallocated objects can combine this optimisation with better cache performance. It is something to consider anyway.

I generally store my game objects in lists, but usually thats just because I'm too lazy to write fixed array resizing code. Its more efficient to write one that works with fixed arrays (for static stuff that doesn't change often) and linked lists (for the dynamic stuff like those bullets, because things can be added to and removed from linked lists without re-sorting or re-sizing an array internally). If you don't know the differences between linked lists and array storage, I recomend you look into them!

C# has an extensive library of data structures. You do not need to write a fixed array class, that is what List<> is. A linked list is a... LinkedList<>.

#8 blackbook   Members   -  Reputation: 110

Like
0Likes
Like

Posted 23 May 2011 - 04:45 AM

The App Hubs well explained Shooter tutorial covers all of the concepts you have a problem with.You could download the source code and take a look.It covers bullets, explosions, lists of active bullets etcIt's quite good.

#9 BeerNutts   Crossbones+   -  Reputation: 2943

Like
-1Likes
Like

Posted 23 May 2011 - 09:36 AM

Let me give you a simple answer.

Have a list (STL, for example: http://www.cplusplus.com/reference/stl/list/) and, when you're ship produces a bullet, add the bullet object to the list. In your main update function, loop through the list, updating the location of the bullet, then draw it. When the bullet should be destroyed (either because it hit something, or it is off the screen), remove it form the list, and delete it.

Worry about optimizing later (if needed, which, I bet you won't need for a simple game). Be careful about removing an object from a list while your traversing it.
My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#10 NEXUSKill   Members   -  Reputation: 458

Like
0Likes
Like

Posted 23 May 2011 - 09:56 AM

First thing you must keep in mind is that XNA is NOT a game engine, this is a common misconception, while XNA is designed to be used in game development specifically, it is merely a wrapper of DirectX and most operative system interactions to get you a more comfortable starting point than you would have on a bare c++ project, its just yet another abstraction layer.

Second, while it is true that you should not concentrate on optimization during the initial stages of a project, specially a small one, simply adding and removing projectiles from a list when shot and when hit is not just a matter of optimization, its bad design, objects that get created and destroyed very often MUST be pooled, usually because of the mere cost of instanciation, initialization and destruction of the objects, but in an environment like XNA which uses a garbage collector its all that much worse, such a high frequency - short lifespan object creation will quickly trash even the simplest of games.

Given the rate of fire and the range of a given type of projectile it is possible to calculate the max number of projectiles that can exist at any given time coming from the same source, this source is most likely to live much longer than its projectiles, therefore it must pre-instanciate and initialize all projectiles and then just re-use the same ones over and over again.

As for managing the actors themselves (ships, asteroids, enemies), you should usually create some kind of manager in charge of the decisions of adding or removing actors to the world, L4D called this the Director, other games implement this in the World, or Level object, but it is important to have some centralized management of these types of object.


Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272



#11 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 23 May 2011 - 10:48 AM

Hm, for the bullets, I guess I could just have a single "Bullet" class for each type of bullet, which is created once and not destroyed. Instead of handling a single instance of the bullet, it would instead have an array for the location and speed variables, and then when it's called to update or draw, it moves all the bullets at once. If one of the bullets needs to be destroyed, then I simply remove that location/speed from the list, bump all the entries above down a notch, and reduce the "bulletsExisting" variable. If I need a different type of bullet, then I simply make another type of this class, and that can handle all of those type of bullet. I don't need to store the ID of the ship that fired it, since friendly fire is just part of the game.

And yeah, having 6 or so different classes is completely unnecessary for the simple game I'm building right now, but I'm planning to develop this into a much more complicated game with many more different types of ships and objects flying around. For example each ship will have a list of Components, which need to be rendered but don't move in the traditional sense, instead being manually moved and rotated by the ship they're attached to. And then there's things like random space debris, space stations, background objects such as planets that move around slowly, etc.

#12 NEXUSKill   Members   -  Reputation: 458

Like
0Likes
Like

Posted 23 May 2011 - 11:13 AM

When you have many different "types" of the same object creating one class for each type is an obscenity, might work, but its very bad design. To create different types of the same object you should use data structures to create each "variant" of the object.

For instance, you have a normal gun bullet and a sniper rifle bullet (its a bad example, you'll likely use ray tracing instead of bullets for those weapons) the behavior is basically the same for both, what changes is the properties, such as base speed, damage and so on, the Projectile or Bullet class should implement all the behavior and take those properties from a data structure, you then create one data structure for pistol bullets, one for sniper bullets and only need to load one for each, when you shoot a pistol you create a new projectile and assign it the PistolBulletData structure by reference and when you use the sniper use the SniperBulletData, you'll have much less classes and load the data as content assets from config files or xml or whatever format making it much easier to change the values without changing the code.


Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272



#13 BeerNutts   Crossbones+   -  Reputation: 2943

Like
2Likes
Like

Posted 23 May 2011 - 12:02 PM

Second, while it is true that you should not concentrate on optimization during the initial stages of a project, specially a small one, simply adding and removing projectiles from a list when shot and when hit is not just a matter of optimization, its bad design, objects that get created and destroyed very often MUST be pooled, usually because of the mere cost of instanciation, initialization and destruction of the objects, but in an environment like XNA which uses a garbage collector its all that much worse, such a high frequency - short lifespan object creation will quickly trash even the simplest of games.



OP, like one posted said, you really should check the App Hubs, as they do this exact thing (and, believe it or not, they create and delete bullets on the fly)
App Hubs Link

That link above puts you at the start of the design, but a few pages in, it talks about adding bullets. it's a pretty good tutorial IMO.

NexusKill, Bad idea for a beginner. You're introducing concepts that he doesn't need to get into yet. You and I both (should) know this game won't have performance problems creating and deleting bullets fired at a high rate. He's messing around, let him get the jest of how these games operate. Once he understands things better and starts working on bigger games, he can look into these concepts.

Hell, why not go ahead and tell him to include spatial hashing in his collision detection, because the collision detection of checking every bullet and enemy is going to be SOOOO expensive. It's just bad design not too...right? (OP, note, that was sarcasm)

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#14 NEXUSKill   Members   -  Reputation: 458

Like
1Likes
Like

Posted 23 May 2011 - 02:06 PM

NexusKill, Bad idea for a beginner. You're introducing concepts that he doesn't need to get into yet. You and I both (should) know this game won't have performance problems creating and deleting bullets fired at a high rate. He's messing around, let him get the jest of how these games operate. Once he understands things better and starts working on bigger games, he can look into these concepts.

Hell, why not go ahead and tell him to include spatial hashing in his collision detection, because the collision detection of checking every bullet and enemy is going to be SOOOO expensive. It's just bad design not too...right? (OP, note, that was sarcasm)




Well... maybe I misunderstood the original post, but as I see it he already tried the brute force approach, it caused him trouble and is now looking for the proper way of doing things.
I suggested what in my experience has been the best way to tackle the issue (not that hard to implement btw) and he still has the liberty of choosing not to go with it, but at least he now knows it exists.


If he was asking how to improve collision checking I would probably mentioned spatial hashing.
Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272



#15 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 23 May 2011 - 11:33 PM

Just posting here to note that I ended up getting it all working. I also made my projectiles use a seperate ProjectileManager class, similar to what I stated above, more to make it easier to tell the game to draw everything than for any other reason.

Anyways my problem now is that I'm trying to get my ships to be able to move around autonomously, tracking their targets and shooting at them as appropriate. I've actually done this once before with a different game in a slightly different environment, and the solution I used back then was simply to find the angle from the ship to the target it wants to be moving to, and then rotate the ship slightly each step to turn towards it. The problem is, I can't seem to get the numbers to line up, causing my ship to just fly around randomly. Here's my code, which by my math SHOULD be working, but isn't:

rotationToTarget = NormalizeAngle(-(float)Math.Atan((AITarget.position.Y - position.Y) / (AITarget.position.X - position.X)) - (rotation - (float)(Math.PI / 2)));

            	Console.WriteLine(name + " Rotation: " + rotation);
            	Console.WriteLine(name + "Rotation to Target: " + rotationToTarget);

            	if (rotationToTarget > .03F)
            	{
                	TurnLeft();
            	}
            	else if (rotationToTarget < -.03F)
            	{
                	TurnRight();
            	}

protected float NormalizeAngle(float Angle)
    	{
        	float normalizedAngle = Angle;
        	while (normalizedAngle > (float)Math.PI)
        	{
            	normalizedAngle -= (float)(Math.PI * 2);
        	}
        	while (normalizedAngle < -(float)Math.PI)
        	{
            	normalizedAngle += (float)(Math.PI * 2);
        	}
        	return normalizedAngle;
    	}

The first is the code itself, the second simply takes a float, and then forces it to be between -PI and PI.

rotation is a variable that all of my ships have, which starts a 0 pointing straight down, then goes counter-clockwise up. It is in radians. position is the position of the object itself, tracked from the top left with positive going right and down. What should happen, is that this should give a value to rotationToTarget between -PI and PI, with 0 meaning that the target is straight ahead, a positive value meaning it's to the ship's left, and a negative value meaning it's to the ship's right. So, if the value is positive the ship turns left, and if it's negative then it'll turn right.

What instead happens is that the ships just seem to fly about randomly. Sometimes it even seems like one is following another, but not for any reason that I can figure out. I'm sure I've just got the formula wrong somewhere, but no matter how I tweak it it won't do what I want. Any ideas?

#16 NEXUSKill   Members   -  Reputation: 458

Like
0Likes
Like

Posted 24 May 2011 - 07:06 AM

You might want to take a read on Steering Behaviors and Flocking techniques, the second is built upon the first, and though it might not be specifically intended at your case, it might still give you a few cool ideas you can use.
Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272



#17 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 24 May 2011 - 10:57 AM

You might want to take a read on Steering Behaviors and Flocking techniques, the second is built upon the first, and though it might not be specifically intended at your case, it might still give you a few cool ideas you can use.


That's the kind of stuff I could be doing, but my problem is that I can't figure out how to get the formula to calculate the difference of two rotations.

#18 haegarr   Crossbones+   -  Reputation: 4310

Like
0Likes
Like

Posted 24 May 2011 - 11:57 AM

That's the kind of stuff I could be doing, but my problem is that I can't figure out how to get the formula to calculate the difference of two rotations.

What is the format of the rotations? Assuming that both rotations are given w.r.t. the same reference frame, then ...

* for matrices R1 and R2, the rotation from R1 to R2 is a matrix R like in (written using column vectors)
R * R1 = R2
so that the "difference" rotation is
R = R2 * R1-1

* for quaternions q1 and q2 similarly
q = q2 * q1-1

* for axis/angle pairs in more than 2 dimensions it would be best to convert them into quaternions (in 2 dimensional space the difference rotation is just the difference of the both angles)

* for Euler angles please convert them into rotation matrices

#19 AgentPaper   Members   -  Reputation: 109

Like
0Likes
Like

Posted 24 May 2011 - 01:02 PM


That's the kind of stuff I could be doing, but my problem is that I can't figure out how to get the formula to calculate the difference of two rotations.

What is the format of the rotations? Assuming that both rotations are given w.r.t. the same reference frame, then ...

* for matrices R1 and R2, the rotation from R1 to R2 is a matrix R like in (written using column vectors)
R * R1 = R2
so that the "difference" rotation is
R = R2 * R1-1

* for quaternions q1 and q2 similarly
q = q2 * q1-1

* for axis/angle pairs in more than 2 dimensions it would be best to convert them into quaternions (in 2 dimensional space the difference rotation is just the difference of the both angles)

* for Euler angles please convert them into rotation matrices


yeebs, that all just went right over my head. Matrices? Quaternions? I thought this was a trig thing, not advanced calculus or whatever that is.

#20 haegarr   Crossbones+   -  Reputation: 4310

Like
0Likes
Like

Posted 25 May 2011 - 03:45 AM

...
yeebs, that all just went right over my head. Matrices? Quaternions? I thought this was a trig thing, not advanced calculus or whatever that is.

In 2D you have a single angle, a fixed rotation axis (so to say; in fact it is a fixed plane of rotation where the said axis is orthogonal to), and a free choosable origin (where the axis passes through, of course). Using 0 (i.e. the point where all co-ordinates are 0) as origin, the formulas for rotation look like
x' := x cos( a ) + y sin( a )
y' := -x sin( a ) + y cos( a )
If you write exactly this as matrix expression (using column vectors), you'll have
p' := R( a ) * p
so there is really nothing advanced at this level. However, as soon as you start to concatenate rotations, or (as in your case) want to compute rotational differences, matrix expression are much more handy than the scalar expressions using trigonometry terms. This gets more and more true when you consider other transformations, too, and when you work in spaces with more dimensions. I suggest you to look at matrix math (not in the broad sense but trimmed for the needs of CG) because after mastered the 3 or 4 hurdles geometric things gets much easier to deal with. Not to mention that there are several packages out there that already implement matrices and vectors for your convenience; you just have to learn how to use the stuff.

If you need help, please tell us how the both original rotations are defined, and how the difference shall look like to be used further.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS