v1.4.0: added a short UI tutorial when the player first plays the game

This commit is contained in:
Evan Debenham
2022-09-10 11:47:41 -04:00
parent efa0fc3678
commit c430a84038
8 changed files with 114 additions and 49 deletions

View File

@@ -48,6 +48,12 @@ scenes.gamescene.pick_up=Pick Up
scenes.gamescene.purchase=Purchase
scenes.gamescene.trample=Trample
scenes.gamescene.examine=Examine
scenes.gamescene.tutorial_move_mobile=Tap a location to to move and interact.
scenes.gamescene.tutorial_move_desktop=Use the mouse or arrow keys to move and interact.
scenes.gamescene.tutorial_move_controller=Select a location or use the left stick to move or interact.
scenes.gamescene.tutorial_guidebook=Select the blinking journal button to read the book you just picked up.
scenes.gamescene.tutorial_ui_mobile=Hero info is on the top left, and your inventory and game actions are below, good luck!
scenes.gamescene.tutorial_ui_desktop=Hero info is below, and your inventory and game actions are to the right, good luck!
scenes.heroselectscene.title=Choose Your Hero
scenes.heroselectscene.options=Game Options

View File

@@ -2020,6 +2020,11 @@ public class Hero extends Char {
} else {
chance = 0.2f - (Dungeon.depth / 100f);
}
//don't want to let the player search though hidden doors in tutorial
if (SPDSettings.intro()){
chance = 0;
}
if (Random.Float() < chance) {

View File

@@ -27,11 +27,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournal;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
import com.watabou.noosa.Game;
import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
public class Guidebook extends Item {
@@ -42,35 +39,10 @@ public class Guidebook extends Item {
@Override
public final boolean doPickUp(Hero hero, int pos) {
GameScene.pickUpJournal(this, pos);
String page = Document.GUIDE_INTRO;
Game.runOnRenderThread(new Callback() {
@Override
public void call() {
GameScene.show(new WndStory(Document.ADVENTURERS_GUIDE.pageSprite(page),
Document.ADVENTURERS_GUIDE.pageTitle(page),
Document.ADVENTURERS_GUIDE.pageBody(page)){
float elapsed = 0;
@Override
public void update() {
elapsed += Game.elapsed;
super.update();
}
@Override
public void hide() {
//prevents accidentally closing
if (elapsed >= 1) {
super.hide();
}
}
});
}
});
Document.ADVENTURERS_GUIDE.readPage(Document.GUIDE_INTRO);
GameScene.flashForDocument(Document.ADVENTURERS_GUIDE, Document.GUIDE_INTRO);
Sample.INSTANCE.play( Assets.Sounds.ITEM );
hero.spendAndNext( TIME_TO_PICK_UP );
GameLog.wipe();
return true;
}

View File

@@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.painters;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
@@ -247,11 +248,13 @@ public abstract class RegularPainter extends Painter {
d.type = Room.Door.Type.UNLOCKED;
}
//entrance doors on floor 1 are hidden during tutorial
//entrance doors on floor 2 are hidden if the player hasn't picked up 2nd guidebook page
if (Dungeon.depth == 2
&& !Document.ADVENTURERS_GUIDE.isPageFound(Document.GUIDE_SEARCHING)
&& r instanceof EntranceRoom){
d.type = Room.Door.Type.HIDDEN;
if (r instanceof EntranceRoom || n instanceof EntranceRoom){
if ((Dungeon.depth == 1 && SPDSettings.intro())
|| (Dungeon.depth == 2 && !Document.ADVENTURERS_GUIDE.isPageFound(Document.GUIDE_SEARCHING))) {
d.type = Room.Door.Type.HIDDEN;
}
}
}

View File

@@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
@@ -50,7 +51,16 @@ public class EntranceRoom extends StandardRoom {
return false;
}
public void paint( Level level ) {
@Override
public boolean canPlaceTrap(Point p) {
if (Dungeon.depth == 1) {
return false;
} else {
return super.canPlaceTrap(p);
}
}
public void paint(Level level ) {
Painter.fill( level, this, Terrain.WALL );
Painter.fill( level, this, 1, Terrain.EMPTY );
@@ -75,7 +85,8 @@ public class EntranceRoom extends StandardRoom {
Random.pushGenerator();
//places the first guidebook page on floor 1
if (Dungeon.depth == 1 && !Document.ADVENTURERS_GUIDE.isPageRead(Document.GUIDE_INTRO)){
if (Dungeon.depth == 1 &&
(!Document.ADVENTURERS_GUIDE.isPageRead(Document.GUIDE_INTRO) || SPDSettings.intro() )){
int pos;
do {
//can't be on bottom row of tiles

View File

@@ -60,6 +60,7 @@ import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
@@ -114,6 +115,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndResurrect;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
import com.watabou.glwrap.Blending;
import com.watabou.input.ControllerHandler;
import com.watabou.input.PointerEvent;
import com.watabou.noosa.Camera;
import com.watabou.noosa.Game;
@@ -127,6 +129,7 @@ import com.watabou.noosa.Visual;
import com.watabou.noosa.audio.Music;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.particles.Emitter;
import com.watabou.utils.Callback;
import com.watabou.utils.DeviceCompat;
import com.watabou.utils.GameMath;
import com.watabou.utils.Point;
@@ -479,6 +482,21 @@ public class GameScene extends PixelScene {
&& (InterlevelScene.mode == InterlevelScene.Mode.DESCEND || InterlevelScene.mode == InterlevelScene.Mode.FALL)) {
GLog.h(Messages.get(this, "descend"), Dungeon.depth);
Sample.INSTANCE.play(Assets.Sounds.DESCEND);
//Tutorial
if (SPDSettings.intro()){
if (ControllerHandler.isControllerConnected()){
GLog.p(Messages.get(GameScene.class, "tutorial_move_controller"));
} else if (SPDSettings.interfaceSize() == 0){
GLog.p(Messages.get(GameScene.class, "tutorial_move_mobile"));
} else {
GLog.p(Messages.get(GameScene.class, "tutorial_move_desktop"));
}
toolbar.visible = false;
status.visible = false;
if (inventory != null) inventory.visible = false;
}
for (Char ch : Actor.chars()){
if (ch instanceof DriedRose.GhostHero){
@@ -771,15 +789,19 @@ public class GameScene extends PixelScene {
float tagWidth = Tag.SIZE + (tagsOnLeft ? insets.left : insets.right);
float tagLeft = tagsOnLeft ? 0 : uiCamera.width - tagWidth;
float invWidth = (scene.inventory != null && scene.inventory.visible) ? scene.inventory.width() : 0;
float y = SPDSettings.interfaceSize() == 0 ? scene.toolbar.top()-2 : scene.status.top()-2;
if (tagsOnLeft) {
scene.log.setRect(tagWidth, y, uiCamera.width - tagWidth - insets.right - invWidth, 0);
} else if (invWidth > 0) {
scene.log.setRect(insets.left, y, uiCamera.width - invWidth, 0);
if (SPDSettings.interfaceSize() == 0){
if (tagsOnLeft) {
scene.log.setRect(tagWidth, y, uiCamera.width - tagWidth - insets.right, 0);
} else {
scene.log.setRect(insets.left, y, uiCamera.width - tagWidth - insets.left, 0);
}
} else {
scene.log.setRect(insets.left, y, uiCamera.width - tagWidth - insets.left, 0);
if (tagsOnLeft) {
scene.log.setRect(tagWidth, y, 160 - tagWidth, 0);
} else {
scene.log.setRect(insets.left, y, 160 - insets.left, 0);
}
}
float pos = scene.toolbar.top();
@@ -1021,9 +1043,42 @@ public class GameScene extends PixelScene {
if (scene != null) scene.menu.pickup( item, pos );
}
//TODO currently only works with guidebooks
public static void flashForDocument( Document doc, String page ){
if (scene != null) scene.menu.flashForPage( doc, page );
if (scene != null) {
scene.menu.flashForPage( doc, page );
//we use a callback here so that regular pickup text appears first
if (SPDSettings.intro()) {
Game.runOnRenderThread(new Callback() {
@Override
public void call() {
GLog.p(Messages.get(GameScene.class, "tutorial_guidebook"));
}
});
}
}
}
public static void endIntro(){
if (scene != null){
SPDSettings.intro(false);
//TODO this is very sudden, should have UI and doors fade in
scene.status.visible = true;
scene.toolbar.visible = true;
if (scene.inventory != null) scene.inventory.visible = true;
GameLog.wipe();
if (SPDSettings.interfaceSize() == 0){
GLog.p(Messages.get(GameScene.class, "tutorial_ui_mobile"));
} else {
GLog.p(Messages.get(GameScene.class, "tutorial_ui_desktop"));
}
//clear hidden doors, it's floor 1 so there are only the entrance ones
for (int i = 0; i < Dungeon.level.length(); i++){
if (Dungeon.level.map[i] == Terrain.SECRET_DOOR){
Dungeon.level.discover(i);
}
}
}
}
public static void updateKeyDisplay(){

View File

@@ -86,6 +86,11 @@ public class WelcomeScene extends PixelScene {
if (ShatteredPixelDungeon.versionCode == previousVersion && !SPDSettings.intro()) {
ShatteredPixelDungeon.switchNoFade(TitleScene.class);
return;
} else {
//TODO temporary so alpha/beta players can test the tutorial
if (previousVersion <= 651){
SPDSettings.intro(true);
}
}
Music.INSTANCE.playTracks(
@@ -181,7 +186,6 @@ public class WelcomeScene extends PixelScene {
String message;
if (previousVersion == 0 || SPDSettings.intro()) {
message = Document.INTROS.pageBody(0);
Document.INTROS.readPage(0);
} else if (previousVersion <= ShatteredPixelDungeon.versionCode) {
if (previousVersion < LATEST_UPDATE){
message = Messages.get(this, "update_intro");
@@ -198,6 +202,7 @@ public class WelcomeScene extends PixelScene {
} else {
message = Messages.get(this, "what_msg");
}
Document.INTROS.readPage(0);
message = "Greetings Alpha Testers!\n\nAll of the gameplay changes for v1.4.0 are in place, but there's still a bit of UI/UX work and lore to finish up.\n\nKeep in mind that this is an early release, so bugs are to be expected. Please let me know if you encounter any!";
@@ -206,7 +211,7 @@ public class WelcomeScene extends PixelScene {
text.setPos((w - text.width()) / 2f, (topRegion + 2) + (textSpace - text.height())/2);
add(text);
if (previousVersion == 0 && ControllerHandler.isControllerConnected()){
if (SPDSettings.intro() && ControllerHandler.isControllerConnected()){
addToFront(new WndHardNotification(Icons.CONTROLLER.get(),
Messages.get(WelcomeScene.class, "controller_title"),
Messages.get(WelcomeScene.class, "controller_body"),

View File

@@ -316,7 +316,15 @@ public class MenuPane extends Component {
} else if (flashingDoc.pageNames().contains(flashingPage)){
GameScene.show( new WndStory( flashingDoc.pageSprite(flashingPage),
flashingDoc.pageTitle(flashingPage),
flashingDoc.pageBody(flashingPage) ));
flashingDoc.pageBody(flashingPage) ){
@Override
public void hide() {
super.hide();
if (SPDSettings.intro()){
GameScene.endIntro();
}
}
});
flashingDoc.readPage(flashingPage);
} else {
GameScene.show( new WndJournal() );