v0.6.4: major overhaul to game start UI

This commit is contained in:
Evan Debenham 2018-03-17 22:31:55 -04:00
parent 090614bb86
commit bc14b0472b
18 changed files with 885 additions and 506 deletions

View File

@ -60,7 +60,6 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SpecialRoom;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.StartScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import com.shatteredpixel.shatteredpixeldungeon.utils.DungeonSeed;
@ -238,7 +237,7 @@ public class Dungeon {
Badges.reset();
StartScene.selectedClass.initHero( hero );
GamesInProgress.selectedClass.initHero( hero );
}
public static boolean isChallenged( int mask ) {
@ -533,8 +532,7 @@ public class Dungeon {
saveGame( GamesInProgress.curSlot );
saveLevel( GamesInProgress.curSlot );
GamesInProgress.set( GamesInProgress.curSlot, depth, challenges,
hero.lvl, hero.heroClass, hero.subClass );
GamesInProgress.set( GamesInProgress.curSlot, depth, challenges, hero );
} else if (WndResurrect.instance != null) {
@ -671,6 +669,7 @@ public class Dungeon {
info.version = bundle.getInt( VERSION );
info.challenges = bundle.getInt( CHALLENGES );
Hero.preview( info, bundle.getBundle( HERO ) );
Statistics.preview( info, bundle );
}
public static void fail( Class cause ) {

View File

@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@ -29,14 +30,21 @@ import com.watabou.utils.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class GamesInProgress {
public static final int MAX_SLOTS = 4;
//null means we have loaded info and it is empty, no entry means unknown.
private static HashMap<Integer, Info> slotStates = new HashMap<>();
public static int curSlot;
public static HeroClass selectedClass;
private static final String GAME_FOLDER = "game%d";
private static final String GAME_FILE = "game.dat";
private static final String DEPTH_FILE = "depth%d.dat";
@ -57,6 +65,23 @@ public class GamesInProgress {
return FileUtils.getFile( gameFolder(slot), Messages.format(DEPTH_FILE, depth));
}
public static int firstEmpty(){
for (int i = 1; i <= MAX_SLOTS; i++){
if (check(i) == null) return i;
}
return -1;
}
public static ArrayList<Info> checkAll(){
ArrayList<Info> result = new ArrayList<>();
for (int i = 0; i <= MAX_SLOTS; i++){
Info curr = check(i);
if (curr != null) result.add(curr);
}
Collections.sort(result, scoreComparator);
return result;
}
public static Info check( int slot ) {
if (slotStates.containsKey( slot )) {
@ -75,6 +100,7 @@ public class GamesInProgress {
Bundle bundle = FileUtils.bundleFromFile(gameFile(slot));
info = new Info();
info.slot = slot;
Dungeon.preview(info, bundle);
//saves from before 0.4.3c are not supported
@ -93,15 +119,26 @@ public class GamesInProgress {
}
}
public static void set( int slot, int depth, int challenges,
int level, HeroClass heroClass, HeroSubClass subClass) {
public static void set(int slot, int depth, int challenges,
Hero hero) {
Info info = new Info();
info.slot = slot;
info.depth = depth;
info.challenges = challenges;
info.level = level;
info.heroClass = heroClass;
info.subClass = subClass;
info.level = hero.lvl;
info.str = hero.STR;
info.exp = hero.exp;
info.hp = hero.HP;
info.ht = hero.HT;
info.shld = hero.SHLD;
info.heroClass = hero.heroClass;
info.subClass = hero.subClass;
info.armorTier = hero.tier();
info.goldCollected = Statistics.goldCollected;
info.maxDepth = Statistics.deepestFloor;
slotStates.put( slot, info );
}
@ -115,12 +152,32 @@ public class GamesInProgress {
}
public static class Info {
public int slot;
public int depth;
public int version;
public int challenges;
public int level;
public int str;
public int exp;
public int hp;
public int ht;
public int shld;
public HeroClass heroClass;
public HeroSubClass subClass;
public int armorTier;
public int goldCollected;
public int maxDepth;
}
public static final Comparator<GamesInProgress.Info> scoreComparator = new Comparator<GamesInProgress.Info>() {
@Override
public int compare(GamesInProgress.Info lhs, GamesInProgress.Info rhs ) {
int lScore = (lhs.level * lhs.maxDepth * 100) + lhs.goldCollected;
int rScore = (rhs.level * rhs.maxDepth * 100) + rhs.goldCollected;
return (int)Math.signum( rScore - lScore );
}
};
}

View File

@ -91,5 +91,10 @@ public class Statistics {
duration = bundle.getFloat( DURATION );
amuletObtained = bundle.getBoolean( AMULET );
}
public static void preview( GamesInProgress.Info info, Bundle bundle ){
info.goldCollected = bundle.getInt( GOLD );
info.maxDepth = bundle.getInt( DEEPEST );
}
}

View File

@ -119,11 +119,11 @@ public abstract class Char extends Actor {
return false;
}
private static final String POS = "pos";
private static final String TAG_HP = "HP";
private static final String TAG_HT = "HT";
private static final String TAG_SHLD = "SHLD";
private static final String BUFFS = "buffs";
protected static final String POS = "pos";
protected static final String TAG_HP = "HP";
protected static final String TAG_HT = "HT";
protected static final String TAG_SHLD = "SHLD";
protected static final String BUFFS = "buffs";
@Override
public void storeInBundle( Bundle bundle ) {

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.hero;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.KindofMisc;
@ -127,6 +128,14 @@ public class Belongings implements Iterable<Item> {
}
}
public static void preview( GamesInProgress.Info info, Bundle bundle ) {
if (bundle.contains( ARMOR )){
info.armorTier = ((Armor)bundle.get( ARMOR )).tier;
} else {
info.armorTier = 0;
}
}
@SuppressWarnings("unchecked")
public<T extends Item> T getItem( Class<T> itemClass ) {

View File

@ -260,8 +260,14 @@ public class Hero extends Char {
public static void preview( GamesInProgress.Info info, Bundle bundle ) {
info.level = bundle.getInt( LEVEL );
info.str = bundle.getInt( STRENGTH );
info.exp = bundle.getInt( EXPERIENCE );
info.hp = bundle.getInt( Char.TAG_HP );
info.ht = bundle.getInt( Char.TAG_HT );
info.shld = bundle.getInt( Char.TAG_SHLD );
info.heroClass = HeroClass.restoreInBundle( bundle );
info.subClass = HeroSubClass.restoreInBundle( bundle );
Belongings.preview( info, bundle );
}
public String className() {
@ -1256,6 +1262,10 @@ public class Hero extends Char {
}
public int maxExp() {
return maxExp( lvl );
}
public static int maxExp( int lvl ){
return 5 + lvl * 5;
}

View File

@ -51,15 +51,17 @@ import com.watabou.utils.Bundle;
public enum HeroClass {
WARRIOR( "warrior" ),
MAGE( "mage" ),
ROGUE( "rogue" ),
HUNTRESS( "huntress" );
WARRIOR( "warrior", HeroSubClass.BERSERKER, HeroSubClass.GLADIATOR ),
MAGE( "mage", HeroSubClass.BATTLEMAGE, HeroSubClass.WARLOCK ),
ROGUE( "rogue", HeroSubClass.ASSASSIN, HeroSubClass.FREERUNNER ),
HUNTRESS( "huntress", HeroSubClass.WARDEN, HeroSubClass.SNIPER );
private String title;
private HeroSubClass[] subClasses;
HeroClass( String title ) {
HeroClass( String title, HeroSubClass...subClasses ) {
this.title = title;
this.subClasses = subClasses;
}
public void initHero( Hero hero ) {
@ -191,6 +193,10 @@ public enum HeroClass {
return Messages.get(HeroClass.class, title);
}
public HeroSubClass[] subClasses() {
return subClasses;
}
public String spritesheet() {
switch (this) {

View File

@ -21,10 +21,10 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SpecialRoom;
import com.shatteredpixel.shatteredpixeldungeon.scenes.StartScene;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
@ -52,7 +52,7 @@ public abstract class SecretRoom extends SpecialRoom {
float[] regionChances = baseRegionSecrets.clone();
if (StartScene.selectedClass == HeroClass.ROGUE){
if (GamesInProgress.selectedClass == HeroClass.ROGUE){
for (int i = 0; i < regionChances.length; i++){
regionChances[i] += 0.6f;
}

View File

@ -62,12 +62,12 @@ public class BadgesScene extends PixelScene {
add( archs );
float left = 5;
float top = 15;
float top = 16;
RenderedText title = PixelScene.renderText( Messages.get(this, "title"), 9 );
title.hardlight(Window.TITLE_COLOR);
title.x = (w - title.width()) / 2 ;
title.y = (top - title.baseLine()) / 2 ;
title.x = (w - title.width()) / 2f;
title.y = (top - title.baseLine()) / 2f;
align(title);
add(title);

View File

@ -94,8 +94,8 @@ public class ChangesScene extends PixelScene {
RenderedText title = PixelScene.renderText( Messages.get(this, "title"), 9 );
title.hardlight(Window.TITLE_COLOR);
title.x = (w - title.width()) / 2 ;
title.y = 4;
title.x = (w - title.width()) / 2f;
title.y = (16 - title.baseLine()) / 2f;
align(title);
add(title);

View File

@ -73,9 +73,9 @@ public class RankingsScene extends PixelScene {
Rankings.INSTANCE.load();
RenderedText title = PixelScene.renderText( Messages.get(this, "title"), 9);
title.hardlight(Window.SHPX_COLOR);
title.x = (w - title.width()) / 2;
title.y = GAP;
title.hardlight(Window.TITLE_COLOR);
title.x = (w - title.width()) / 2f;
title.y = (16 - title.baseLine()) / 2f;
align(title);
add(title);

View File

@ -21,510 +21,263 @@
package com.shatteredpixel.shatteredpixeldungeon.scenes;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Chrome;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.effects.BannerSprites;
import com.shatteredpixel.shatteredpixeldungeon.effects.BannerSprites.Type;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.ActionIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndChallenges;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndClass;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndMessage;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndGameInProgress;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStartGame;
import com.watabou.noosa.BitmapText;
import com.watabou.noosa.Camera;
import com.watabou.noosa.Game;
import com.watabou.noosa.Group;
import com.watabou.noosa.Image;
import com.watabou.noosa.NinePatch;
import com.watabou.noosa.RenderedText;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.particles.BitmaskEmitter;
import com.watabou.noosa.particles.Emitter;
import com.watabou.noosa.ui.Button;
import com.watabou.utils.Callback;
import java.util.HashMap;
import java.util.ArrayList;
public class StartScene extends PixelScene {
private static final float BUTTON_HEIGHT = 24;
private static final float GAP = 2;
private static final float WIDTH_P = 116;
private static final float HEIGHT_P = 220;
private static final float WIDTH_L = 224;
private static final float HEIGHT_L = 124;
private static HashMap<HeroClass, ClassShield> shields = new HashMap<>();
private float buttonX;
private float buttonY;
private GameButton btnLoad;
private GameButton btnNewGame;
private boolean huntressUnlocked;
private Group unlock;
public static HeroClass selectedClass;
private static final int SLOT_WIDTH = 120;
private static final int SLOT_HEIGHT = 30;
private static final int CHALLENGE_SIZE = 22;
@Override
public void create() {
super.create();
Badges.loadGlobal();
Journal.loadGlobal();
uiCamera.visible = false;
int w = Camera.main.width;
int h = Camera.main.height;
float width, height;
if (SPDSettings.landscape()) {
width = WIDTH_L;
height = HEIGHT_L;
} else {
width = WIDTH_P;
height = HEIGHT_P;
}
float left = (w - width) / 2;
float top = (h - height) / 2;
float bottom = h - top;
Archs archs = new Archs();
archs.setSize( w, h );
add( archs );
Image title = BannerSprites.get( Type.SELECT_YOUR_HERO );
title.x = (w - title.width()) / 2;
title.y = top;
align( title );
add( title );
buttonX = left;
buttonY = bottom - BUTTON_HEIGHT;
btnNewGame = new GameButton( Messages.get(this, "new") ) {
@Override
protected void onClick() {
if (GamesInProgress.check( GamesInProgress.curSlot ) != null) {
StartScene.this.add( new WndOptions(
Messages.get(StartScene.class, "really"),
Messages.get(StartScene.class, "warning"),
Messages.get(StartScene.class, "yes"),
Messages.get(StartScene.class, "no") ) {
@Override
protected void onSelect( int index ) {
if (index == 0) {
startNewGame();
}
}
} );
} else {
startNewGame();
}
}
};
add( btnNewGame );
btnLoad = new GameButton( Messages.get(this, "load") ) {
@Override
protected void onClick() {
InterlevelScene.mode = InterlevelScene.Mode.CONTINUE;
Game.switchScene( InterlevelScene.class );
}
};
add( btnLoad );
float centralHeight = buttonY - title.y - title.height();
HeroClass[] classes = {
HeroClass.WARRIOR, HeroClass.MAGE, HeroClass.ROGUE, HeroClass.HUNTRESS
};
for (HeroClass cl : classes) {
ClassShield shield = new ClassShield( cl );
shields.put( cl, shield );
add( shield );
}
if (SPDSettings.landscape()) {
float shieldW = width / 4;
float shieldH = Math.min( centralHeight, shieldW );
top = title.y + title.height + (centralHeight - shieldH) / 2;
for (int i=0; i < classes.length; i++) {
ClassShield shield = shields.get( classes[i] );
shield.setRect( left + i * shieldW, top, shieldW, shieldH );
align(shield);
}
ChallengeButton challenge = new ChallengeButton();
challenge.setPos(
w/2 - challenge.width()/2,
top + shieldH/2 - challenge.height()/2 );
add( challenge );
} else {
float shieldW = width / 2;
float shieldH = Math.min( centralHeight / 2, shieldW * 1.2f );
top = title.y + title.height() + centralHeight / 2 - shieldH;
for (int i=0; i < classes.length; i++) {
ClassShield shield = shields.get( classes[i] );
shield.setRect(
left + (i % 2) * shieldW,
top + (i / 2) * shieldH,
shieldW, shieldH );
align(shield);
}
ChallengeButton challenge = new ChallengeButton();
challenge.setPos(
w/2 - challenge.width()/2,
top + shieldH - challenge.height()/2 );
align(challenge);
add( challenge );
}
unlock = new Group();
add( unlock );
if (!(huntressUnlocked = Badges.isUnlocked( Badges.Badge.BOSS_SLAIN_3 ))) {
RenderedTextMultiline text = PixelScene.renderMultiline( Messages.get(this, "unlock"), 9 );
text.maxWidth((int)width);
text.hardlight( 0xFFFF00 );
text.setPos(w / 2 - text.width() / 2, (bottom - BUTTON_HEIGHT) + (BUTTON_HEIGHT - text.height()) / 2);
align(text);
unlock.add(text);
}
ExitButton btnExit = new ExitButton();
btnExit.setPos( Camera.main.width - btnExit.width(), 0 );
btnExit.setPos( w - btnExit.width(), 0 );
add( btnExit );
RenderedText title = PixelScene.renderText( Messages.get(this, "title"), 9);
title.hardlight(Window.TITLE_COLOR);
title.x = (w - title.width()) / 2f;
title.y = (16 - title.baseLine()) / 2f;
align(title);
add(title);
ArrayList<GamesInProgress.Info> games = GamesInProgress.checkAll();
int slotGap = SPDSettings.landscape() ? 5 : 10;
int slotCount = Math.min(GamesInProgress.MAX_SLOTS, games.size()+1);
int slotsHeight = slotCount*SLOT_HEIGHT + (slotCount-1)* slotGap;
float yPos = (h - slotsHeight)/2f;
if (SPDSettings.landscape()) yPos += 8;
for (GamesInProgress.Info game : games) {
SaveSlotButton existingGame = new SaveSlotButton();
existingGame.set(game.slot);
existingGame.setRect((w - SLOT_WIDTH) / 2f, yPos, SLOT_WIDTH, SLOT_HEIGHT);
yPos += SLOT_HEIGHT + slotGap;
align(existingGame);
add(existingGame);
}
if (games.size() < GamesInProgress.MAX_SLOTS){
SaveSlotButton newGame = new SaveSlotButton();
newGame.set(GamesInProgress.firstEmpty());
newGame.setRect((w - SLOT_WIDTH) / 2f, yPos, SLOT_WIDTH, SLOT_HEIGHT);
yPos += SLOT_HEIGHT + slotGap;
align(newGame);
add(newGame);
}
IconButton challengeButton = new IconButton(
Icons.get( SPDSettings.challenges() > 0 ? Icons.CHALLENGE_ON :Icons.CHALLENGE_OFF)){
@Override
protected void onClick() {
ShatteredPixelDungeon.scene().add(new WndChallenges(SPDSettings.challenges(), true) {
public void onBackPressed() {
super.onBackPressed();
icon( Icons.get( SPDSettings.challenges() > 0 ?
Icons.CHALLENGE_ON :Icons.CHALLENGE_OFF ) );
}
} );
}
};
if (SPDSettings.landscape()){
challengeButton.setRect(4*slotGap + (w + SLOT_WIDTH - CHALLENGE_SIZE)/2, 8 + (h - CHALLENGE_SIZE)/2f,
CHALLENGE_SIZE, CHALLENGE_SIZE);
} else {
challengeButton.setRect((w - CHALLENGE_SIZE)/2f, yPos + 2*slotGap - CHALLENGE_SIZE/2f,
CHALLENGE_SIZE, CHALLENGE_SIZE);
}
align(challengeButton);
if (!Badges.isUnlocked(Badges.Badge.VICTORY)){
add(challengeButton);
} else {
Dungeon.challenges = 0;
SPDSettings.challenges(0);
}
GamesInProgress.curSlot = 0;
ActionIndicator.action = null;
updateClass( HeroClass.values()[SPDSettings.lastClass()] );
fadeIn();
Badges.loadingListener = new Callback() {
@Override
public void call() {
if (Game.scene() == StartScene.this) {
ShatteredPixelDungeon.switchNoFade( StartScene.class );
}
}
};
}
@Override
public void destroy() {
Badges.saveGlobal();
Badges.loadingListener = null;
super.destroy();
}
private void updateClass( HeroClass cl ) {
int slot = cl.ordinal()+1;
if (GamesInProgress.curSlot == slot) {
add( new WndClass( cl ) );
return;
} else {
GamesInProgress.curSlot = slot;
}
if (selectedClass != null) {
shields.get( selectedClass ).highlight( false );
}
shields.get( selectedClass = cl ).highlight( true );
if (cl != HeroClass.HUNTRESS || huntressUnlocked) {
unlock.visible = false;
GamesInProgress.Info info = GamesInProgress.check( GamesInProgress.curSlot );
if (info != null) {
btnLoad.visible = true;
btnLoad.secondary( Messages.format( Messages.get(this, "depth_level"), info.depth, info.level ), info.challenges != 0 );
btnNewGame.visible = true;
btnNewGame.secondary( Messages.get(this, "erase"), false );
float w = (Camera.main.width - GAP) / 2 - buttonX;
btnLoad.setRect(
buttonX, buttonY, w, BUTTON_HEIGHT );
btnNewGame.setRect(
btnLoad.right() + GAP, buttonY, w, BUTTON_HEIGHT );
} else {
btnLoad.visible = false;
btnNewGame.visible = true;
btnNewGame.secondary( null, false );
btnNewGame.setRect( buttonX, buttonY, Camera.main.width - buttonX * 2, BUTTON_HEIGHT );
}
} else {
unlock.visible = true;
btnLoad.visible = false;
btnNewGame.visible = false;
}
fadeIn();
}
private void startNewGame() {
Dungeon.hero = null;
InterlevelScene.mode = InterlevelScene.Mode.DESCEND;
if (SPDSettings.intro()) {
SPDSettings.intro( false );
Game.switchScene( IntroScene.class );
} else {
Game.switchScene( InterlevelScene.class );
}
}
@Override
protected void onBackPressed() {
ShatteredPixelDungeon.switchNoFade( TitleScene.class );
}
private static class GameButton extends RedButton {
private static final int SECONDARY_COLOR_N = 0xCACFC2;
private static final int SECONDARY_COLOR_H = 0xFFFF88;
private RenderedText secondary;
public GameButton( String primary ) {
super( primary );
this.secondary.text( null );
}
@Override
protected void createChildren() {
super.createChildren();
secondary = renderText( 6 );
add( secondary );
}
@Override
protected void layout() {
super.layout();
if (secondary.text().length() > 0) {
text.y = y + (height - text.height() - secondary.baseLine()) / 2;
secondary.x = x + (width - secondary.width()) / 2;
secondary.y = text.y + text.height();
} else {
text.y = y + (height - text.baseLine()) / 2;
}
align(text);
align(secondary);
}
public void secondary( String text, boolean highlighted ) {
secondary.text( text );
secondary.hardlight( highlighted ? SECONDARY_COLOR_H : SECONDARY_COLOR_N );
}
}
private class ClassShield extends Button {
private static final float MIN_BRIGHTNESS = 0.6f;
private static final int BASIC_NORMAL = 0x444444;
private static final int BASIC_HIGHLIGHTED = 0xCACFC2;
private static final int MASTERY_NORMAL = 0x666644;
private static final int MASTERY_HIGHLIGHTED= 0xFFFF88;
private static final int WIDTH = 24;
private static final int HEIGHT = 32;
private static final float SCALE = 1.75f;
private HeroClass cl;
private Image avatar;
private static class SaveSlotButton extends Button {
private NinePatch bg;
private Image hero;
private RenderedText name;
private Emitter emitter;
private float brightness;
private int normal;
private int highlighted;
public ClassShield( HeroClass cl ) {
super();
this.cl = cl;
avatar.frame( cl.ordinal() * WIDTH, 0, WIDTH, HEIGHT );
avatar.scale.set( SCALE );
if (Badges.isUnlocked( cl.masteryBadge() )) {
normal = MASTERY_NORMAL;
highlighted = MASTERY_HIGHLIGHTED;
} else {
normal = BASIC_NORMAL;
highlighted = BASIC_HIGHLIGHTED;
}
name.text( cl.title().toUpperCase() );
name.hardlight( normal );
brightness = MIN_BRIGHTNESS;
updateBrightness();
}
private Image steps;
private BitmapText depth;
private Image classIcon;
private BitmapText level;
private int slot;
private boolean newGame;
@Override
protected void createChildren() {
super.createChildren();
avatar = new Image( Assets.AVATARS );
add( avatar );
name = PixelScene.renderText( 9 );
add( name );
emitter = new BitmaskEmitter( avatar );
add( emitter );
bg = Chrome.get(Chrome.Type.GEM);
add( bg);
name = PixelScene.renderText(9);
add(name);
}
@Override
protected void layout() {
super.layout();
avatar.x = x + (width - avatar.width()) / 2;
avatar.y = y + (height - avatar.height() - name.height()) / 2;
align(avatar);
name.x = x + (width - name.width()) / 2;
name.y = avatar.y + avatar.height() + SCALE;
align(name);
}
@Override
protected void onTouchDown() {
emitter.revive();
emitter.start( Speck.factory( Speck.LIGHT ), 0.05f, 7 );
Sample.INSTANCE.play( Assets.SND_CLICK, 1, 1, 1.2f );
updateClass( cl );
}
@Override
public void update() {
super.update();
if (brightness < 1.0f && brightness > MIN_BRIGHTNESS) {
if ((brightness -= Game.elapsed) <= MIN_BRIGHTNESS) {
brightness = MIN_BRIGHTNESS;
public void set( int slot ){
this.slot = slot;
GamesInProgress.Info info = GamesInProgress.check(slot);
newGame = info == null;
if (newGame){
name.text( Messages.get(StartScene.class, "new"));
if (hero != null){
remove(hero);
hero = null;
remove(steps);
steps = null;
remove(depth);
depth = null;
remove(classIcon);
classIcon = null;
remove(level);
level = null;
}
updateBrightness();
}
}
public void highlight( boolean value ) {
if (value) {
brightness = 1.0f;
name.hardlight( highlighted );
} else {
brightness = 0.999f;
name.hardlight( normal );
name.text( Messages.titleCase(info.heroClass.title()) );
if (hero == null){
hero = new Image(info.heroClass.spritesheet(), 0, 15*info.armorTier, 12, 15);
add(hero);
steps = new Image(Icons.get(Icons.DEPTH));
add(steps);
depth = new BitmapText(PixelScene.pixelFont);
add(depth);
classIcon = new Image(Icons.get(info.heroClass));
add(classIcon);
level = new BitmapText(PixelScene.pixelFont);
add(level);
} else {
hero.copy(new Image(info.heroClass.spritesheet(), 0, 15*info.armorTier, 12, 15));
classIcon.copy(Icons.get(info.heroClass));
}
depth.text(Integer.toString(info.depth));
depth.measure();
level.text(Integer.toString(info.level));
level.measure();
}
updateBrightness();
layout();
}
private void updateBrightness() {
avatar.gm = avatar.bm = avatar.rm = avatar.am = brightness;
}
}
private class ChallengeButton extends Button {
private Image image;
public ChallengeButton() {
super();
width = image.width;
height = image.height;
image.am = Badges.isUnlocked( Badges.Badge.VICTORY ) ? 1.0f : 0.5f;
}
@Override
protected void createChildren() {
super.createChildren();
image = Icons.get( SPDSettings.challenges() > 0 ? Icons.CHALLENGE_ON :Icons.CHALLENGE_OFF );
add( image );
}
@Override
protected void layout() {
super.layout();
image.x = x;
image.y = y;
bg.x = x;
bg.y = y;
bg.size( width, height );
if (hero != null){
hero.x = x+8;
hero.y = y + (height - hero.height())/2f;
align(hero);
name.x = hero.x + hero.width() + 6;
name.y = y + (height - name.baseLine())/2f;
align(name);
classIcon.x = x + width - classIcon.width() - 8;
classIcon.y = y + (height - classIcon.height())/2f;
level.x = classIcon.x + (classIcon.width() - level.width()) / 2f;
level.y = classIcon.y + (classIcon.height() - level.height()) / 2f + 1;
align(level);
steps.x = classIcon.x - steps.width();
steps.y = y + (height - steps.height())/2f;
depth.x = steps.x + (steps.width() - depth.width()) / 2f;
depth.y = steps.y + (steps.height() - depth.height()) / 2f + 1;
align(depth);
} else {
name.x = x + (width - name.width())/2f;
name.y = y + (height - name.baseLine())/2f;
align(name);
}
}
@Override
protected void onClick() {
if (Badges.isUnlocked( Badges.Badge.VICTORY )) {
StartScene.this.add(new WndChallenges(SPDSettings.challenges(), true) {
public void onBackPressed() {
super.onBackPressed();
image.copy( Icons.get( SPDSettings.challenges() > 0 ?
Icons.CHALLENGE_ON :Icons.CHALLENGE_OFF ) );
}
} );
if (newGame) {
ShatteredPixelDungeon.scene().add( new WndStartGame(slot));
} else {
StartScene.this.add( new WndMessage( Messages.get(StartScene.class, "need_to_win") ) );
SPDSettings.challenges(0);
ShatteredPixelDungeon.scene().add( new WndGameInProgress(slot));
}
}
@Override
protected void onTouchDown() {
Sample.INSTANCE.play( Assets.SND_CLICK );
}
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2017 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.ui;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.watabou.noosa.Image;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.ui.Button;
public class IconButton extends Button {
protected Image icon;
public IconButton(){
super();
}
public IconButton( Image icon ){
super();
icon( icon );
}
@Override
protected void layout() {
super.layout();
if (icon != null) {
icon.x = x + (width - icon.width()) / 2f;
icon.y = y + (height - icon.height()) / 2f;
PixelScene.align(icon);
}
}
@Override
protected void onTouchDown() {
if (icon != null) icon.brightness( 1.5f );
Sample.INSTANCE.play( Assets.SND_CLICK );
}
@Override
protected void onTouchUp() {
if (icon != null) icon.resetColor();
}
public void enable( boolean value ) {
active = value;
if (icon != null) icon.alpha( value ? 1.0f : 0.3f );
}
public void icon( Image icon ) {
if (this.icon != null) {
remove( this.icon );
}
this.icon = icon;
if (this.icon != null) {
add( this.icon );
layout();
}
}
public Image icon(){
return icon;
}
}

View File

@ -0,0 +1,134 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2017 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.InterlevelScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.noosa.RenderedText;
import com.watabou.utils.FileUtils;
import java.util.Locale;
public class WndGameInProgress extends Window {
private static final int WIDTH = 120;
private static final int HEIGHT = 120;
private static final int GAP = 5;
private float pos;
public WndGameInProgress(final int slot){
GamesInProgress.Info info = GamesInProgress.check(slot);
IconTitle title = new IconTitle();
title.icon( HeroSprite.avatar(info.heroClass, info.armorTier) );
title.label((Messages.get(this, "title", info.level, info.heroClass.title())).toUpperCase(Locale.ENGLISH));
title.color(Window.SHPX_COLOR);
title.setRect( 0, 0, WIDTH, 0 );
add(title);
pos = title.bottom() + 2*GAP;
statSlot( Messages.get(this, "str"), info.str );
if (info.shld > 0) statSlot( Messages.get(this, "health"), info.hp + "+" + info.shld + "/" + info.ht );
else statSlot( Messages.get(this, "health"), (info.hp) + "/" + info.ht );
statSlot( Messages.get(this, "exp"), info.exp + "/" + Hero.maxExp(info.level) );
pos += GAP;
statSlot( Messages.get(this, "gold"), info.goldCollected );
statSlot( Messages.get(this, "depth"), info.maxDepth );
pos += GAP;
RedButton cont = new RedButton(Messages.get(this, "continue")){
@Override
protected void onClick() {
super.onClick();
GamesInProgress.curSlot = slot;
Dungeon.hero = null;
InterlevelScene.mode = InterlevelScene.Mode.CONTINUE;
ShatteredPixelDungeon.switchScene(InterlevelScene.class);
}
};
RedButton erase = new RedButton( Messages.get(this, "erase")){
@Override
protected void onClick() {
super.onClick();
ShatteredPixelDungeon.scene().add(new WndOptions(
Messages.get(WndGameInProgress.class, "erase_warn_title"),
Messages.get(WndGameInProgress.class, "erase_warn_body"),
Messages.get(WndGameInProgress.class, "erase_warn_yes"),
Messages.get(WndGameInProgress.class, "erase_warn_no") ) {
@Override
protected void onSelect( int index ) {
if (index == 0) {
FileUtils.deleteDir(GamesInProgress.gameFolder(slot));
GamesInProgress.setUnknown(slot);
ShatteredPixelDungeon.switchNoFade((Class<? extends PixelScene>) ShatteredPixelDungeon.scene().getClass());
}
}
} );
}
};
cont.setRect(0, HEIGHT - 20, WIDTH/2 -1, 20);
add(cont);
erase.setRect(WIDTH/2 + 1, HEIGHT-20, WIDTH/2 - 1, 20);
add(erase);
resize(WIDTH, HEIGHT);
}
private void statSlot( String label, String value ) {
RenderedText txt = PixelScene.renderText( label, 8 );
txt.y = pos;
add( txt );
txt = PixelScene.renderText( value, 8 );
txt.x = WIDTH * 0.6f;
txt.y = pos;
PixelScene.align(txt);
add( txt );
pos += GAP + txt.baseLine();
}
private void statSlot( String label, int value ) {
statSlot( label, Integer.toString( value ) );
}
}

View File

@ -0,0 +1,317 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2017 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.InterlevelScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.IntroScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.noosa.ColorBlock;
import com.watabou.noosa.Game;
import com.watabou.noosa.Image;
import com.watabou.noosa.RenderedText;
import com.watabou.noosa.ui.Button;
import com.watabou.noosa.ui.Component;
public class WndStartGame extends Window {
private static final int WIDTH = 120;
private static final int HEIGHT = 140;
public WndStartGame(final int slot){
Badges.loadGlobal();
RenderedText title = PixelScene.renderText(Messages.get(this, "title"), 12 );
title.hardlight(Window.TITLE_COLOR);
title.x = (WIDTH - title.width())/2f;
title.y = 2;
add(title);
float heroBtnSpacing = (WIDTH - 4*HeroBtn.WIDTH)/5f;
float curX = heroBtnSpacing;
for (HeroClass cl : HeroClass.values()){
HeroBtn button = new HeroBtn(cl);
button.setRect(curX, title.baseLine() + 4, HeroBtn.WIDTH, HeroBtn.HEIGHT);
curX += HeroBtn.WIDTH + heroBtnSpacing;
add(button);
}
ColorBlock separator = new ColorBlock(1, 1, 0xFF222222);
separator.size(WIDTH, 1);
separator.x = 0;
separator.y = title.baseLine() + 6 + HeroBtn.HEIGHT;
add(separator);
HeroPane ava = new HeroPane();
ava.setRect(20, separator.y + 2, WIDTH-30, 80);
add(ava);
RedButton start = new RedButton(Messages.get(this, "start")){
@Override
protected void onClick() {
if (GamesInProgress.selectedClass == null) return;
super.onClick();
GamesInProgress.curSlot = slot;
Dungeon.hero = null;
InterlevelScene.mode = InterlevelScene.Mode.DESCEND;
if (SPDSettings.intro()) {
SPDSettings.intro( false );
Game.switchScene( IntroScene.class );
} else {
Game.switchScene( InterlevelScene.class );
}
}
@Override
public void update() {
if( !visible && GamesInProgress.selectedClass != null){
visible = true;
}
super.update();
}
};
start.visible = false;
start.setRect(0, HEIGHT - 20, WIDTH, 20);
add(start);
resize(WIDTH, HEIGHT);
}
private static class HeroBtn extends Button {
private HeroClass cl;
private Image hero;
private static final int WIDTH = 24;
private static final int HEIGHT = 16;
HeroBtn ( HeroClass cl ){
super();
this.cl = cl;
if (cl == HeroClass.WARRIOR){
hero = new Image(Assets.WARRIOR, 0, 90, 12, 15);
} else if (cl == HeroClass.MAGE){
hero = new Image(Assets.MAGE, 0, 90, 12, 15);
} else if (cl == HeroClass.ROGUE){
hero = new Image(Assets.ROGUE, 0, 90, 12, 15);
} else if (cl == HeroClass.HUNTRESS){
hero = new Image(Assets.HUNTRESS, 0, 90, 12, 15);
}
add(hero);
}
@Override
protected void layout() {
super.layout();
if (hero != null){
hero.x = x + (width - hero.width()) / 2f;
hero.y = y + (height - hero.height()) / 2f;
PixelScene.align(hero);
}
}
@Override
public void update() {
super.update();
if (cl != GamesInProgress.selectedClass){
if (cl == HeroClass.HUNTRESS && !Badges.isUnlocked(Badges.Badge.BOSS_SLAIN_3)){
hero.brightness( 0f );
} else {
hero.brightness(0.6f);
}
} else {
hero.brightness(1f);
}
}
@Override
protected void onClick() {
super.onClick();
if( cl == HeroClass.HUNTRESS && !Badges.isUnlocked(Badges.Badge.BOSS_SLAIN_3)){
ShatteredPixelDungeon.scene().add(
new WndMessage(Messages.get(WndStartGame.class, "huntress_unlock")));
} else {
GamesInProgress.selectedClass = cl;
}
}
}
private class HeroPane extends Component {
private HeroClass cl;
private Image avatar;
private IconButton heroItem;
private IconButton heroLoadout;
private IconButton heroMisc;
private IconButton heroSubclass;
private RenderedText name;
private static final int BTN_SIZE = 20;
@Override
protected void createChildren() {
super.createChildren();
avatar = new Image(Assets.AVATARS);
avatar.scale.set(2f);
add(avatar);
heroItem = new IconButton(){
@Override
protected void onClick() {
if (cl == null) return;
ShatteredPixelDungeon.scene().add(new WndMessage(Messages.get(cl, cl.name() + "_desc_item")));
}
};
heroItem.setSize(BTN_SIZE, BTN_SIZE);
add(heroItem);
heroLoadout = new IconButton(){
@Override
protected void onClick() {
if (cl == null) return;
ShatteredPixelDungeon.scene().add(new WndMessage(Messages.get(cl, cl.name() + "_desc_loadout")));
}
};
heroLoadout.setSize(BTN_SIZE, BTN_SIZE);
add(heroLoadout);
heroMisc = new IconButton(){
@Override
protected void onClick() {
if (cl == null) return;
ShatteredPixelDungeon.scene().add(new WndMessage(Messages.get(cl, cl.name() + "_desc_misc")));
}
};
heroMisc.setSize(BTN_SIZE, BTN_SIZE);
add(heroMisc);
heroSubclass = new IconButton(new ItemSprite(ItemSpriteSheet.MASTERY, null)){
@Override
protected void onClick() {
if (cl == null) return;
String msg = Messages.get(cl, cl.name() + "_desc_subclasses");
for (HeroSubClass sub : cl.subClasses()){
msg += "\n\n" + sub.desc();
}
ShatteredPixelDungeon.scene().add(new WndMessage(msg));
}
};
heroSubclass.setSize(BTN_SIZE, BTN_SIZE);
add(heroSubclass);
name = PixelScene.renderText(12);
add(name);
visible = false;
}
@Override
protected void layout() {
super.layout();
avatar.x = x;
avatar.y = y + (height - avatar.height() - name.baseLine() - 2)/2f;
PixelScene.align(avatar);
name.x = x + (avatar.width() - name.width())/2f;
name.y = avatar.y + avatar.height() + 2;
PixelScene.align(name);
heroItem.setPos(x + width - BTN_SIZE, y);
heroLoadout.setPos(x + width - BTN_SIZE, heroItem.bottom());
heroMisc.setPos(x + width - BTN_SIZE, heroLoadout.bottom());
heroSubclass.setPos(x + width - BTN_SIZE, heroMisc.bottom());
}
@Override
public synchronized void update() {
super.update();
if (GamesInProgress.selectedClass != cl){
cl = GamesInProgress.selectedClass;
if (cl != null) {
avatar.frame(cl.ordinal() * 24, 0, 24, 32);
name.text(Messages.capitalize(cl.title()));
switch(cl){
case WARRIOR:
heroItem.icon(new ItemSprite(ItemSpriteSheet.SEAL, null));
heroLoadout.icon(new ItemSprite(ItemSpriteSheet.WORN_SHORTSWORD, null));
heroMisc.icon(new ItemSprite(ItemSpriteSheet.RATION, null));
break;
case MAGE:
heroItem.icon(new ItemSprite(ItemSpriteSheet.MAGES_STAFF, null));
heroLoadout.icon(new ItemSprite(ItemSpriteSheet.HOLDER, null));
heroMisc.icon(new ItemSprite(ItemSpriteSheet.WAND_MAGIC_MISSILE, null));
break;
case ROGUE:
heroItem.icon(new ItemSprite(ItemSpriteSheet.ARTIFACT_CLOAK, null));
heroLoadout.icon(new ItemSprite(ItemSpriteSheet.DAGGER, null));
heroMisc.icon(Icons.get(Icons.DEPTH));
break;
case HUNTRESS:
heroItem.icon(new ItemSprite(ItemSpriteSheet.BOOMERANG, null));
heroLoadout.icon(new ItemSprite(ItemSpriteSheet.KNUCKLEDUSTER, null));
heroMisc.icon(new ItemSprite(ItemSpriteSheet.DART, null));
break;
}
layout();
visible = true;
} else {
visible = false;
}
}
}
}
}

View File

@ -249,32 +249,28 @@ actors.hero.hero.pain_resist=The pain helps you resist the urge to sleep.
actors.hero.hero.revive=The ankh explodes with life-giving energy!
actors.hero.heroclass.warrior=warrior
actors.hero.heroclass.warrior_perk1=The Warrior starts with a broken seal which he can affix to armor.
actors.hero.heroclass.warrior_perk2=The Warrior will slowly generate a shield while he is wearing armor with the seal affixed.
actors.hero.heroclass.warrior_perk3=The seal can be moved between armor, carrying a single upgrade with it.
actors.hero.heroclass.warrior_perk4=Any piece of food restores some health when eaten.
actors.hero.heroclass.warrior_perk5=Potions of Healing are automatically identified.
actors.hero.heroclass.warrior_desc_item=The Warrior starts with a _unique broken seal,_ which he can affix to armor.\n\nHe will slowly generate shielding over his health while he is wearing armor with the seal affixed.\n\nThe seal can be moved between armor, carrying a single upgrade with it.
actors.hero.heroclass.warrior_desc_loadout=The Warrior starts with a _worn shortsword,_ which offers more direct damage than other starter weapons.\n\nThe Warrior starts with _three throwing stones,_ which offer limited ranged damage.\n\nThe Warrior starts with a _potion bandolier,_ which can store various potions and protect them from the cold.
actors.hero.heroclass.warrior_desc_misc=The Warrior automatically identifies potions of healing.\n\nThe Warrior regains a small amount of HP whenever he eats food.
actors.hero.heroclass.warrior_desc_subclasses=A subclass can be chosen after defeating the second boss. The Warrior has two subclasses:
actors.hero.heroclass.mage=mage
actors.hero.heroclass.mage_perk1=The Mage starts with a unique Staff, which can be imbued with the properties of a wand.
actors.hero.heroclass.mage_perk2=The Mage's staff can be used as a melee weapon or a more powerful wand.
actors.hero.heroclass.mage_perk3=The Mage partially identifies wands after using them.
actors.hero.heroclass.mage_perk4=When eaten, any piece of food restores 1 charge for all wands in the inventory.
actors.hero.heroclass.mage_perk5=Scrolls of Upgrade are automatically identified.
actors.hero.heroclass.mage_desc_item=The Mage starts with a _unique staff,_ which can be imbued with the properties of a wand.\n\nThe staff recharges significantly faster than a wand, and has 1 more maximum charge.\n\nThe staff starts out imbued with magic missile.
actors.hero.heroclass.mage_desc_loadout=The Mage starts with his staff as his melee weapon. The staff deals less melee damage than other starter weapons.\n\nThe Mage can use the magic in his staff to attack at range.\n\nThe Mage starts with a _scroll holder,_ which can store various scrolls and protect them from fire.
actors.hero.heroclass.mage_desc_misc=The Mage partially identified wands the moment he uses them.\n\nThe Mage automatically identifies scrolls of upgrade/\n\nThe Mage regains a small amount of wand and staff charge whenever he eats food.
actors.hero.heroclass.mage_desc_subclasses=A subclass can be chosen after defeating the second boss. The Mage has two subclasses:
actors.hero.heroclass.rogue=rogue
actors.hero.heroclass.rogue_perk1=The Rogue starts with a unique Cloak of Shadows, which he can use to become invisible at will.
actors.hero.heroclass.rogue_perk2=The Rogue's cloak is an artifact, it becomes more powerful as he uses it.
actors.hero.heroclass.rogue_perk3=The Rogue detects secrets and traps from farther away than other heroes.
actors.hero.heroclass.rogue_perk4=The Rogue is able to find more secrets hidden in the dungeon than other heroes.
actors.hero.heroclass.rogue_perk5=Scrolls of Magic Mapping are automatically identified.
actors.hero.heroclass.rogue_desc_item=The Rogue starts with a unique artifact: the _Cloak of Shadows,_ which he can use to become invisible at will.\n\nLike all artifacts, the cloak cannot be directly upgraded. Instead it becomes more powerful as it is used.
actors.hero.heroclass.rogue_desc_loadout=The Rogue starts with a _dagger,_ which deals more damage when surprising enemies.\n\nThe Rogue starts with _three throwing knives,_ which offer some ranged damage and deal more damage to surprised enemies.\n\nThe Rogue starts with a _velvet pouch,_ which can store small items like seeds and runestones.
actors.hero.heroclass.rogue_desc_misc=The Rogue detects secrets and traps from a greater distance.\n\nThe Rogue is able to find more secrets hidden in the dungeon.\n\nThe Rogue automatically identifies scrolls of magic mapping.
actors.hero.heroclass.rogue_desc_subclasses=A subclass can be chosen after defeating the second boss. The Rogue has two subclasses:
actors.hero.heroclass.huntress=huntress
actors.hero.heroclass.huntress_perk1=The Huntress starts with a unique upgradeable boomerang.
actors.hero.heroclass.huntress_perk2=The Huntress gains bonus damage from excess strength on thrown weapons.
actors.hero.heroclass.huntress_perk3=The Huntress can use thrown weapons for longer before they break.
actors.hero.heroclass.huntress_perk4=The Huntress senses neighbouring monsters even if they are hidden behind obstacles.
actors.hero.heroclass.huntress_perk5=Potions of Mind Vision are automatically identified.
actors.hero.heroclass.huntress_desc_item=The Huntress starts with a _unique boomerang,_ which can be thrown an infinite number of times.\n\nThe boomerang is upgradeable and can be imbued and enchanted, just like a melee weapon.
actors.hero.heroclass.huntress_desc_loadout=The Huntress starts with _knuckledusters,_ which attack much faster than other starter weapons.\n\nThe Huntress starts with her boomerang as a ranged option.\n\nThe Huntress starts with a _velvet pouch,_ which can store small items like seeds and runestones.
actors.hero.heroclass.huntress_desc_misc=The Huntress gains bonus damage from excess strength on thrown weapons.\n\nThe Huntress can use thrown weapons for longer before they break.\n\nThe Huntress senses nearby enemies even if they are hidden behind obstacles.\n\nThe Huntress automatically identifies potions of mind vision.
actors.hero.heroclass.huntress_desc_subclasses=A subclass can be chosen after defeating the second boss. The Huntress has two subclasses:
actors.hero.herosubclass.gladiator=gladiator
actors.hero.herosubclass.gladiator_desc=A successful attack with a melee weapon allows the _Gladiator_ to start a combo. Building combo allows him to use unique finisher moves.

View File

@ -44,16 +44,8 @@ scenes.rankingsscene.total=Games Played:
scenes.rankingsscene.no_games=No games have been played yet.
scenes.rankingsscene.no_info=No additional information
scenes.startscene.load=Load Game
scenes.startscene.title=Games in Progress
scenes.startscene.new=New Game
scenes.startscene.erase=Erases Progress
scenes.startscene.depth_level=Depth: %1$d, Level: %2$d
scenes.startscene.really=Do you really want to start new game?
scenes.startscene.warning=Your current game progress will be erased.
scenes.startscene.yes=Yes, start new game
scenes.startscene.no=No, return to main menu
scenes.startscene.unlock=To unlock the Huntress,\nslay the boss on floor 15.
scenes.startscene.need_to_win=To unlock Challenges, win the game with any character class.
scenes.surfacescene.exit=Game Over

View File

@ -27,6 +27,19 @@ windows.wndgame.menu=Main Menu
windows.wndgame.exit=Exit Game
windows.wndgame.return=Return to Game
windows.wndgameinprogress.title=Level %1$d %2$s
windows.wndgameinprogress.exp=Experience
windows.wndgameinprogress.str=Strength
windows.wndgameinprogress.health=Health
windows.wndgameinprogress.gold=Gold Collected
windows.wndgameinprogress.depth=Maximum Depth
windows.wndgameinprogress.continue=Continue
windows.wndgameinprogress.erase=Erase
windows.wndgameinprogress.erase_warn_title=Are you sure you want to delete this save?
windows.wndgameinprogress.erase_warn_body=All progress on this game will be lot
windows.wndgameinprogress.erase_warn_yes=Yes, delete this save
windows.wndgameinprogress.erase_warn_no=No, I want to continue
windows.wndhero.stats=Stats
windows.wndhero.buffs=Buffs
windows.wndhero$statstab.title=Level %1$d %2$s
@ -119,6 +132,10 @@ windows.wndsettings$audiotab.music_mute=Mute Music
windows.wndsettings$audiotab.sfx_vol=SFX Volume
windows.wndsettings$audiotab.sfx_mute=Mute SFX
windows.wndstartgame.title=Choose Your Hero
windows.wndstartgame.huntress_unlock=Defeat the boss on floor 15 to unlock this character
windows.wndstartgame.start=Start Game
windows.wndstory.sewers=The Dungeon lies right beneath the City, its upper levels actually constitute the City's sewer system.\n\nAs dark energy has crept up from below the usually harmless sewer creatures have become more and more dangerous. The city sends guard patrols down here to try and maintain safety for those above, but they are slowly failing.\n\nThis place is dangerous, but at least the evil magic at work here is weak.
windows.wndstory.prison=Many years ago a prison was built here to house dangerous criminals. Tightly regulated and secure, convicts from all over the land were brought here to serve time.\n\nBut soon dark miasma started to creep from below, twisting the minds of guard and prisoner alike.\n\nIn response to the mounting chaos, the city sealed off the entire prison. Nobody knows what became of those who were left for dead within these walls...
windows.wndstory.caves=The caves, which stretch down under the abandoned prison, are sparcely populated. They lie too deep to be exploited by the City and they are too poor in minerals to interest the dwarves. In the past there was a trade outpost somewhere here on the route between these two states, but it has perished since the decline of Dwarven Metropolis. Only omnipresent gnolls and subterranean animals dwell here now.