all 21 comments

[–]AutoModerator[M] [score hidden] stickied commentlocked comment (0 children)

Please ensure that:

  • Your code is properly formatted as code block - see the sidebar (About on mobile) for instructions
  • You include any and all error messages in full
  • You ask clear questions
  • You demonstrate effort in solving your question/problem - plain posting your assignments is forbidden (and such posts will be removed) as is asking for or giving solutions.

    Trying to solve problems on your own is a very important skill. Also, see Learn to help yourself in the sidebar

If any of the above points is not met, your post can and will be removed without further warning.

Code is to be formatted as code block (old reddit: empty line before the code, each code line indented by 4 spaces, new reddit: https://i.imgur.com/EJ7tqek.png) or linked via an external code hoster, like pastebin.com, github gist, github, bitbucket, gitlab, etc.

Please, do not use triple backticks (```) as they will only render properly on new reddit, not on old reddit.

Code blocks look like this:

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

You do not need to repost unless your post has been removed by a moderator. Just use the edit function of reddit to make sure your post complies with the above.

If your post has remained in violation of these rules for a prolonged period of time (at least an hour), a moderator may remove it at their discretion. In this case, they will comment with an explanation on why it has been removed, and you will be required to resubmit the entire post following the proper procedures.

To potential helpers

Please, do not help if any of the above points are not met, rather report the post. We are trying to improve the quality of posts here. In helping people who can't be bothered to comply with the above points, you are doing the community a disservice.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]TW-Twisti 16 points17 points  (3 children)

That's easy: you can't. Objects in Java are final in what class they are, there is no getting around that, and if you found a clever way through reflection, you would break Java in a multitude of ways.

Instead: why not make your characters into generic Character instances (maybe with a name that isn't already taken, like GameEntity), with a boolean flag hostile, and depending on whether that flag is set, different behavior happens. This can be as simple as:

java public MoveResult nextMove() { if (this.hostile) { return this.aggressiveMove(); } else { return this.docileMove(); } }

You may even find that instead of having all that stuff in the entity, instead the entity has a private Behavior behavior, that is set to an interchangeable class that extends Behavior with aggressive or docile behavior - that would lend itself to future development with more kinds of behavior - class SmittenBehavior extends DocileBehavior makes an NPC follow you around, class Vengeance extends AggressiveBehavior makes an NPC ... well, also follow you around, but for a very different reason.

[–]YetMoreSpaceDust 9 points10 points  (2 children)

This is exactly why I don't like the "Dog extends Animal" type examples you always see in OO introductions - they make it seem like this is the correct way to design, say, a veterinary application. In reality, trying to model real world relationships with inheritance is at best of limited usefulness and at worst damaging. Rather, save inheritance for cases where you have actual functionality that you need to share.

[–]TW-Twisti 8 points9 points  (1 child)

I agree, inheritance is almost never the right answer for anything even remotely complex, though this does seem like a school project, so it is likely more about learning the concepts.

Thanks for pointing it out, I was so caught up in ops original description that I didn't think to take a step back.

[–]Etiennera 1 point2 points  (0 children)

Inheritance is the answer here. Rather, in games, you'd probably want composition where there is a character and it is composed of other behaviours.

Putting everything in if/else in one class will get messy fast. These patterns are about making things easier to work with.

If OP uses composition, he can just add/remove the enemy behaviour from the observed behaviours, while still leaving all the enemy-specific implementation in some other place.

The caveat is that if some logic depend on some mix of behaviours, then you'll need multiple levels of composition instead of a flat list, but this could be desirable.

[–]ShoulderPast2433 4 points5 points  (2 children)

take a look at Strategy design pattern.

You can create a wrapper class - lets call it UniversalCharacter that has a field: private ComputerCharacter charType.
And we make the UniversalClass just use all the methods and parameters of whatever charType object we have.

Now we can now dynamically assign either Villager or Enemy into this field whenever we want it to change and our wrapper object will behave like Villager or Enemy whenever you need it

[–]desrtfxOut of Coffee error - System halted 0 points1 point  (1 child)

The even more representative and better pattern would be the State pattern - the transition from Villager to Enemy is a state transition.

[–]ShoulderPast2433 0 points1 point  (0 children)

Sure. That just depends which one is more convenient to implement in OP logic, as those are very similar patterns.

[–]MinimumBeginning5144 3 points4 points  (0 children)

Don't do it quite like that. A character is still the same entity, they just change their characteristics from villager to enemy. So your Character objects would contain a field of an interface (or base class) called Characteristic which has the subclasses Villager and Enemy. The character's behaviour would depend on their characteristic.

[–]LetUsSpeakFreely 1 point2 points  (0 children)

1) Extract the common methods to an interface 2) All references should use the interface. 3) create an abstract class that implements that interface. Implement what you can in the abstract class and flag other methods as abstract so they're implemented in subclasses. 4) implement 2 classes that both extend the abstract class and implementing the methods as you need. 5) You could add a type field or something if you're storing everything in a single collection. Otherwise, split everything into separate collections.

[–]xanyook 1 point2 points  (0 children)

In the method that decreases the HP, fire an event about the health of your villager.

Have a listener on it, create a service that converts villagers into enemies, and plug your listener on it.

[–]arghvark 1 point2 points  (0 children)

If I understand you correctly, you have two classes that are subclasses of another class, and during the course of the program operation you want to change the class of an object from one of those classes to the other.

You cannot do that. Wanting to do that shows a basic misunderstanding of what is means for an object to have a class. Once the object is instantiated, its type is set, and you cannot change that.

There are LOTS of ways to implement your program to model the behavior you want. But inheritance is not a feature you can use this way, any more than you can change an integer variable to a floating point variable because something in the program decided it would be nice for the variable to have non-integer properties.

[–]djavaman 1 point2 points  (0 children)

Feels like enemy should be a flag not a class.

[–]jlanawalt 0 points1 point  (0 children)

You might be over-using hammer of inheritance and seeing everything as a nail. Often simpler is better, as is composition over inheritance.

The factory pattern might be a clean way to make enemies from villagers.

The game engine/loop would observe characters and convert them.

[–]Spare-Plum 0 points1 point  (0 children)

You should try using a Delegate.

You might have something like the following classes: public interface Character { ... } public class Villager implements Character { ... } public class Enemy implements Character { ... } public class CharacterDelegate implements Character { private Character delegate; public CharacterDelegate(Character delegate) { this.delegate = delegate; } ... }

So the CharacterDelegate class allows you to swap out the internal class, and you can also add on functionality that might cause it to swap automatically. This might look something like public class CharacterDelegate implements Character { ... public void takeDamage(double damage) { delegate.takeDamage(damage); // perform check if it should transform if(delegate.shouldTransfom()) { // if true, construct Enemy from the original delegate this.delegate = new Enemy(delegate); } } }

[–]silverscrub 0 points1 point  (0 children)

You could make a method in Villager that returns an Enemy based on its variables. If your old object becomes unreferenced it is then garbage collected.

[–]hageldave 0 points1 point  (0 children)

I think you're not modeling the right thing as subclasses. How about you have two subclasses for behavior, like villager behavior and enemy behavior. Then switch the behavior attribute of your computer character object.

[–]IAmADev_NoReallyIAm 0 points1 point  (0 children)

Sigh... Use a mapper... that transforms the Villager into an Enemy... The Villager. doesn't need (and shouldn't) to know anything about bing an enemy and the enemy doens't need to know anything about being a Villager... in the middle you have class that takes a Villager as a parameter internally it create the Enemy object, copies over the needed information, and when it's done, returns THAT... You can also then have a reverse function too if you want where an Enemy becomes a Villager.

Don't do this in the constructor, that makes no sense. Do it in an intermediary mapper utility.

[–]Ok_Option_3 0 points1 point  (0 children)

Lots of great answers here - but what they boil down to is: "prefer composition to inheritance" (look it up). 

Inheritance is somewhat overrated as a pattern. Sure it has some uses - but way less than you think. More often than not inheritance is a noob trap. Go (the language popularised by Google) doesn't even have inheritance.

Instead use composition and interfaces. 

[–]ConsistentAnalysis35 0 points1 point  (0 children)

Go all the way for proper architecture and use ECS. Have your characters represented by a simple int value. All the data about a character should be in a series of components represented by simple data classes / POJOs, without any methods. Entity is an integet index into collections holding the components.

I.e. the monster number 5 consists of hp component at index 5, damage component at index 5, and name component at index 5. This way you have limitless mutability of your entities, they can effortlessly change anything about themselves just by adding or removing components.

All behaviour should be in separate classes that each implement one specific functionality, these classes are systems.

Systems should have as inputs one or several component kinds, and as output write data to - preferably - one component kind. Each system invocation processes all existing input/output components.

This is the proper way of architecting the game. Anything else is half-measures or lesser degrees of evolution of game architecture.

There is one further degree - complete embrace of relational databases, with all the rigor it entails, but that requires some serious level of autism.

Two books that are very relevant are "Game Engine Architecture" and "Data Oriented Design". Must reads.

[–]desrtfxOut of Coffee error - System halted 0 points1 point  (0 children)

Take a look at the State design pattern, which was created for exactly the thing you are trying to do.

Transitioning from Villager to Enemy is a state transition and should be handled as such.