Improvements

Randomized Girl damage
Better attack range calculation
Fixes some errors
Better checking for which weapon was used
Weapons now increase attack
Manga is now consumable
Adds a missing wall
Stops calling consumables potions
Better player stats
Removes hp numbers
This commit is contained in:
2018-03-21 18:24:13 +01:00
parent ab31b9b4e0
commit 781f893226
13 changed files with 204 additions and 70 deletions

View File

@ -25,7 +25,8 @@ public class Girl implements INonPlayer {
private int hp;
private int attack;
private int defence;
private int visibility;
private int visibility;
private int damage;
private final Backpack backpack = new Backpack();
private static final Random random = new Random();
private List<Class<?>> validItems;
@ -42,30 +43,35 @@ public class Girl implements INonPlayer {
maxhp = 30;
attack = 10;
defence = 50;
damage = 1 + random.nextInt(5);
visibility = 1;
break;
case CHILD:
maxhp = 50;
attack = 20;
defence = 40;
damage = 2 + random.nextInt(5);
visibility = 2;
break;
case TEEN:
maxhp = 70;
attack = 25;
defence = 30;
damage = 3 + random.nextInt(5);
visibility = 3;
break;
case ADULT:
maxhp = 100;
attack = 30;
defence = 20;
damage = 4 + random.nextInt(5);
visibility = 4;
break;
case ELDER:
maxhp = 70;
attack = 15;
defence = 35;
damage = 3 + random.nextInt(5);
visibility = 2;
break;
}
@ -169,11 +175,11 @@ public class Girl implements INonPlayer {
}
break;
case MAGE:
if (NPC.tryAttack(game, 2, Attack.MAGIC)) {
if (NPC.tryAttack(game, getVision() / 2 + getVision() % 2 == 0 ? 0 : 1, Attack.MAGIC)) {
return true;
}
case BOWSMAN:
if (NPC.tryAttack(game, 4, Attack.RANGED)) {
if (NPC.tryAttack(game, getVision(), Attack.RANGED)) {
return true;
}
}
@ -213,7 +219,7 @@ public class Girl implements INonPlayer {
@Override
public int getDamage() {
return 5;
return damage;
}
@Override

View File

@ -20,7 +20,7 @@ import inf101.v18.rogue101.Main;
import inf101.v18.rogue101.enemies.Boss;
import inf101.v18.rogue101.enemies.Girl;
import inf101.v18.rogue101.examples.Carrot;
import inf101.v18.rogue101.examples.Manga;
import inf101.v18.rogue101.items.Manga;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.examples.Rabbit;
import inf101.v18.rogue101.map.GameMap;
@ -67,7 +67,7 @@ public class Game implements IGame {
private final ITurtle painter;
private final Printer printer;
private int numPlayers = 0;
boolean won = false;
private boolean won = false;
public Game(Screen screen, ITurtle painter, Printer printer) {
this.painter = painter;
@ -91,7 +91,7 @@ public class Game implements IGame {
map.add(map.getLocation( 3, 3), new Player()); //We must be sure there is never more than one player
// Prints some helpful information.
String[] info = {"Controls:", "WASD or arrow keys for movement", "E to pick up an item", "Q to drop an item", "1-0 to choose an item (10=0)", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack", "C to use a potion"};
String[] info = {"Controls:", "WASD or arrow keys for movement", "E to pick up an item", "Q to drop an item", "1-0 to choose an item (10=0)", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack", "C to use a consumable"};
for (int i = 0; i < info.length; i++) {
this.printer.printAt(Main.COLUMN_RIGHTSIDE_START, 1 + i, info[i]);
}
@ -125,18 +125,46 @@ public class Game implements IGame {
map.add(currentLocation, item);
}
/**
* Goes up one floor if possible.
*/
public void goUp() {
if (currentLVL + 1 < maps.size()) {
map = maps.get(++currentLVL);
if (!map.has(currentLocation, currentActor)) {
map.add(currentLocation, currentActor);
}
actors = new ArrayList<>();
displayMessage("You went up the stairs");
}
}
/**
* Goes down one floor if possible.
*/
public void goDown() {
if (currentLVL > 0) {
map = maps.get(--currentLVL);
if (!map.has(currentLocation, currentActor)) {
map.add(currentLocation, currentActor);
}
actors = new ArrayList<>();
displayMessage("You went down the stairs");
}
}
/**
* Calculates the attack of an IActor based on equipped items.
*
* @return The attack
*/
private int getAttack() {
int damage = currentActor.getAttack() + random.nextInt(20) + 1;
IBuffItem buff = (IBuffItem)currentActor.getItem(IBuffItem.class);
if (buff != null) {
damage += buff.getBuffDamage();
private int getAttack(Attack type) {
int attack = currentActor.getAttack() + random.nextInt(20) + 1;
IWeapon weapon = NPC.getWeapon(type, currentActor);
if (weapon != null) {
attack += weapon.getWeaponAttack();
}
return damage;
return attack;
}
/**
@ -155,42 +183,23 @@ public class Game implements IGame {
return defence;
}
public void goUp() {
if (currentLVL + 1 < maps.size()) {
map = maps.get(++currentLVL);
if (!map.has(currentLocation, currentActor)) {
map.add(currentLocation, currentActor);
}
actors = new ArrayList<>();
displayMessage("You went up the stairs");
}
}
public void goDown() {
if (currentLVL > 0) {
map = maps.get(--currentLVL);
if (!map.has(currentLocation, currentActor)) {
map.add(currentLocation, currentActor);
}
actors = new ArrayList<>();
displayMessage("You went down the stairs");
}
}
/**
* Gets the damage done against the current target.
*
* @param target The target to evaluate.
* @return The damage done to the target.
*/
private int getDamage(IItem target) {
private int getDamage(IItem target, Attack type) {
int damage = currentActor.getDamage();
IWeapon weapon = (IWeapon)currentActor.getItem(IWeapon.class);
IWeapon weapon = NPC.getWeapon(type, currentActor);
if (weapon != null) {
damage += weapon.getWeaponDamage();
}
IActor actor = (IActor) target;
IBuffItem item = (IBuffItem) actor.getItem(IBuffItem.class);
IBuffItem buff = (IBuffItem)currentActor.getItem(IBuffItem.class);
if (buff != null) {
damage += buff.getBuffDamage();
}
IBuffItem item = (IBuffItem)((IActor)target).getItem(IBuffItem.class);
if (item != null) {
damage -= item.getBuffDamageReduction();
}
@ -209,8 +218,8 @@ public class Game implements IGame {
} else {
NPC.playSound("audio/Realistic_Punch-Mark_DiAngelo-1609462330.wav");
}
if (getAttack() >= getDefence(target)) {
int actualDamage = target.handleDamage(this, target, getDamage(target));
if (getAttack(Attack.MELEE) >= getDefence(target)) {
int actualDamage = target.handleDamage(this, target, getDamage(target, Attack.MELEE));
if (currentActor != null) {
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
}
@ -244,8 +253,8 @@ public class Game implements IGame {
} else {
NPC.playSound("audio/Snow Ball Throw And Splat-SoundBible.com-992042947.wav");
}
if (getAttack() >= getDefence(target)) {
int damage = getDamage(target) / loc.gridDistanceTo(map.getLocation(target));
if (getAttack(type) >= getDefence(target)) {
int damage = getDamage(target, type) / loc.gridDistanceTo(map.getLocation(target));
int actualDamage = target.handleDamage(this, target, damage);
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
} else {
@ -500,6 +509,7 @@ public class Game implements IGame {
boolean secondLineWritten = false;
boolean thirdLineWritten = false;
//Makes long messages overflow to the next line.
if (lastMessages.size() > 0) {
String message = lastMessages.get(0);
if (message.length() > 2 * maxLen) {

View File

@ -15,6 +15,11 @@ public class Bow implements IRangedWeapon {
return damage;
}
@Override
public int getWeaponAttack() {
return 15;
}
@Override
public int getCurrentHealth() {
return hp;

View File

@ -23,13 +23,14 @@ public class Chest implements IContainer, IStatic {
*/
public void fill (int lvl) {
Random random = new Random();
int itemChance = 5;
int itemChance = 3;
List<IItem> items = new ArrayList<>();
items.add(new Staff());
items.add(new Sword());
items.add(new Bow());
items.add(new Binoculars());
items.add(new Shield());
items.add(new Manga());
for (int i = 0; i < MAX_SIZE; i++) {
int num = random.nextInt(100);
boolean added = false;

View File

@ -10,6 +10,13 @@ public interface IWeapon extends IItem {
*/
int getWeaponDamage();
/**
* Returns the attack ponts of a weapon.
*
* @return an int
*/
int getWeaponAttack();
/**
* Retrieves the string path of the sound to play upon an attack.
*

View File

@ -1,9 +1,9 @@
package inf101.v18.rogue101.examples;
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
public class Manga implements IItem {
public class Manga implements IConsumable {
private int hp = getMaxHealth();
@Override
@ -46,4 +46,19 @@ public class Manga implements IItem {
hp -= amount;
return amount;
}
@Override
public int hpIncrease() {
return 0;
}
@Override
public int attackIncrease() {
return 5;
}
@Override
public int defenceIncrease() {
return 5;
}
}

View File

@ -15,6 +15,11 @@ public class Staff implements IMagicWeapon {
return damage;
}
@Override
public int getWeaponAttack() {
return 10;
}
@Override
public int getCurrentHealth() {
return hp;

View File

@ -15,6 +15,11 @@ public class Sword implements IMeleeWeapon {
return damage;
}
@Override
public int getWeaponAttack() {
return 5;
}
@Override
public int getCurrentHealth() {
return hp;

View File

@ -13,7 +13,7 @@ import inf101.v18.rogue101.Main;
import inf101.v18.rogue101.examples.Carrot;
import inf101.v18.rogue101.game.IllegalMoveException;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.examples.Manga;
import inf101.v18.rogue101.items.Manga;
import inf101.v18.rogue101.objects.*;
import javafx.scene.canvas.GraphicsContext;

View File

@ -3,7 +3,7 @@
# G = #
# c G = < #
# G = #
# ############################### #
################################# #
# #
# G #
# ###############################

View File

@ -46,7 +46,7 @@ public class Player implements IPlayer {
tryToMove(game, GridDirection.SOUTH);
turnConsumed = true;
} else if (key == KeyCode.E) {
turnConsumed = pickUpInit(game);
turnConsumed = interactInit(game);
} else if (key == KeyCode.Q) {
turnConsumed = dropInit(game);
} else if (key == KeyCode.C) {
@ -74,9 +74,9 @@ public class Player implements IPlayer {
game.displayMessage("Please enter your name: ");
writing = true;
} else if (key == KeyCode.R) {
turnConsumed = nonMeleeAttack(game, 3, Attack.RANGED, "ranged");
turnConsumed = nonMeleeAttack(game, getVision(), Attack.RANGED, "ranged");
} else if (key == KeyCode.F) {
turnConsumed = nonMeleeAttack(game, 2, Attack.MAGIC, "magic");
turnConsumed = nonMeleeAttack(game, getVision() / 2 + getVision() % 2 == 0 ? 0 : 1, Attack.MAGIC, "magic");
}
showStatus(game);
return turnConsumed;
@ -136,17 +136,17 @@ public class Player implements IPlayer {
}
/**
* Initializes the picking up of an item, and does what is needed.
* Initializes the interaction with an item, and does what is needed.
*
* @param game An IGame object
* @return True if a turn was consumed. False otherwise
*/
private boolean pickUpInit(IGame game) {
private boolean interactInit(IGame game) {
List<IItem> items = game.getLocalItems();
if (items.size() < 1) {
game.displayMessage("There is nothing to pick up");
} else {
if (items.get(0) instanceof IStatic && items.get(0) instanceof IContainer) { //Static items are always the biggest (and hopefully the only) item on a tile.
if (items.get(0) instanceof IStatic && items.get(0) instanceof IContainer) { //A Static item is always the biggest (and hopefully the only) item on a tile.
openChest(game, items);
} else if (items.get(0) instanceof IStatic && items.get(0) instanceof StairsUp) {
((Game)game).goUp();
@ -174,19 +174,19 @@ public class Player implements IPlayer {
* @return True if a potion was used. False otherwise
*/
private boolean useConsumable(IGame game) {
IConsumable potion = (IConsumable) equipped.getFirst(IConsumable.class);
if (potion != null) {
hp += potion.hpIncrease();
IConsumable consumable = (IConsumable) equipped.getFirst(IConsumable.class);
if (consumable != null) {
hp += consumable.hpIncrease();
if (hp > getMaxHealth()) {
hp = getMaxHealth();
}
attack += potion.attackIncrease();
defence += potion.defenceIncrease();
equipped.remove(potion);
game.displayMessage("Used " + potion.getName());
attack += consumable.attackIncrease();
defence += consumable.defenceIncrease();
equipped.remove(consumable);
game.displayMessage("Used " + consumable.getName());
return true;
} else {
game.displayMessage("You don't have any potions.");
game.displayMessage("You don't have any consumables.");
return false;
}
}
@ -335,8 +335,16 @@ public class Player implements IPlayer {
* @param game An IGame object
*/
private void showStatus(IGame game) {
//TODO: Add item bonuses to visible stats.
game.formatStatus("HP: %s %d/%d ATK: %d DEF: %s DMG: %s", NPC.hpBar(this), hp, getMaxHealth(), getAttack(), getDefence(), getDamage());
game.formatStatus("HP: %s ATK: %d RATK: %d MATK: %d DEF: %s DMG: %s RDMG: %s MDMG: %s",
NPC.hpBar(this),
getAttack(Attack.MELEE),
getAttack(Attack.RANGED),
getAttack(Attack.MAGIC),
getDefence(),
getDamage(Attack.MELEE),
getDamage(Attack.RANGED),
getDamage(Attack.MAGIC)
);
printInventory(game);
}
@ -380,16 +388,36 @@ public class Player implements IPlayer {
if (game.canGo(dir)) {
game.move(dir);
} else if (loc.canGo(dir) && game.getMap().hasActors(loc.go(dir))) {
NPC.playSound("audio/Sword Swing-SoundBible.com-639083727.wav");
game.attack(dir, game.getMap().getActors(loc.go(dir)).get(0));
} else {
game.displayMessage("Umm, it is not possible to move in that direction.");
}
}
@Override
public int getAttack() {
return attack;
IBuffItem buff = (IBuffItem)getItem(IBuffItem.class);
if (buff != null) {
return attack + buff.getBuffDamage();
} else {
return attack;
}
}
/**
* Gets the attack with added weapon attack
*
* @param type The attack type corresponding to the weapon type.
* @return Attack as int
*/
private int getAttack(Attack type) {
IWeapon weapon = NPC.getWeapon(type, this);
if (weapon != null) {
return getAttack() + weapon.getWeaponAttack();
} else {
return getAttack();
}
}
@Override
@ -397,6 +425,21 @@ public class Player implements IPlayer {
return 10;
}
/**
* Gets the damage with added weapon damage
*
* @param type The attack type corresponding to the weapon type.
* @return Damage as int
*/
private int getDamage(Attack type) {
IWeapon weapon = NPC.getWeapon(type, this);
if (weapon != null) {
return getDamage() + weapon.getWeaponDamage();
} else {
return getDamage();
}
}
@Override
public IItem getItem(Class<?> type) {
for (IItem item : equipped.getContent()) {
@ -414,7 +457,12 @@ public class Player implements IPlayer {
@Override
public int getDefence() {
return defence;
IBuffItem buff = (IBuffItem)getItem(IBuffItem.class);
if (buff != null) {
return defence + buff.getBuffDamage();
} else {
return defence;
}
}
@Override
@ -462,6 +510,14 @@ public class Player implements IPlayer {
}
}
/**
* Performs a ranged attack on the nearest visible enemy (if any);
*
* @param game An IGame object
* @param range The range of the attack
* @param type The attack type
* @return True if the player attacked. False otherwise
*/
private boolean rangedAttack(IGame game, int range, Attack type) {
List<ILocation> neighbours = game.getVisible();
for (ILocation neighbour : neighbours) {

View File

@ -4,6 +4,7 @@ import inf101.v18.gfx.textmode.BlocksAndBoxes;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.map.IMapView;
import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
@ -194,4 +195,25 @@ public class NPC {
str.append(BlocksAndBoxes.BLOCK_HALF);
return str.toString();
}
/**
* Gets the weapon appropriate for the used attack.
*
* @param type The attack type
* @return An IWeapon or null
*/
public static IWeapon getWeapon(Attack type, IActor actor) {
IWeapon weapon = null;
switch (type) {
case MELEE:
weapon = (IWeapon)actor.getItem(IMeleeWeapon.class);
break;
case RANGED:
weapon = (IWeapon)actor.getItem(IRangedWeapon.class);
break;
case MAGIC:
weapon = (IWeapon)actor.getItem(IMagicWeapon.class);
}
return weapon;
}
}