Kind of finished

It is possible to win and lose the game.
The boss is quite hard to beat, but it should be possible if RNGsus is with you.
Girls have an increasing chance of having a weapon based on map lvl.
Player has been nerfed.
Shields have been nerfed.
Girls' hp has been nerfed.
This commit is contained in:
Kristian Knarvik 2018-03-21 00:10:42 +01:00
parent 89de233b6c
commit a869cb1049
21 changed files with 221 additions and 143 deletions

View File

@ -87,10 +87,11 @@ d)
* Grafisk er spillet blandet. Fonten fungerer relativt dårlig til gridet. I det minste er det bedre enn bokstaver, og lettere enn å bruke TurtlePainter. * Grafisk er spillet blandet. Fonten fungerer relativt dårlig til gridet. I det minste er det bedre enn bokstaver, og lettere enn å bruke TurtlePainter.
### Tredjepartsfiler ### Tredjepartsfiler
* Bow Fire Arrow Sound (http://soundbible.com, Stephan Schutze, Noncommercial 3.0) * Bow Fire Arrow (http://soundbible.com, Stephan Schutze, Noncommercial 3.0)
* Sword Swing Sound (http://soundbible.com, Mike Koenig, Attribution 3.0) * Sword Swing (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Large Fireball Sound (http://soundbible.com, Mike Koenig, Attribution 3.0) * Large Fireball (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Realistic Punch Sound (http://soundbible.com, Mark DiAngelo, Attribution 3.0) * Realistic Punch (http://soundbible.com, Mark DiAngelo, Attribution 3.0)
* Snow Ball Throw And Splat Sound (http://soundbible.com, Mike Koenig, Attribution 3.0) * Snow Ball Throw And Splat (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Dying Sound (http://soundbible.com, Mike Koenig, Attribution 3.0) * Dying (http://soundbible.com, Mike Koenig, Attribution 3.0)
* 1 Person Cheering (http://soundbible.com, Jett Rifkin, Attribution 3.0)

Binary file not shown.

View File

@ -14,7 +14,6 @@ import javafx.application.Application;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.Duration; import javafx.util.Duration;
@ -34,7 +33,7 @@ public class Main extends Application {
public static final int LINE_MSG3 = 24; public static final int LINE_MSG3 = 24;
public static final int LINE_DEBUG = 25; public static final int LINE_DEBUG = 25;
public static final int COLUMN_MAP_END = 40; public static final int COLUMN_MAP_END = 40;
public static final int COLUMN_RIGHTSIDE_START = 41; public static final int COLUMN_RIGHTSIDE_START = 43;
public static void main(String[] args) { public static void main(String[] args) {
launch(args); launch(args);

View File

@ -1,6 +1,9 @@
package inf101.v18.rogue101.enemies; package inf101.v18.rogue101.enemies;
import inf101.v18.gfx.textmode.BlocksAndBoxes;
import inf101.v18.grid.ILocation; import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.Main;
import inf101.v18.rogue101.game.Game;
import inf101.v18.rogue101.game.IGame; import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.Backpack; import inf101.v18.rogue101.items.Backpack;
import inf101.v18.rogue101.items.Sword; import inf101.v18.rogue101.items.Sword;
@ -51,12 +54,12 @@ public class Boss implements INonPlayer {
@Override @Override
public int getDefence() { public int getDefence() {
return 50; return 40;
} }
@Override @Override
public int getMaxHealth() { public int getMaxHealth() {
return 500; return 550;
} }
@Override @Override
@ -84,6 +87,27 @@ public class Boss implements INonPlayer {
return 3; return 3;
} }
/**
* Generates a hp bar based on the boss' current hp
*
* @return HP bar as string
*/
private String hpBar() {
StringBuilder str = new StringBuilder(BlocksAndBoxes.BLOCK_HALF);
int hpLen = (int)((float)hp/getMaxHealth() * 10.0);
if (hp > 0 && hp < 100) {
hpLen++;
}
for (int i = 0; i < hpLen; i++) {
str.append("\u001b[91m" + BlocksAndBoxes.BLOCK_HALF + "\u001b[0m");
}
for (int i = 0; i < 10 - hpLen; i++) {
str.append(" ");
}
str.append(BlocksAndBoxes.BLOCK_HALF);
return str.toString();
}
@Override @Override
public int handleDamage(IGame game, IItem source, int amount) { public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount; hp -= amount;
@ -97,7 +121,9 @@ public class Boss implements INonPlayer {
if (dropped) { if (dropped) {
game.displayMessage(getName() + " dropped something"); game.displayMessage(getName() + " dropped something");
} }
((Game)game).win();
} }
game.getPrinter().printAt(Main.COLUMN_RIGHTSIDE_START, 19, "Boss HP: " + hpBar());
return amount; return amount;
} }
} }

View File

@ -36,35 +36,34 @@ public class Girl implements INonPlayer {
setValidItems(); setValidItems();
} }
//TODO: Game balance
private void setStats() { private void setStats() {
switch (age) { switch (age) {
case TODDLER: case TODDLER:
maxhp = 100; maxhp = 30;
attack = 10; attack = 10;
defence = 50; defence = 50;
visibility = 1; visibility = 1;
break; break;
case CHILD: case CHILD:
maxhp = 150; maxhp = 50;
attack = 20; attack = 20;
defence = 40; defence = 40;
visibility = 2; visibility = 2;
break; break;
case TEEN: case TEEN:
maxhp = 200; maxhp = 70;
attack = 25; attack = 25;
defence = 30; defence = 30;
visibility = 3; visibility = 3;
break; break;
case ADULT: case ADULT:
maxhp = 250; maxhp = 100;
attack = 30; attack = 30;
defence = 20; defence = 20;
visibility = 4; visibility = 4;
break; break;
case ELDER: case ELDER:
maxhp = 200; maxhp = 70;
attack = 15; attack = 15;
defence = 35; defence = 35;
visibility = 2; visibility = 2;
@ -72,8 +71,7 @@ public class Girl implements INonPlayer {
} }
if (occupation == Occupation.KNIGHT) { if (occupation == Occupation.KNIGHT) {
attack += 10; //Knights are quite powerful. attack += 10; //Knights are quite powerful.
} } else if (occupation == Occupation.MAGE) {
if (occupation == Occupation.MAGE) {
attack += 5; // Mages have lesser range than bowsmen, but more damage. attack += 5; // Mages have lesser range than bowsmen, but more damage.
} }
maxhp += (int)(random.nextGaussian() * 10); maxhp += (int)(random.nextGaussian() * 10);
@ -82,6 +80,26 @@ public class Girl implements INonPlayer {
defence += (int)(random.nextGaussian() * 5); defence += (int)(random.nextGaussian() * 5);
} }
/**
* Has a chance of giving a weapon to a girl.
*
* @param lvl The current map level
*/
public void giveWeapon(int lvl) {
if (random.nextInt(5) < lvl) {
switch (occupation) {
case KNIGHT:
backpack.add(new Sword());
break;
case MAGE:
backpack.add(new Staff());
break;
case BOWSMAN:
backpack.add(new Bow());
}
}
}
/** /**
* Specified which items the current Girl should be able to pick up. * Specified which items the current Girl should be able to pick up.
*/ */
@ -111,7 +129,7 @@ public class Girl implements INonPlayer {
@Override @Override
public void doTurn(IGame game) { public void doTurn(IGame game) {
if (!backpack.isFull()) { if (backpack.hasSpace()) {
IItem item = NPC.pickUp(game, validItems); IItem item = NPC.pickUp(game, validItems);
if (item != null) { if (item != null) {
backpack.add(item); backpack.add(item);

View File

@ -67,6 +67,7 @@ public class Game implements IGame {
private final ITurtle painter; private final ITurtle painter;
private final Printer printer; private final Printer printer;
private int numPlayers = 0; private int numPlayers = 0;
boolean won = false;
public Game(Screen screen, ITurtle painter, Printer printer) { public Game(Screen screen, ITurtle painter, Printer printer) {
this.painter = painter; this.painter = painter;
@ -92,7 +93,7 @@ public class Game implements IGame {
// Prints some helpful information. // 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 potion"};
for (int i = 0; i < info.length; i++) { for (int i = 0; i < info.length; i++) {
this.printer.printAt(Main.COLUMN_MAP_END + 2, 1 + i, info[i]); this.printer.printAt(Main.COLUMN_RIGHTSIDE_START, 1 + i, info[i]);
} }
} }
@ -219,7 +220,7 @@ public class Game implements IGame {
map.clean(loc); map.clean(loc);
if (target.isDestroyed()) { if (target.isDestroyed() && currentLocation != null) {
return move(dir); return move(dir);
} else { } else {
movePoints--; movePoints--;
@ -285,14 +286,14 @@ public class Game implements IGame {
// *false*), and then insert a little timer delay between each non-player move // *false*), and then insert a little timer delay between each non-player move
// (the timer // (the timer
// is already set up in Main) // is already set up in Main)
if (numPlayers == 0) { if (numPlayers == 0 && !won) {
kill(); kill();
} }
while (!actors.isEmpty()) { while (!actors.isEmpty()) {
// get the next player or non-player in the queue // get the next player or non-player in the queue
currentActor = actors.remove(0); currentActor = actors.remove(0);
if (currentActor == null) { if (currentActor == null) {
return false; //TODO: Find out why a girl suddenly becomes null (only after map change, not caught in beginTurn) return false; //TODO: Find out why a girl suddenly becomes null (only after map change, not caught in beginTurn, happens randomly)
} }
if (currentActor.isDestroyed()) // skip if it's dead if (currentActor.isDestroyed()) // skip if it's dead
continue; continue;
@ -341,6 +342,17 @@ public class Game implements IGame {
NPC.playSound("audio/Dying-SoundBible.com-1255481835.wav"); NPC.playSound("audio/Dying-SoundBible.com-1255481835.wav");
} }
public void win() { //Trigger when the boss dies
map.remove(currentLocation, currentActor);
currentActor = null;
currentLocation = null;
actors = new ArrayList<>();
loadMap("victory.txt");
map.draw(painter, printer);
NPC.playSound("audio/1_person_cheering-Jett_Rifkin-1851518140.wav");
won = true;
}
/** /**
* Loads a map with the desired name * Loads a map with the desired name
* *
@ -373,6 +385,8 @@ public class Game implements IGame {
IItem item = createItem(inputGrid.get(loc)); IItem item = createItem(inputGrid.get(loc));
if (item instanceof Chest) { if (item instanceof Chest) {
((Chest) item).fill(lvl); ((Chest) item).fill(lvl);
} else if (item instanceof Girl) {
((Girl) item).giveWeapon(lvl);
} }
if (item != null) { if (item != null) {
map.add(loc, item); map.add(loc, item);
@ -631,13 +645,10 @@ public class Game implements IGame {
} }
public boolean keyPressed(KeyCode code) { public boolean keyPressed(KeyCode code) {
// only an IPlayer/human can handle keypresses, and only if it's the human's // only an IPlayer/human can handle keypresses, and only if it's the human's
// turn // turn
if (currentActor instanceof IPlayer) { return !(currentActor instanceof IPlayer) || !((IPlayer) currentActor).keyPressed(this, code);
return !((IPlayer) currentActor).keyPressed(this, code); }
}
return true;
}
@Override @Override
public ILocation move(GridDirection dir) { public ILocation move(GridDirection dir) {

View File

@ -129,8 +129,8 @@ public class Backpack implements IContainer {
} }
@Override @Override
public boolean isFull() { public boolean hasSpace() {
return size() >= MAX_SIZE; return size() < MAX_SIZE;
} }
@Override @Override

View File

@ -4,20 +4,6 @@ import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem; import inf101.v18.rogue101.objects.IItem;
public class Binoculars implements IBuffItem { public class Binoculars implements IBuffItem {
@Override
public int getBuffDamage() {
return 0;
}
@Override
public int getBuffDefence() {
return 0;
}
@Override
public int getBuffDamageReduction() {
return 0;
}
@Override @Override
public int getBuffVisibility() { public int getBuffVisibility() {

View File

@ -58,8 +58,8 @@ public class Chest implements IContainer, IStatic {
} }
@Override @Override
public boolean isFull() { public boolean hasSpace() {
return container.size() >= MAX_SIZE; return container.size() < MAX_SIZE;
} }
@Override @Override

View File

@ -8,26 +8,34 @@ public interface IBuffItem extends IItem {
* *
* @return An int, May be 0 * @return An int, May be 0
*/ */
int getBuffDamage(); default int getBuffDamage() {
return 0;
}
/** /**
* Retrieve defence increase done by the buff item. * Retrieve defence increase done by the buff item.
* *
* @return An int, May be 0 * @return An int, May be 0
*/ */
int getBuffDefence(); default int getBuffDefence() {
return 0;
}
/** /**
* Retrieve defence increase done by the buff item. * Retrieve defence increase done by the buff item.
* *
* @return An int, May be 0 * @return An int, May be 0
*/ */
int getBuffDamageReduction(); default int getBuffDamageReduction() {
return 0;
}
/** /**
* Retrieve visibility increase done by the buff item. * Retrieve visibility increase done by the buff item.
* *
* @return An int, May be 0 * @return An int, May be 0
*/ */
int getBuffVisibility(); default int getBuffVisibility() {
return 0;
}
} }

View File

@ -18,7 +18,7 @@ public interface IContainer extends IItem {
* Adds an item to a container. * Adds an item to a container.
* *
* @return True if the container was not full * @return True if the container was not full
* @praram item The item to add to the container * @param item The item to add to the container
*/ */
boolean addItem(IItem item); boolean addItem(IItem item);
@ -34,7 +34,7 @@ public interface IContainer extends IItem {
* *
* @return True if it has no space left * @return True if it has no space left
*/ */
boolean isFull(); boolean hasSpace();
/** /**
* Returns the message to show the user upon interacting with the container. * Returns the message to show the user upon interacting with the container.

View File

@ -6,7 +6,7 @@ public interface IWeapon extends IItem {
/** /**
* Retrieves the damage points of a weapon. * Retrieves the damage points of a weapon.
* *
* @return * @return An int
*/ */
int getWeaponDamage(); int getWeaponDamage();

View File

@ -6,11 +6,6 @@ import inf101.v18.rogue101.objects.IItem;
public class Shield implements IBuffItem { public class Shield implements IBuffItem {
private final int hp = getMaxHealth(); private final int hp = getMaxHealth();
@Override
public int getBuffDamage() {
return 0;
}
@Override @Override
public int getBuffDefence() { public int getBuffDefence() {
return 10; return 10;
@ -18,12 +13,7 @@ public class Shield implements IBuffItem {
@Override @Override
public int getBuffDamageReduction() { public int getBuffDamageReduction() {
return 5; return 1;
}
@Override
public int getBuffVisibility() {
return 0;
} }
@Override @Override

View File

@ -13,8 +13,8 @@
# G # G # # G # G #
# # # # # #
#### ########################### ####### #### ########################### #######
# # G s c S G # # # # G c G # #
# b # # # # #
# G # G G G # # # G # G G G # #
# ################## G # # ################## G #
# # # #

View File

@ -1,8 +1,8 @@
40 20 40 20
######################################## ########################################
# G # # G = #
# c G < # # c G = < #
# G # # G = #
# ############################### # # ############################### #
# # # #
# G # # G #

View File

@ -1,21 +1,21 @@
40 20 40 20
######################################## ########################################
# # # # # #
# # # # G c G # > #
# # # # G # #
# # # # # G G # #
# # # ######## ########### G #
############################### #
# = #
# > # #
# # #
# # #
# # #
# # #
#### ########################### #######
# # # #
# # #
# # # #
# ################## #
# # # #
# #
# G G #
# #
# #################################
# #
# G G #
# #
# #
################### #
# # G #
# < #
# # #
######################################## ########################################

View File

@ -1,21 +1,21 @@
40 20 40 20
######################################## ########################################
# # # # B #
# # # # H H #
# # # # #
# # # # ################### ###################
# # # # #
############################### # # G G #
# = # # #
# > # # # ################################## #
# # # # #
# # # # G G #
# # # # #
# # # ################### ###################
#### ########################### ####### # G G #
# # # # # #
# # # # ################################ #
# # # # # #
# ################## # # > G c #
# # # #
######################################## ########################################

View File

@ -1,14 +1,14 @@
40 20 40 20
######################################## ########################################
# # # #
# . C R #
# # # #
# # # . G G #
# G G # # R #
# #
############### # # ############### # #
# # # # # #
# S s # # # S s # #
# b H @ ######## ######### # b m H @ ######## #########
# # # # # #
# H # # # H # #
# = # # = #

View File

@ -0,0 +1,30 @@
40 20
########################################
# #
# #
# #
# #
# #
# #
# # # # ### ##### ## #### # # #
# # # # # # # # # # # # #
# # # # # # # # #### ### #
# # # # # # # # # # # #
# ## # ### # ## # # # #
# #
# #
# #
# #
# #
# #
# #
########################################

View File

@ -11,13 +11,12 @@ import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.shared.NPC; import inf101.v18.rogue101.shared.NPC;
import inf101.v18.rogue101.states.Attack; import inf101.v18.rogue101.states.Attack;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Player implements IPlayer { public class Player implements IPlayer {
private int hp = getMaxHealth(); private int hp = getMaxHealth();
private int attack = 50; private int attack = 40;
private int defence = 30; private int defence = 20;
private final Backpack equipped = new Backpack(); private final Backpack equipped = new Backpack();
// True if the player wants to pick up something using the digit keys // True if the player wants to pick up something using the digit keys
private boolean dropping = false; private boolean dropping = false;
@ -54,25 +53,23 @@ public class Player implements IPlayer {
} else if (key == KeyCode.C) { } else if (key == KeyCode.C) {
turnConsumed = useConsumable(game); turnConsumed = useConsumable(game);
} else if (key.isDigitKey()) { } else if (key.isDigitKey()) {
//Takes care of all digit keys, but we only use the first 5 for picking up and dropping. //Takes care of all digit keys.
int keyValue = Integer.parseInt(key.getName()); int keyValue = Integer.parseInt(key.getName());
if (keyValue <= 9 && keyValue >= 0) { if (keyValue == 0) {
if (keyValue == 0) { keyValue = 10;
keyValue = 10; }
} if (dropping) {
if (dropping) { drop(game, keyValue - 1);
drop(game, keyValue - 1); dropping = false;
dropping = false; turnConsumed = true;
turnConsumed = true; } else if (picking) {
} else if (picking) { pickUp(game, keyValue - 1);
pickUp(game, keyValue - 1); picking = false;
picking = false; turnConsumed = true;
turnConsumed = true; } else if (exploringChest) {
} else if (exploringChest) { loot(game, keyValue - 1);
loot(game, keyValue - 1); exploringChest = false;
exploringChest = false; turnConsumed = true;
turnConsumed = true;
}
} }
} else if (key == KeyCode.N) { } else if (key == KeyCode.N) {
game.displayMessage("Please enter your name: "); game.displayMessage("Please enter your name: ");
@ -249,7 +246,7 @@ public class Player implements IPlayer {
* @param i The wanted index * @param i The wanted index
*/ */
private void pickUp(IGame game, int i) { private void pickUp(IGame game, int i) {
if (!equipped.isFull()) { if (equipped.hasSpace()) {
List<IItem> items = game.getLocalItems(); List<IItem> items = game.getLocalItems();
if (items.size() >= i) { if (items.size() >= i) {
IItem pickedUp = game.pickUp(items.get(i)); IItem pickedUp = game.pickUp(items.get(i));
@ -271,7 +268,7 @@ public class Player implements IPlayer {
* Takes an item from a chest, and gives it to the player. * Takes an item from a chest, and gives it to the player.
*/ */
private void loot(IGame game, int i) { private void loot(IGame game, int i) {
if (!equipped.isFull()) { if (equipped.hasSpace()) {
IContainer container = getStaticContainer(game); IContainer container = getStaticContainer(game);
if (container != null && i < container.getContent().size()) { if (container != null && i < container.getContent().size()) {
IItem loot = container.getContent().get(i); IItem loot = container.getContent().get(i);
@ -344,6 +341,11 @@ public class Player implements IPlayer {
printInventory(game); printInventory(game);
} }
/**
* Prints all items in the player's inventory.
*
* @param game An IGame object
*/
private void printInventory(IGame game) { private void printInventory(IGame game) {
Printer printer = game.getPrinter(); Printer printer = game.getPrinter();
List<IItem> items = equipped.getContent(); List<IItem> items = equipped.getContent();
@ -354,6 +356,11 @@ public class Player implements IPlayer {
} }
} }
/**
* Generates a hp bar based on player's current hp
*
* @return HP bar as string
*/
private String hpBar() { private String hpBar() {
StringBuilder str = new StringBuilder(BlocksAndBoxes.BLOCK_HALF); StringBuilder str = new StringBuilder(BlocksAndBoxes.BLOCK_HALF);
int hpLen = (int)((float)hp/getMaxHealth() * 10.0); int hpLen = (int)((float)hp/getMaxHealth() * 10.0);
@ -409,7 +416,7 @@ public class Player implements IPlayer {
@Override @Override
public int getDamage() { public int getDamage() {
return 20; return 10;
} }
@Override @Override

View File

@ -3,11 +3,9 @@ package inf101.v18.rogue101.shared;
import inf101.v18.grid.GridDirection; import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation; import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame; import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.map.IGameMap;
import inf101.v18.rogue101.map.IMapView; import inf101.v18.rogue101.map.IMapView;
import inf101.v18.rogue101.objects.IActor; import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem; import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.INonPlayer;
import inf101.v18.rogue101.objects.IPlayer; import inf101.v18.rogue101.objects.IPlayer;
import inf101.v18.rogue101.states.Attack; import inf101.v18.rogue101.states.Attack;
import javafx.scene.media.Media; import javafx.scene.media.Media;
@ -144,14 +142,18 @@ public class NPC {
private static List<GridDirection> reverseDir(List<GridDirection> dirs) { private static List<GridDirection> reverseDir(List<GridDirection> dirs) {
for (int i = 0; i < dirs.size(); i++) { for (int i = 0; i < dirs.size(); i++) {
GridDirection dir = dirs.get(i); GridDirection dir = dirs.get(i);
if (dir == GridDirection.SOUTH) { switch (dir) {
dirs.set(i, GridDirection.NORTH); case SOUTH:
} else if (dir == GridDirection.NORTH) { dirs.set(i, GridDirection.NORTH);
dirs.set(i, GridDirection.SOUTH); break;
} else if (dir == GridDirection.WEST) { case NORTH:
dirs.set(i, GridDirection.EAST); dirs.set(i, GridDirection.SOUTH);
} else { break;
dirs.set(i, GridDirection.WEST); case WEST:
dirs.set(i, GridDirection.EAST);
break;
case EAST:
dirs.set(i, GridDirection.WEST);
} }
} }
return dirs; return dirs;