Adds a new type of NPC
This commit is contained in:
		
							
								
								
									
										182
									
								
								src/inf101/v18/rogue101/enemies/Girl.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/inf101/v18/rogue101/enemies/Girl.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
			
		||||
package inf101.v18.rogue101.enemies;
 | 
			
		||||
 | 
			
		||||
import inf101.v18.grid.GridDirection;
 | 
			
		||||
import inf101.v18.rogue101.game.IGame;
 | 
			
		||||
import inf101.v18.rogue101.objects.IItem;
 | 
			
		||||
import inf101.v18.rogue101.objects.INonPlayer;
 | 
			
		||||
import inf101.v18.rogue101.shared.NPC;
 | 
			
		||||
import inf101.v18.rogue101.states.Age;
 | 
			
		||||
import inf101.v18.rogue101.states.Occupation;
 | 
			
		||||
import inf101.v18.rogue101.states.Personality;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
public class Girl implements INonPlayer {
 | 
			
		||||
    private String name = randomName();
 | 
			
		||||
    private Age age = Age.getRandom();
 | 
			
		||||
    private Personality personality = Personality.getRandom();
 | 
			
		||||
    private Occupation occupation = Occupation.getRandom();
 | 
			
		||||
    private int maxhp;
 | 
			
		||||
    private int hp;
 | 
			
		||||
    private int attack;
 | 
			
		||||
    private int defence;
 | 
			
		||||
    private  int visibility;
 | 
			
		||||
    private IItem equipped;
 | 
			
		||||
    private static Random random = new Random();
 | 
			
		||||
 | 
			
