-1

I'm kinda new to Java, and I'm trying to write an RPG of sorts.

Now, in the game the player character would have skills. These could be very diverse, from hurting enemies to healing the player and a lot of other things. It'd make sense to create a Skill class with an abstract applyEffect() method, to be defined on each particular skill.

However, I cannot have a non-abstract class containing abstract methods, and every skill should be an object of the Skill class, so it can't be abstract. The obvious solution is to make the Skill class abstract and create a subclass for every single skill, and then instantiate that into an object to use. This approach seems a bit redundant. Is there anything else I could conceivably do in this situation?

EDIT: While we're at it, if I want an object that will appear a single time with standard variables, is there any workaround to making a class just for that one object and then instantiating it?

Sotiris
  • 53
  • 11
  • 1
    Abstract methods must be implemented in a subclass. Without subclasses, how are you going to implement them? – user253751 Mar 19 '16 at 21:48
  • You need an abstract Skill class. Each skill or skill type or whatever can extend the Skill class and have a concrete applyEffect() method. – lhoworko Mar 19 '16 at 21:49
  • You're right, but what I'm asking is if there's any workaround to that. It seems redundant to create, say, a DamageSkill1 subclass and then have to instantiate it in a DamageSkill1 object, since the latter will appear in only one instance. It kind of beats the purpose of the class. – Sotiris Mar 19 '16 at 21:50
  • You have to. And its not redundant. Its perfectly fine to do this. Minecraft does it the exact same way. – Mario Dekena Mar 19 '16 at 21:51
  • 1
    You could try anonymous classes, if you don't like creating named ones. They will look nastier in stack traces though. – sfThomas Mar 19 '16 at 21:52
  • Let me get you straight. Do you have players with several skills, but they all have different skills? For example a doctor can have the skills of "heal" and "runFast" and the murderer can have the skills of "attack" and "hide"? – RaminS Mar 19 '16 at 21:52
  • If youre skills are not too different from each other you might be able to get away with some fields. You could have a field for damage, flying speed and texture for example. You could read these Spell descriptions from file too. This approach is very limited though. – Mario Dekena Mar 19 '16 at 21:55
  • @Gendarme I'm not sure why you ask this. I have only one player character, but he can use various skills. So, the player, depending on their choices, can use any of the "heal", "runFast", "attack" and "hide" skills. – Sotiris Mar 19 '16 at 21:55
  • @MarioDekena that won't work, I'm afraid. The skills are pretty diverse. Thanks for your input though! – Sotiris Mar 19 '16 at 21:56
  • @sfThomas anonymous classes would work, but they're only local, aren't they? – Sotiris Mar 19 '16 at 21:57
  • Not sure what you mean by 'local' - you can use such a class only once, but then again, if your skills are stateless, it is okay for them to be singletons, isn't it? – sfThomas Mar 19 '16 at 21:59
  • The "modern" aproach for this is an Entity Component System. Its very flexible and you dont have subclasses. But its a little overkill in your case i think. http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013 – Mario Dekena Mar 19 '16 at 21:59
  • @sfThomas Ugh, it may just be the java noob inside me talking. I'll try to implement your suggestion ^_^ – Sotiris Mar 19 '16 at 22:00

3 Answers3

1

How about this:

public abstract class Skill {
    public abstract void applyEffect();
}


... somewhere else ... 
Skill dig = new Skill() {
    @Override
    public void applyEffect() {
        doSomeDigging();
    }
};

This one still creates a subclass in the background, but you might like it better.

sfThomas
  • 1,895
  • 2
  • 18
  • 26
  • This is an anonymous class, right? It does seem the sharpest way to go about it, but I think they're only local. That would be a great hindrance since I need to pass them around classes and packages. – Sotiris Mar 19 '16 at 21:59
  • You can pass around the instance, as it conforms to the Skill class. But you might want to clarify your requirements in the question. – sfThomas Mar 19 '16 at 22:00
1

I would not write skills (like 'heal' and 'hide') as classes. I view classes as objects (players), and methods as abilities (skills). Skills like 'heal' or 'hide' are clearly better as methods than classes.

I would simply have one class that has all methods, but only the selected ones are available for use. Having the skills as enums isn't a bad idea either.

enum Skill {
    HEAL, HIDE, ATTACK, THROW
}

class Player {
    boolean canHeal = false;
    boolean canHide = false;
    boolean canAttack = false;
    boolean canThrow = false;

    Player(Skill[] skills) {
        for(skill : skills) {
            switch(skill) {
                case Skills.HEAL: canHeal = true;
                break;

                case Skills.HIDE: canHide = true;
                break;

                case Skills.ATTACK: canAttack = true;
                break;

                case Skills.THROW: canThrow = true;
                break;

                default: //error
            }
        }
    }

    void heal() {
        [...]
    }
    void hide() {
        [...]
    }
    void attack() {
        [...]
    }
    void throw() {
        [...]
    }
    boolean canHeal() {
        return canHeal;
    }
    boolean canHide() {
        return canHide;
    }
    boolean canAttack() {
        return canAttack;
    }
    boolean canThrow() {
        return canThrow;
    }
}

Now the players can be restricted to only use the methods that should be available to them. What I would do is probably to write a GameHandler-class to take care of everything and do all the checking there.

RaminS
  • 2,208
  • 4
  • 22
  • 30
  • An interesting approach. Probably better than mine. What I'm afraid of is that if the skills measure up to at least, say, 100 (scattered across multiple player proffessions), that one player class would get humongous and unwieldy, as would all the canUseSkill booleans. Then again, I could dump all this in another class, as you say. I think your approach is the most logical one, thanks ^_^ – Sotiris Mar 19 '16 at 23:02
  • @Sotos You could also use interfaces if it gets too complicated. – RaminS Mar 19 '16 at 23:03
0

i would use enums also, you can stuff a bunch of login in them. the maps let each player have whatever skills and stats they need. you can nest enums like this or that.

import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
public class So36107587 {
    enum Stat {
        food,health,magic;
    }
    enum Skill {
        heal,hurt,hunt;
        static void apply(Skill skill,double amount,Player player) {
            double a=amount*random.nextDouble(),x;
            switch(skill) {
                case heal:
                    x=player.stats.get(Stat.health);
                    player.stats.put(Stat.health,a+x);
                    break;
                case hurt:
                    x=player.stats.get(Stat.health);
                    player.stats.put(Stat.health,a-x);
                    break;
                case hunt:
                    x=player.stats.get(Stat.food);
                    player.stats.put(Stat.food,a+x);
                    break;
            }
        }
        static final Random random=new Random();
    }
    static class Player {
        Player() {
            init();
        }
        void init() {
            for(Stat stat:Stat.values())
                stats.put(stat,1.);
            for(Skill skill:Skill.values())
                skills.put(skill,1.);
        }
        void apply(Skill skill,Player player) {
            Skill.apply(skill,skills.get(skill),player);
        }
        @Override public String toString() {
            return ""+skills+" "+stats;
        }
        final Map<Stat,Double> stats=new TreeMap<>();
        final Map<Skill,Double> skills=new TreeMap<>();
    }
    public static void main(String[] args) {
        Player player=new Player();
        System.out.println(player);
        player.apply(Skill.heal,player);
        System.out.println(player);
        player.apply(Skill.hunt,player);
        System.out.println(player);
    }
}
Community
  • 1
  • 1
Ray Tayek
  • 9,841
  • 8
  • 50
  • 90