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
@@ -48,6 +48,12 @@ scenes.gamescene.pick_up=Pick Up
scenes.gamescene.purchase=Purchase scenes.gamescene.purchase=Purchase
scenes.gamescene.trample=Trample scenes.gamescene.trample=Trample
scenes.gamescene.examine=Examine 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.title=Choose Your Hero
scenes.heroselectscene.options=Game Options scenes.heroselectscene.options=Game Options
@@ -2021,6 +2021,11 @@ public class Hero extends Char {
chance = 0.2f - (Dungeon.depth / 100f); 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) { if (Random.Float() < chance) {
int oldValue = Dungeon.level.map[curr]; int oldValue = Dungeon.level.map[curr];
@@ -27,11 +27,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournal; import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
import com.watabou.noosa.Game;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
public class Guidebook extends Item { public class Guidebook extends Item {
@@ -42,35 +39,10 @@ public class Guidebook extends Item {
@Override @Override
public final boolean doPickUp(Hero hero, int pos) { public final boolean doPickUp(Hero hero, int pos) {
GameScene.pickUpJournal(this, pos); GameScene.pickUpJournal(this, pos);
String page = Document.GUIDE_INTRO; GameScene.flashForDocument(Document.ADVENTURERS_GUIDE, 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);
Sample.INSTANCE.play( Assets.Sounds.ITEM ); Sample.INSTANCE.play( Assets.Sounds.ITEM );
hero.spendAndNext( TIME_TO_PICK_UP ); hero.spendAndNext( TIME_TO_PICK_UP );
GameLog.wipe();
return true; return true;
} }
@@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.painters; package com.shatteredpixel.shatteredpixeldungeon.levels.painters;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
@@ -247,11 +248,13 @@ public abstract class RegularPainter extends Painter {
d.type = Room.Door.Type.UNLOCKED; 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 //entrance doors on floor 2 are hidden if the player hasn't picked up 2nd guidebook page
if (Dungeon.depth == 2 if (r instanceof EntranceRoom || n instanceof EntranceRoom){
&& !Document.ADVENTURERS_GUIDE.isPageFound(Document.GUIDE_SEARCHING) if ((Dungeon.depth == 1 && SPDSettings.intro())
&& r instanceof EntranceRoom){ || (Dungeon.depth == 2 && !Document.ADVENTURERS_GUIDE.isPageFound(Document.GUIDE_SEARCHING))) {
d.type = Room.Door.Type.HIDDEN; d.type = Room.Door.Type.HIDDEN;
}
} }
} }
@@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard; package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage; import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook; import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
@@ -50,7 +51,16 @@ public class EntranceRoom extends StandardRoom {
return false; 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, Terrain.WALL );
Painter.fill( level, this, 1, Terrain.EMPTY ); Painter.fill( level, this, 1, Terrain.EMPTY );
@@ -75,7 +85,8 @@ public class EntranceRoom extends StandardRoom {
Random.pushGenerator(); Random.pushGenerator();
//places the first guidebook page on floor 1 //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; int pos;
do { do {
//can't be on bottom row of tiles //can't be on bottom row of tiles
@@ -60,6 +60,7 @@ import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal; import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel; 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.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap; 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.WndResurrect;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory; import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
import com.watabou.glwrap.Blending; import com.watabou.glwrap.Blending;
import com.watabou.input.ControllerHandler;
import com.watabou.input.PointerEvent; import com.watabou.input.PointerEvent;
import com.watabou.noosa.Camera; import com.watabou.noosa.Camera;
import com.watabou.noosa.Game; 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.Music;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.particles.Emitter; import com.watabou.noosa.particles.Emitter;
import com.watabou.utils.Callback;
import com.watabou.utils.DeviceCompat; import com.watabou.utils.DeviceCompat;
import com.watabou.utils.GameMath; import com.watabou.utils.GameMath;
import com.watabou.utils.Point; import com.watabou.utils.Point;
@@ -480,6 +483,21 @@ public class GameScene extends PixelScene {
GLog.h(Messages.get(this, "descend"), Dungeon.depth); GLog.h(Messages.get(this, "descend"), Dungeon.depth);
Sample.INSTANCE.play(Assets.Sounds.DESCEND); 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()){ for (Char ch : Actor.chars()){
if (ch instanceof DriedRose.GhostHero){ if (ch instanceof DriedRose.GhostHero){
((DriedRose.GhostHero) ch).sayAppeared(); ((DriedRose.GhostHero) ch).sayAppeared();
@@ -771,15 +789,19 @@ public class GameScene extends PixelScene {
float tagWidth = Tag.SIZE + (tagsOnLeft ? insets.left : insets.right); float tagWidth = Tag.SIZE + (tagsOnLeft ? insets.left : insets.right);
float tagLeft = tagsOnLeft ? 0 : uiCamera.width - tagWidth; 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; float y = SPDSettings.interfaceSize() == 0 ? scene.toolbar.top()-2 : scene.status.top()-2;
if (tagsOnLeft) { if (SPDSettings.interfaceSize() == 0){
scene.log.setRect(tagWidth, y, uiCamera.width - tagWidth - insets.right - invWidth, 0); if (tagsOnLeft) {
} else if (invWidth > 0) { scene.log.setRect(tagWidth, y, uiCamera.width - tagWidth - insets.right, 0);
scene.log.setRect(insets.left, y, uiCamera.width - invWidth, 0); } else {
scene.log.setRect(insets.left, y, uiCamera.width - tagWidth - insets.left, 0);
}
} else { } 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(); float pos = scene.toolbar.top();
@@ -1021,9 +1043,42 @@ public class GameScene extends PixelScene {
if (scene != null) scene.menu.pickup( item, pos ); if (scene != null) scene.menu.pickup( item, pos );
} }
//TODO currently only works with guidebooks
public static void flashForDocument( Document doc, String page ){ 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(){ public static void updateKeyDisplay(){
@@ -86,6 +86,11 @@ public class WelcomeScene extends PixelScene {
if (ShatteredPixelDungeon.versionCode == previousVersion && !SPDSettings.intro()) { if (ShatteredPixelDungeon.versionCode == previousVersion && !SPDSettings.intro()) {
ShatteredPixelDungeon.switchNoFade(TitleScene.class); ShatteredPixelDungeon.switchNoFade(TitleScene.class);
return; return;
} else {
//TODO temporary so alpha/beta players can test the tutorial
if (previousVersion <= 651){
SPDSettings.intro(true);
}
} }
Music.INSTANCE.playTracks( Music.INSTANCE.playTracks(
@@ -181,7 +186,6 @@ public class WelcomeScene extends PixelScene {
String message; String message;
if (previousVersion == 0 || SPDSettings.intro()) { if (previousVersion == 0 || SPDSettings.intro()) {
message = Document.INTROS.pageBody(0); message = Document.INTROS.pageBody(0);
Document.INTROS.readPage(0);
} else if (previousVersion <= ShatteredPixelDungeon.versionCode) { } else if (previousVersion <= ShatteredPixelDungeon.versionCode) {
if (previousVersion < LATEST_UPDATE){ if (previousVersion < LATEST_UPDATE){
message = Messages.get(this, "update_intro"); message = Messages.get(this, "update_intro");
@@ -198,6 +202,7 @@ public class WelcomeScene extends PixelScene {
} else { } else {
message = Messages.get(this, "what_msg"); 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!"; 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); text.setPos((w - text.width()) / 2f, (topRegion + 2) + (textSpace - text.height())/2);
add(text); add(text);
if (previousVersion == 0 && ControllerHandler.isControllerConnected()){ if (SPDSettings.intro() && ControllerHandler.isControllerConnected()){
addToFront(new WndHardNotification(Icons.CONTROLLER.get(), addToFront(new WndHardNotification(Icons.CONTROLLER.get(),
Messages.get(WelcomeScene.class, "controller_title"), Messages.get(WelcomeScene.class, "controller_title"),
Messages.get(WelcomeScene.class, "controller_body"), Messages.get(WelcomeScene.class, "controller_body"),
@@ -316,7 +316,15 @@ public class MenuPane extends Component {
} else if (flashingDoc.pageNames().contains(flashingPage)){ } else if (flashingDoc.pageNames().contains(flashingPage)){
GameScene.show( new WndStory( flashingDoc.pageSprite(flashingPage), GameScene.show( new WndStory( flashingDoc.pageSprite(flashingPage),
flashingDoc.pageTitle(flashingPage), flashingDoc.pageTitle(flashingPage),
flashingDoc.pageBody(flashingPage) )); flashingDoc.pageBody(flashingPage) ){
@Override
public void hide() {
super.hide();
if (SPDSettings.intro()){
GameScene.endIntro();
}
}
});
flashingDoc.readPage(flashingPage); flashingDoc.readPage(flashingPage);
} else { } else {
GameScene.show( new WndJournal() ); GameScene.show( new WndJournal() );