		||||
    public Girl() {
 | 
			
		||||
        setStats();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setStats() {
 | 
			
		||||
        switch (age) {
 | 
			
		||||
            case TODDLER:
 | 
			
		||||
                maxhp = 100;
 | 
			
		||||
                attack = 10;
 | 
			
		||||
                defence = 50;
 | 
			
		||||
                visibility = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case CHILD:
 | 
			
		||||
                maxhp = 150;
 | 
			
		||||
                attack = 20;
 | 
			
		||||
                defence = 40;
 | 
			
		||||
                visibility = 2;
 | 
			
		||||
                break;
 | 
			
		||||
            case TEEN:
 | 
			
		||||
                maxhp = 200;
 | 
			
		||||
                attack = 25;
 | 
			
		||||
                defence = 30;
 | 
			
		||||
                visibility = 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case ADULT:
 | 
			
		||||
                maxhp = 250;
 | 
			
		||||
                attack = 30;
 | 
			
		||||
                defence = 20;
 | 
			
		||||
                visibility = 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case ELDER:
 | 
			
		||||
                maxhp = 200;
 | 
			
		||||
                attack = 15;
 | 
			
		||||
                defence = 35;
 | 
			
		||||
                visibility = 2;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        if (occupation == Occupation.MAGE) {
 | 
			
		||||
            attack += 10; //Knights are quite powerful.
 | 
			
		||||
        }
 | 
			
		||||
        if (occupation == Occupation.MAGE) {
 | 
			
		||||
            attack += 5; // Mages have lesser range than bowsmen, but more damage.
 | 
			
		||||
        }
 | 
			
		||||
        maxhp += (int)(random.nextGaussian() * 10);
 | 
			
		||||
        hp = maxhp;
 | 
			
		||||
        attack += (int)(random.nextGaussian() * 5);
 | 
			
		||||
        defence += (int)(random.nextGaussian() * 5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String randomName() {
 | 
			
		||||
        //TODO: Choose from a list of names, or generate name.
 | 
			
		||||
        return "Girl";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void doTurn(IGame game) {
 | 
			
		||||
        if (equipped == null) {
 | 
			
		||||
            //TODO: Check if there is a item on the ground to pick up.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        boolean attack = false;
 | 
			
		||||
        switch (personality) {
 | 
			
		||||
            case CALM:
 | 
			
		||||
                if (hp < maxhp) {
 | 
			
		||||
                    attack = true;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case AFRAID:
 | 
			
		||||
                if (!NPC.flee(game)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                } else {
 | 
			
		||||
                    attack = true;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case AGRESSIVE:
 | 
			
		||||
                attack = true;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        if (attack) {
 | 
			
		||||
            switch (occupation) {
 | 
			
		||||
                case KNIGHT:
 | 
			
		||||
                    if (NPC.tryAttack(game)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case MAGE:
 | 
			
		||||
                    if (NPC.tryAttackRanged(game, 2)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                case BOWSMAN:
 | 
			
		||||
                    if (NPC.tryAttackRanged(game, 4)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<GridDirection> possibleMoves = game.getPossibleMoves();
 | 
			
		||||
        if (!possibleMoves.isEmpty()) {
 | 
			
		||||
            Collections.shuffle(possibleMoves);
 | 
			
		||||
            game.move(possibleMoves.get(0));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getAttack() {
 | 
			
		||||
        return attack;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getDamage() {
 | 
			
		||||
        return 5;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getCurrentHealth() {
 | 
			
		||||
        return hp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getDefence() {
 | 
			
		||||
        return defence;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getMaxHealth() {
 | 
			
		||||
        return maxhp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getSize() {
 | 
			
		||||
        return 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getSymbol() {
 | 
			
		||||
        return "G";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getVision() {
 | 
			
		||||
        return visibility;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int handleDamage(IGame game, IItem source, int amount) {
 | 
			
		||||
        hp -= amount;
 | 
			
		||||
        return amount;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +27,6 @@ public class Rabbit implements INonPlayer {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//TODO: It seems the rabbit moves twice. We don't want that.
 | 
			
		||||
		if (NPC.tryAttack(game)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
@@ -39,8 +38,7 @@ public class Rabbit implements INonPlayer {
 | 
			
		||||
				if (eaten > 0) {
 | 
			
		||||
					System.out.println("ate carrot worth " + eaten + "!");
 | 
			
		||||
					food += eaten;
 | 
			
		||||
					game.displayMessage("You hear a faint crunching (" + getName() + " eats " + item.getArticle() + " "
 | 
			
		||||
							+ item.getName() + ")");
 | 
			
		||||
					game.displayMessage("You hear a faint crunching (" + getName() + " eats " + item.getArticle() + " " + item.getName() + ")");
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import inf101.v18.grid.GridDirection;
 | 
			
		||||
import inf101.v18.grid.IGrid;
 | 
			
		||||
import inf101.v18.grid.ILocation;
 | 
			
		||||
import inf101.v18.rogue101.Main;
 | 
			
		||||
import inf101.v18.rogue101.enemies.Girl;
 | 
			
		||||
import inf101.v18.rogue101.examples.Carrot;
 | 
			
		||||
import inf101.v18.rogue101.items.Manga;
 | 
			
		||||
import inf101.v18.rogue101.examples.Rabbit;
 | 
			
		||||
@@ -258,6 +259,7 @@ public class Game implements IGame {
 | 
			
		||||
		itemFactories.put("C", Carrot::new);
 | 
			
		||||
		itemFactories.put("R", Rabbit::new);
 | 
			
		||||
		itemFactories.put("M", Manga::new);
 | 
			
		||||
		itemFactories.put("G", Girl::new);
 | 
			
		||||
		itemFactories.put(".", Dust::new);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -435,7 +437,21 @@ public class Game implements IGame {
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public ILocation rangedAttack(GridDirection dir, IItem target) {
 | 
			
		||||
		return currentLocation;
 | 
			
		||||
		ILocation loc = currentLocation;
 | 
			
		||||
		int damage = (currentActor.getAttack() + random.nextInt(20) + 1)
 | 
			
		||||
				/ loc.gridDistanceTo(map.getLocation(target)); //Close attacks will take more damage.
 | 
			
		||||
		if (damage >= target.getDefence() + 10) {
 | 
			
		||||
			int actualDamage = target.handleDamage(this, target, damage);
 | 
			
		||||
			formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
 | 
			
		||||
		} else {
 | 
			
		||||
			displayMessage("The attack missed.");
 | 
			
		||||
		}
 | 
			
		||||
		map.clean(loc);
 | 
			
		||||
		if (target.isDestroyed() && map.has(currentLocation.go(dir), target)) {
 | 
			
		||||
			return move(dir);
 | 
			
		||||
		} else {
 | 
			
		||||
			return currentLocation;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,40 @@ public class NPC {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs a ranged attack on the closest actor (if any) observable by the current actor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param game      An IGame object
 | 
			
		||||
     * @param range     The range of the equipped weapon
 | 
			
		||||
     * @return          True if an attack or a move towards an enemy was successful. False otherwise
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean tryAttackRanged(IGame game, int range) {
 | 
			
		||||
        // Ranged attacks don't care about walls.
 | 
			
		||||
        List<ILocation> neighbours = game.getMap().getNeighbourhood(game.getLocation(), game.getActor().getVision());
 | 
			
		||||
        for (ILocation neighbour : neighbours) {
 | 
			
		||||
            if (game.getMap().hasActors(neighbour)) {
 | 
			
		||||
                ILocation current = game.getLocation();
 | 
			
		||||
                GridDirection dir = game.locationDirection(current, neighbour);
 | 
			
		||||
                IActor actor = game.getMap().getActors(neighbour).get(0); //We assume there is only one actor.
 | 
			
		||||
                if (current.gridDistanceTo(neighbour) <= range && !actor.isDestroyed()) {
 | 
			
		||||
                    game.rangedAttack(dir, actor);
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else if (game.canGo(dir)) {
 | 
			
		||||
                    game.move(dir);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tries to find and reach an item of a specified class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param game      An IGame object
 | 
			
		||||
     * @param element   The class of the objects we are looking for
 | 
			
		||||
     * @return          True if an appropriate item was found in the range of the current actor. False otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean trackItem(IGame game, Class<?> element) {
 | 
			
		||||
        List<ILocation> neighbours = game.getVisible();
 | 
			
		||||
        for (ILocation neighbour : neighbours) {
 | 
			
		||||
@@ -55,4 +89,31 @@ public class NPC {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean flee(IGame game) {
 | 
			
		||||
        List<ILocation> neighbours = game.getVisible();
 | 
			
		||||
        for (ILocation neighbour : neighbours) {
 | 
			
		||||
            if (game.getMap().hasActors(neighbour)) {
 | 
			
		||||
                ILocation current = game.getLocation();
 | 
			
		||||
                GridDirection dir = reverseDir(game.locationDirection(current, neighbour));
 | 
			
		||||
                if (game.canGo(dir)) {
 | 
			
		||||
                    game.move(dir);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static GridDirection reverseDir(GridDirection dir) {
 | 
			
		||||
        if (dir == GridDirection.SOUTH)  {
 | 
			
		||||
            return GridDirection.NORTH;
 | 
			
		||||
        } else if (dir == GridDirection.NORTH) {
 | 
			
		||||
           return GridDirection.SOUTH;
 | 
			
		||||
        } else if (dir == GridDirection.WEST) {
 | 
			
		||||
            return GridDirection.EAST;
 | 
			
		||||
        } else {
 | 
			
		||||
            return GridDirection.WEST;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/inf101/v18/rogue101/states/Age.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/inf101/v18/rogue101/states/Age.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
package inf101.v18.rogue101.states;
 | 
			
		||||
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
public enum Age {
 | 
			
		||||
    TODDLER, CHILD, TEEN, ADULT, ELDER;
 | 
			
		||||
 | 
			
		||||
    private static Random random = new Random();
 | 
			
		||||
 | 
			
		||||
    public static Age getRandom() {
 | 
			
		||||
        return values()[random.nextInt(values().length)];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/inf101/v18/rogue101/states/Occupation.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/inf101/v18/rogue101/states/Occupation.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
package inf101.v18.rogue101.states;
 | 
			
		||||
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
public enum Occupation {
 | 
			
		||||
    KNIGHT, MAGE, BOWSMAN;
 | 
			
		||||
 | 
			
		||||
    private static Random random = new Random();
 | 
			
		||||
 | 
			
		||||
    public static Occupation getRandom() {
 | 
			
		||||
        return values()[random.nextInt(values().length)];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/inf101/v18/rogue101/states/Personality.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/inf101/v18/rogue101/states/Personality.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
package inf101.v18.rogue101.states;
 | 
			
		||||
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
public enum Personality {
 | 
			
		||||
    AGRESSIVE, CALM, AFRAID;
 | 
			
		||||
 | 
			
		||||
    private static Random random = new Random();
 | 
			
		||||
 | 
			
		||||
    public static Personality getRandom() {
 | 
			
		||||
        return values()[random.nextInt(values().length)];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user