diff --git a/core/src/main/assets/splashes/huntress.jpg b/core/src/main/assets/splashes/huntress.jpg new file mode 100644 index 000000000..118d9d9e7 Binary files /dev/null and b/core/src/main/assets/splashes/huntress.jpg differ diff --git a/core/src/main/assets/splashes/mage.jpg b/core/src/main/assets/splashes/mage.jpg new file mode 100644 index 000000000..d1de34e54 Binary files /dev/null and b/core/src/main/assets/splashes/mage.jpg differ diff --git a/core/src/main/assets/splashes/rogue.jpg b/core/src/main/assets/splashes/rogue.jpg new file mode 100644 index 000000000..7f19bb05d Binary files /dev/null and b/core/src/main/assets/splashes/rogue.jpg differ diff --git a/core/src/main/assets/splashes/warrior.jpg b/core/src/main/assets/splashes/warrior.jpg new file mode 100644 index 000000000..72124e8ec Binary files /dev/null and b/core/src/main/assets/splashes/warrior.jpg differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java index 64aafcb12..ac9befbd5 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java @@ -178,6 +178,14 @@ public class Assets { }; } + public static class Splashes { + + public static final String WARRIOR = "splashes/warrior.jpg"; + public static final String MAGE = "splashes/mage.jpg"; + public static final String ROGUE = "splashes/rogue.jpg"; + public static final String HUNTRESS = "splashes/huntress.jpg"; + } + public static class Sprites { public static final String ITEMS = "sprites/items.png"; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java index 8162b3730..c62d9e846 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java @@ -214,6 +214,19 @@ public enum HeroClass { return Assets.Sprites.HUNTRESS; } } + + public String splashArt(){ + switch (this) { + case WARRIOR: default: + return Assets.Splashes.WARRIOR; + case MAGE: + return Assets.Splashes.MAGE; + case ROGUE: + return Assets.Splashes.ROGUE; + case HUNTRESS: + return Assets.Splashes.HUNTRESS; + } + } public String[] perks() { switch (this) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java new file mode 100644 index 000000000..9857bd6be --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java @@ -0,0 +1,415 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2020 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 + */ + +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.actors.hero.HeroSubClass; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.shatteredpixel.shatteredpixeldungeon.ui.ActionIndicator; +import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; +import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; +import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndChallenges; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndMessage; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndStartGame; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndTabbed; +import com.watabou.input.PointerEvent; +import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.NinePatch; +import com.watabou.noosa.PointerArea; +import com.watabou.utils.DeviceCompat; +import com.watabou.utils.GameMath; + +import java.util.ArrayList; + +public class HeroSelectScene extends PixelScene { + + private Image background; + private NinePatch border; + private RenderedTextBlock prompt; + + //fading UI elements + private ArrayList heroBtns = new ArrayList<>(); + private StyledButton startBtn; + private IconButton infoButton; + private IconButton challengeButton; + private IconButton btnExit; + + @Override + public void create() { + super.create(); + + background = new Image(HeroClass.WARRIOR.splashArt()){ + @Override + public void update() { + if (rm > 1f){ + rm -= Game.elapsed; + gm = bm = rm; + } else { + rm = gm = bm = 1; + } + } + }; + background.scale.set(Camera.main.height/background.height); + + background.x = (Camera.main.width - background.width())/2f; + background.y = (Camera.main.height - background.height())/2f; + background.visible = false; + PixelScene.align(background); + + border = Chrome.get(Chrome.Type.WINDOW_SILVER); + border.size(background.width()+border.marginHor()-4, background.height()+border.marginVer()-4); + border.x = background.x - border.marginLeft()+2; + border.y = background.y - border.marginTop()+2; + border.visible = false; + add(border); + add(background); + + prompt = PixelScene.renderTextBlock(Messages.get(WndStartGame.class, "title"), 12); + prompt.hardlight(Window.TITLE_COLOR); + prompt.setPos( (Camera.main.width - prompt.width())/2f, (Camera.main.height - HeroBtn.HEIGHT - prompt.height() - 4)); + PixelScene.align(prompt); + add(prompt); + + startBtn = new StyledButton(Chrome.Type.GREY_BUTTON_TR, ""){ + @Override + protected void onClick() { + super.onClick(); + + if (GamesInProgress.selectedClass == null) return; + + Dungeon.hero = null; + ActionIndicator.action = null; + InterlevelScene.mode = InterlevelScene.Mode.DESCEND; + + if (SPDSettings.intro()) { + SPDSettings.intro( false ); + Game.switchScene( IntroScene.class ); + } else { + Game.switchScene( InterlevelScene.class ); + } + } + }; + startBtn.icon(Icons.get(Icons.ENTER)); + startBtn.setSize(80, 21); + startBtn.setPos((Camera.main.width - startBtn.width())/2f, (Camera.main.height - HeroBtn.HEIGHT + 2 - startBtn.height())); + add(startBtn); + startBtn.visible = false; + + infoButton = new IconButton(Icons.get(Icons.INFO)){ + @Override + protected void onClick() { + super.onClick(); + ShatteredPixelDungeon.scene().addToFront(new WndHeroInfo(GamesInProgress.selectedClass)); + } + }; + infoButton.visible = false; + infoButton.setSize(21, 21); + add(infoButton); + + HeroClass[] classes = HeroClass.values(); + + int btnWidth = HeroBtn.MIN_WIDTH; + int curX = (Camera.main.width - btnWidth * classes.length)/2; + if (curX > 0){ + btnWidth += Math.min(curX/(classes.length/2), 15); + curX = (Camera.main.width - btnWidth * classes.length)/2; + } + + int heroBtnleft = curX; + for (HeroClass cl : classes){ + HeroBtn button = new HeroBtn(cl); + button.setRect(curX, Camera.main.height-HeroBtn.HEIGHT+3, btnWidth, HeroBtn.HEIGHT); + curX += btnWidth; + add(button); + heroBtns.add(button); + } + + challengeButton = new IconButton( + Icons.get( SPDSettings.challenges() > 0 ? Icons.CHALLENGE_ON :Icons.CHALLENGE_OFF)){ + @Override + protected void onClick() { + ShatteredPixelDungeon.scene().addToFront(new WndChallenges(SPDSettings.challenges(), true) { + public void onBackPressed() { + super.onBackPressed(); + icon(Icons.get(SPDSettings.challenges() > 0 ? Icons.CHALLENGE_ON : Icons.CHALLENGE_OFF)); + } + } ); + } + + @Override + public void update() { + if( !visible && GamesInProgress.selectedClass != null){ + visible = true; + } + super.update(); + } + }; + challengeButton.setRect(heroBtnleft + 16, Camera.main.height-HeroBtn.HEIGHT-16, 21, 21); + challengeButton.visible = false; + + if (DeviceCompat.isDebug() || Badges.isUnlocked(Badges.Badge.VICTORY)){ + add(challengeButton); + } else { + Dungeon.challenges = 0; + SPDSettings.challenges(0); + } + + btnExit = new ExitButton(); + btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); + add( btnExit ); + + PointerArea fadeResetter = new PointerArea(0, 0, Camera.main.width, Camera.main.height){ + @Override + public boolean onSignal(PointerEvent event) { + resetFade(); + return false; + } + }; + add(fadeResetter); + resetFade(); + + if (GamesInProgress.selectedClass != null){ + setSelectedHero(GamesInProgress.selectedClass); + } + + fadeIn(); + + } + + private void setSelectedHero(HeroClass cl){ + GamesInProgress.selectedClass = cl; + + background.texture( cl.splashArt() ); + background.visible = true; + background.hardlight(1.5f,1.5f,1.5f); + border.visible = true; + + prompt.visible = false; + startBtn.visible = true; + startBtn.text(Messages.titleCase(cl.title())); + startBtn.textColor(Window.TITLE_COLOR); + startBtn.setSize(startBtn.reqWidth() + 8, 21); + startBtn.setPos((Camera.main.width - startBtn.width())/2f, startBtn.top()); + PixelScene.align(startBtn); + + infoButton.visible = true; + infoButton.setPos(startBtn.right(), startBtn.top()); + + challengeButton.visible = true; + challengeButton.setPos(startBtn.left()-challengeButton.width(), startBtn.top()); + } + + private float uiAlpha; + + @Override + public void update() { + super.update(); + //do not fade when a window is open + for (Object v : members){ + if (v instanceof Window) resetFade(); + } + if (GamesInProgress.selectedClass != null) { + if (uiAlpha > 0f){ + uiAlpha -= Game.elapsed/4f; + } + float alpha = GameMath.gate(0f, uiAlpha, 1f); + for (StyledButton b : heroBtns){ + b.alpha(alpha); + } + startBtn.alpha(alpha); + btnExit.icon().alpha(alpha); + challengeButton.icon().alpha(alpha); + infoButton.icon().alpha(alpha); + } + } + + private void resetFade(){ + //starts fading after 4 seconds, fades over 4 seconds. + uiAlpha = 2f; + } + + private class HeroBtn extends StyledButton { + + private HeroClass cl; + + private static final int MIN_WIDTH = 20; + private static final int HEIGHT = 24; + + HeroBtn ( HeroClass cl ){ + super(Chrome.Type.GREY_BUTTON_TR, ""); + + this.cl = cl; + + icon(new Image(cl.spritesheet(), 0, 90, 12, 15)); + + } + + @Override + public void update() { + super.update(); + if (cl != GamesInProgress.selectedClass){ + if (!cl.isUnlocked()){ + icon.brightness(0.1f); + } else { + icon.brightness(0.6f); + } + } else { + icon.brightness(1f); + } + } + + @Override + protected void onClick() { + super.onClick(); + + if( !cl.isUnlocked() ){ + ShatteredPixelDungeon.scene().addToFront( new WndMessage(cl.unlockMsg())); + } else if (GamesInProgress.selectedClass == cl) { + ShatteredPixelDungeon.scene().add(new WndHeroInfo(cl)); + } else { + setSelectedHero(cl); + } + } + } + + private static class WndHeroInfo extends WndTabbed { + + private RenderedTextBlock info; + + private int WIDTH = 120; + private int MARGIN = 4; + private int INFO_WIDTH = WIDTH - MARGIN*2; + + public WndHeroInfo( HeroClass cl ){ + + Tab tab; + Image[] tabIcons; + switch (cl){ + case WARRIOR: default: + tabIcons = new Image[]{ + new ItemSprite(ItemSpriteSheet.SEAL, null), + new ItemSprite(ItemSpriteSheet.WORN_SHORTSWORD, null), + new ItemSprite(ItemSpriteSheet.RATION, null) + }; + break; + case MAGE: + tabIcons = new Image[]{ + new ItemSprite(ItemSpriteSheet.MAGES_STAFF, null), + new ItemSprite(ItemSpriteSheet.HOLDER, null), + new ItemSprite(ItemSpriteSheet.WAND_MAGIC_MISSILE, null) + }; + break; + case ROGUE: + tabIcons = new Image[]{ + new ItemSprite(ItemSpriteSheet.ARTIFACT_CLOAK, null), + new ItemSprite(ItemSpriteSheet.DAGGER, null), + Icons.get(Icons.DEPTH) + }; + break; + case HUNTRESS: + tabIcons = new Image[]{ + new ItemSprite(ItemSpriteSheet.SPIRIT_BOW, null), + new ItemSprite(ItemSpriteSheet.GLOVES, null), + new Image(Assets.Environment.TILES_SEWERS, 112, 96, 16, 16 ) + }; + break; + } + + tab = new IconTab( tabIcons[0] ){ + @Override + protected void select(boolean value) { + super.select(value); + if (value){ + info.text(Messages.get(cl, cl.name() + "_desc_item"), INFO_WIDTH); + } + } + }; + add(tab); + + tab = new IconTab( tabIcons[1] ){ + @Override + protected void select(boolean value) { + super.select(value); + if (value){ + info.text(Messages.get(cl, cl.name() + "_desc_loadout"), INFO_WIDTH); + } + } + }; + add(tab); + + tab = new IconTab( tabIcons[2] ){ + @Override + protected void select(boolean value) { + super.select(value); + if (value){ + info.text(Messages.get(cl, cl.name() + "_desc_misc"), INFO_WIDTH); + } + } + }; + add(tab); + + tab = new IconTab(new ItemSprite(ItemSpriteSheet.MASTERY, null)){ + @Override + protected void select(boolean value) { + super.select(value); + if (value){ + String msg = Messages.get(cl, cl.name() + "_desc_subclasses"); + for (HeroSubClass sub : cl.subClasses()){ + msg += "\n\n" + sub.desc(); + } + info.text(msg, INFO_WIDTH); + } + } + }; + add(tab); + + info = PixelScene.renderTextBlock(6); + info.setPos(MARGIN, MARGIN); + add(info); + + select(0); + + } + + @Override + public void select(Tab tab) { + super.select(tab); + resize(WIDTH, (int)info.bottom()+MARGIN); + layoutTabs(); + } + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java index 97bf3e392..da53240dc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java @@ -258,7 +258,9 @@ public class StartScene extends PixelScene { @Override protected void onClick() { if (newGame) { - ShatteredPixelDungeon.scene().add( new WndStartGame(slot)); + GamesInProgress.selectedClass = null; + GamesInProgress.curSlot = slot; + ShatteredPixelDungeon.switchScene(HeroSelectScene.class); } else { ShatteredPixelDungeon.scene().add( new WndGameInProgress(slot)); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java index 091ebf832..477c7ede2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java @@ -103,7 +103,9 @@ public class TitleScene extends PixelScene { @Override protected void onClick() { if (GamesInProgress.checkAll().size() == 0){ - TitleScene.this.add( new WndStartGame(1) ); + GamesInProgress.selectedClass = null; + GamesInProgress.curSlot = 1; + ShatteredPixelDungeon.switchScene(HeroSelectScene.class); } else { ShatteredPixelDungeon.switchNoFade( StartScene.class ); } @@ -113,7 +115,10 @@ public class TitleScene extends PixelScene { protected boolean onLongClick() { //making it easier to start runs quickly while debugging if (DeviceCompat.isDebug()) { - TitleScene.this.add( new WndStartGame(1) ); + GamesInProgress.selectedClass = null; + GamesInProgress.curSlot = 1; + ShatteredPixelDungeon.switchScene(HeroSelectScene.class); + //TitleScene.this.add( new WndStartGame(1) ); return true; } return super.onLongClick(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java index dbfa9a179..88c4c6fda 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java @@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.scenes; import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Chrome; +import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress; import com.shatteredpixel.shatteredpixeldungeon.Rankings; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; @@ -102,7 +103,9 @@ public class WelcomeScene extends PixelScene { super.onClick(); if (previousVersion == 0){ SPDSettings.version(ShatteredPixelDungeon.versionCode); - WelcomeScene.this.add(new WndStartGame(1)); + GamesInProgress.selectedClass = null; + GamesInProgress.curSlot = 1; + ShatteredPixelDungeon.switchScene(HeroSelectScene.class); } else { updateVersion(previousVersion); ShatteredPixelDungeon.switchScene(TitleScene.class); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndGame.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndGame.java index a0495cb9d..2a22aaaa2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndGame.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndGame.java @@ -26,6 +26,7 @@ import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.scenes.HeroSelectScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.InterlevelScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.RankingsScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.TitleScene; @@ -73,9 +74,10 @@ public class WndGame extends Window { addButton( btnStart = new RedButton( Messages.get(this, "start") ) { @Override protected void onClick() { - GamesInProgress.selectedClass = Dungeon.hero.heroClass; InterlevelScene.noStory = true; - GameScene.show(new WndStartGame(GamesInProgress.firstEmpty())); + GamesInProgress.selectedClass = Dungeon.hero.heroClass; + GamesInProgress.curSlot = GamesInProgress.firstEmpty(); + ShatteredPixelDungeon.switchScene(HeroSelectScene.class); } } ); btnStart.textColor(Window.TITLE_COLOR); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTabbed.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTabbed.java index 61432fc86..179e7cb02 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTabbed.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTabbed.java @@ -49,7 +49,7 @@ public class WndTabbed extends Window { tab.setPos( tabs.size() == 0 ? -chrome.marginLeft() + 1 : tabs.get( tabs.size() - 1 ).right(), height ); - tab.select( false ); + tab.select( tab.selected ); super.add( tab ); tabs.add( tab );