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.
### Tredjepartsfiler
* Bow Fire Arrow Sound (http://soundbible.com, Stephan Schutze, Noncommercial 3.0)
* Sword Swing Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Large Fireball Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Realistic Punch Sound (http://soundbible.com, Mark DiAngelo, Attribution 3.0)
* Snow Ball Throw And Splat Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Dying Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Bow Fire Arrow (http://soundbible.com, Stephan Schutze, Noncommercial 3.0)
* Sword Swing (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Large Fireball (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Realistic Punch (http://soundbible.com, Mark DiAngelo, Attribution 3.0)
* Snow Ball Throw And Splat (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.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
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_DEBUG = 25;
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) {
launch(args);

View File

@ -1,6 +1,9 @@
package inf101.v18.rogue101.enemies;
import inf101.v18.gfx.textmode.BlocksAndBoxes;
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.items.Backpack;
import inf101.v18.rogue101.items.Sword;
@ -51,12 +54,12 @@ public class Boss implements INonPlayer {
@Override
public int getDefence() {
return 50;
return 40;
}
@Override
public int getMaxHealth() {
return 500;
return 550;
}
@Override
@ -84,6 +87,27 @@ public class Boss implements INonPlayer {
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
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
@ -97,7 +121,9 @@ public class Boss implements INonPlayer {
if (dropped) {
game.displayMessage(getName() + " dropped something");
}
((Game)game).win();
}
game.getPrinter().printAt(Main.COLUMN_RIGHTSIDE_START, 19, "Boss HP: " + hpBar());
return amount;
}
}

View File

@ -36,35 +36,34 @@ public class Girl implements INonPlayer {
setValidItems();
}
//TODO: Game balance
private void setStats() {
switch (age) {
case TODDLER:
maxhp = 100;
maxhp = 30;
attack = 10;
defence = 50;
visibility = 1;
break;
case CHILD:
maxhp = 150;
maxhp = 50;
attack = 20;
defence = 40;
visibility = 2;
break;
case TEEN:
maxhp = 200;
maxhp = 70;
attack = 25;
defence = 30;
visibility = 3;
break;
case ADULT:
maxhp = 250;
maxhp = 100;
attack = 30;
defence = 20;
visibility = 4;
break;
case ELDER:
maxhp = 200;
maxhp = 70;
attack = 15;
defence = 35;
visibility = 2;
@ -72,8 +71,7 @@ public class Girl implements INonPlayer {
}
if (occupation == Occupation.KNIGHT) {
attack += 10; //Knights are quite powerful.
}
if (occupation == Occupation.MAGE) {
} else if (occupation == Occupation.MAGE) {
attack += 5; // Mages have lesser range than bowsmen, but more damage.
}
maxhp += (int)(random.nextGaussian() * 10);
@ -82,6 +80,26 @@ public class Girl implements INonPlayer {
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.
*/
@ -111,7 +129,7 @@ public class Girl implements INonPlayer {
@Override
public void doTurn(IGame game) {
if (!backpack.isFull()) {
if (backpack.hasSpace()) {
IItem item = NPC.pickUp(game, validItems);
if (item != null) {
backpack.add(item);

View File

@ -67,6 +67,7 @@ public class Game implements IGame {
private final ITurtle painter;
private final Printer printer;
private int numPlayers = 0;
boolean won = false;
public Game(Screen screen, ITurtle painter, Printer printer) {
this.painter = painter;
@ -92,7 +93,7 @@ public class Game implements IGame {
// 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"};
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);
if (target.isDestroyed()) {
if (target.isDestroyed() && currentLocation != null) {
return move(dir);
} else {
movePoints--;
@ -285,14 +286,14 @@ public class Game implements IGame {
// *false*), and then insert a little timer delay between each non-player move
// (the timer
// is already set up in Main)
if (numPlayers == 0) {
if (numPlayers == 0 && !won) {
kill();
}
while (!actors.isEmpty()) {
// get the next player or non-player in the queue
currentActor = actors.remove(0);
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
continue;
@ -341,6 +342,17 @@ public class Game implements IGame {
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
*
@ -373,6 +385,8 @@ public class Game implements IGame {
IItem item = createItem(inputGrid.get(loc));
if (item instanceof Chest) {
((Chest) item).fill(lvl);
} else if (item instanceof Girl) {
((Girl) item).giveWeapon(lvl);
}
if (item != null) {
map.add(loc, item);
@ -631,13 +645,10 @@ public class Game implements IGame {
}
public boolean keyPressed(KeyCode code) {
// only an IPlayer/human can handle keypresses, and only if it's the human's
// turn
if (currentActor instanceof IPlayer) {
return !((IPlayer) currentActor).keyPressed(this, code);
}
return true;
}
// only an IPlayer/human can handle keypresses, and only if it's the human's
// turn
return !(currentActor instanceof IPlayer) || !((IPlayer) currentActor).keyPressed(this, code);
}
@Override
public ILocation move(GridDirection dir) {

View File

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

View File

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

View File

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

View File

@ -8,26 +8,34 @@ public interface IBuffItem extends IItem {
*
* @return An int, May be 0
*/
int getBuffDamage();
default int getBuffDamage() {
return 0;
}
/**
* Retrieve defence increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffDefence();
default int getBuffDefence() {
return 0;
}
/**
* Retrieve defence increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffDamageReduction();
default int getBuffDamageReduction() {
return 0;
}
/**
* Retrieve visibility increase done by the buff item.
*
* @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.
*
* @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);
@ -34,7 +34,7 @@ public interface IContainer extends IItem {
*
* @return True if it has no space left
*/
boolean isFull();
boolean hasSpace();
/**
* 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.
*
* @return
* @return An int
*/
int getWeaponDamage();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,14 @@
40 20
########################################
# #
# . C R #
# #
# #
# G G #
# #
# . G G #
# R #
############### # #
# # #
# S s # #
# b H @ ######## #########
# b m 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.states.Attack;
import javafx.scene.input.KeyCode;
import java.util.ArrayList;
import java.util.List;
public class Player implements IPlayer {
private int hp = getMaxHealth();
private int attack = 50;
private int defence = 30;
private int attack = 40;
private int defence = 20;
private final Backpack equipped = new Backpack();
// True if the player wants to pick up something using the digit keys
private boolean dropping = false;
@ -54,25 +53,23 @@ public class Player implements IPlayer {
} else if (key == KeyCode.C) {
turnConsumed = useConsumable(game);
} 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());
if (keyValue <= 9 && keyValue >= 0) {
if (keyValue == 0) {
keyValue = 10;
}
if (dropping) {
drop(game, keyValue - 1);
dropping = false;
turnConsumed = true;
} else if (picking) {
pickUp(game, keyValue - 1);
picking = false;
turnConsumed = true;
} else if (exploringChest) {
loot(game, keyValue - 1);
exploringChest = false;
turnConsumed = true;
}
if (keyValue == 0) {
keyValue = 10;
}
if (dropping) {
drop(game, keyValue - 1);
dropping = false;
turnConsumed = true;
} else if (picking) {
pickUp(game, keyValue - 1);
picking = false;
turnConsumed = true;
} else if (exploringChest) {
loot(game, keyValue - 1);
exploringChest = false;
turnConsumed = true;
}
} else if (key == KeyCode.N) {
game.displayMessage("Please enter your name: ");
@ -249,7 +246,7 @@ public class Player implements IPlayer {
* @param i The wanted index
*/
private void pickUp(IGame game, int i) {
if (!equipped.isFull()) {
if (equipped.hasSpace()) {
List<IItem> items = game.getLocalItems();
if (items.size() >= 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.
*/
private void loot(IGame game, int i) {
if (!equipped.isFull()) {
if (equipped.hasSpace()) {
IContainer container = getStaticContainer(game);
if (container != null && i < container.getContent().size()) {
IItem loot = container.getContent().get(i);
@ -344,6 +341,11 @@ public class Player implements IPlayer {
printInventory(game);
}
/**
* Prints all items in the player's inventory.
*
* @param game An IGame object
*/
private void printInventory(IGame game) {
Printer printer = game.getPrinter();
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() {
StringBuilder str = new StringBuilder(BlocksAndBoxes.BLOCK_HALF);
int hpLen = (int)((float)hp/getMaxHealth() * 10.0);
@ -409,7 +416,7 @@ public class Player implements IPlayer {
@Override
public int getDamage() {
return 20;
return 10;
}
@Override

View File

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