Magic_Ling_Pixel_Dungeon/src/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java
Evan Debenham 0ba004b1fa v0.3.1d: removed strict pixel align pt.1
there is no need to enforce whole interger positions for UI elements. Especially with the recent font changes this was causing alignment problems on high resolution displays.
Elements with non-whole number sizes and positions should be used sparingly, but there's no reason to strictly align them.
2015-09-15 21:32:58 -04:00

792 lines
21 KiB
Java

/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2015 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.scenes;
import java.io.IOException;
import java.util.ArrayList;
import com.shatteredpixel.shatteredpixeldungeon.*;
import com.shatteredpixel.shatteredpixeldungeon.items.Honeypot;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.PotionBandolier;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.ScrollHolder;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.SeedPouch;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.WandHolster;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
import com.shatteredpixel.shatteredpixeldungeon.sprites.TrapSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.LootIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.ResumeIndicator;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoTrap;
import com.watabou.noosa.Camera;
import com.watabou.noosa.Game;
import com.watabou.noosa.Group;
import com.watabou.noosa.SkinnedBlock;
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.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.effects.BannerSprites;
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.EmoIcon;
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText;
import com.shatteredpixel.shatteredpixeldungeon.effects.Ripple;
import com.shatteredpixel.shatteredpixeldungeon.effects.SpellSprite;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.DiscardedItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.PlantSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.Banner;
import com.shatteredpixel.shatteredpixeldungeon.ui.BusyIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
import com.shatteredpixel.shatteredpixeldungeon.ui.HealthIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.StatusPane;
import com.shatteredpixel.shatteredpixeldungeon.ui.Toast;
import com.shatteredpixel.shatteredpixeldungeon.ui.Toolbar;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag.Mode;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndGame;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndHero;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoCell;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoItem;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoMob;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoPlant;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndMessage;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem;
import com.watabou.utils.GameMath;
import com.watabou.utils.Random;
public class GameScene extends PixelScene {
private static final String TXT_WELCOME = "Welcome to the level %d of Pixel Dungeon!";
private static final String TXT_WELCOME_BACK = "Welcome back to the level %d of Pixel Dungeon!";
private static final String TXT_CHASM = "Your steps echo across the dungeon.";
private static final String TXT_WATER = "You hear water splashing around you.";
private static final String TXT_GRASS = "The smell of vegetation is thick in the air.";
private static final String TXT_DARK = "You can hear enemies moving in the darkness...";
private static final String TXT_SECRETS = "The atmosphere hints that this floor hides many secrets.";
static GameScene scene;
private SkinnedBlock water;
private DungeonTilemap tiles;
private FogOfWar fog;
private HeroSprite hero;
private GameLog log;
private BusyIndicator busy;
private static CellSelector cellSelector;
private Group terrain;
private Group ripples;
private Group plants;
private Group traps;
private Group heaps;
private Group mobs;
private Group emitters;
private Group effects;
private Group gases;
private Group spells;
private Group statuses;
private Group emoicons;
private Toolbar toolbar;
private Toast prompt;
private AttackIndicator attack;
private LootIndicator loot;
private ResumeIndicator resume;
@Override
public void create() {
Music.INSTANCE.play( Assets.TUNE, true );
Music.INSTANCE.volume( ShatteredPixelDungeon.musicVol()/10f );
ShatteredPixelDungeon.lastClass(Dungeon.hero.heroClass.ordinal());
super.create();
Camera.main.zoom( GameMath.gate(minZoom, defaultZoom + ShatteredPixelDungeon.zoom(), maxZoom));
scene = this;
terrain = new Group();
add( terrain );
water = new SkinnedBlock(
Level.WIDTH * DungeonTilemap.SIZE,
Level.HEIGHT * DungeonTilemap.SIZE,
Dungeon.level.waterTex() );
terrain.add( water );
ripples = new Group();
terrain.add( ripples );
tiles = new DungeonTilemap();
terrain.add( tiles );
Dungeon.level.addVisuals(this);
traps = new Group();
add(traps);
int size = Dungeon.level.traps.size();
for (int i=0; i < size; i++) {
addTrapSprite( Dungeon.level.traps.valueAt( i ) );
}
plants = new Group();
add( plants );
size = Dungeon.level.plants.size();
for (int i=0; i < size; i++) {
addPlantSprite( Dungeon.level.plants.valueAt( i ) );
}
heaps = new Group();
add( heaps );
size = Dungeon.level.heaps.size();
for (int i=0; i < size; i++) {
addHeapSprite( Dungeon.level.heaps.valueAt( i ) );
}
emitters = new Group();
effects = new Group();
emoicons = new Group();
mobs = new Group();
add( mobs );
for (Mob mob : Dungeon.level.mobs) {
addMobSprite( mob );
if (Statistics.amuletObtained) {
mob.beckon( Dungeon.hero.pos );
}
}
add( emitters );
add( effects );
gases = new Group();
add( gases );
for (Blob blob : Dungeon.level.blobs.values()) {
blob.emitter = null;
addBlobSprite( blob );
}
fog = new FogOfWar( Level.WIDTH, Level.HEIGHT );
fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped );
add( fog );
brightness( ShatteredPixelDungeon.brightness() );
spells = new Group();
add( spells );
statuses = new Group();
add( statuses );
add( emoicons );
hero = new HeroSprite();
hero.place( Dungeon.hero.pos );
hero.updateArmor();
mobs.add( hero );
add( new HealthIndicator() );
add( cellSelector = new CellSelector( tiles ) );
StatusPane sb = new StatusPane();
sb.camera = uiCamera;
sb.setSize( uiCamera.width, 0 );
add( sb );
toolbar = new Toolbar();
toolbar.camera = uiCamera;
toolbar.setRect( 0,uiCamera.height - toolbar.height(), uiCamera.width, toolbar.height() );
add( toolbar );
attack = new AttackIndicator();
attack.camera = uiCamera;
add( attack );
loot = new LootIndicator();
loot.camera = uiCamera;
add( loot );
resume = new ResumeIndicator();
resume.camera = uiCamera;
add( resume );
log = new GameLog();
log.camera = uiCamera;
add( log );
layoutTags();
if (Dungeon.depth < Statistics.deepestFloor) {
GLog.i(TXT_WELCOME_BACK, Dungeon.depth);
} else {
GLog.i(TXT_WELCOME, Dungeon.depth);
if (InterlevelScene.mode == InterlevelScene.Mode.DESCEND) Sample.INSTANCE.play(Assets.SND_DESCEND);
}
switch (Dungeon.level.feeling) {
case CHASM:
GLog.w( TXT_CHASM );
break;
case WATER:
GLog.w( TXT_WATER );
break;
case GRASS:
GLog.w( TXT_GRASS );
break;
case DARK:
GLog.w( TXT_DARK );
break;
default:
}
if (Dungeon.level instanceof RegularLevel &&
((RegularLevel)Dungeon.level).secretDoors > Random.IntRange( 3, 4 )) {
GLog.w( TXT_SECRETS );
}
busy = new BusyIndicator();
busy.camera = uiCamera;
busy.x = 1;
busy.y = sb.bottom() + 1;
add( busy );
switch (InterlevelScene.mode) {
case RESURRECT:
ScrollOfTeleportation.appear( Dungeon.hero, Dungeon.level.entrance );
new Flare( 8, 32 ).color( 0xFFFF66, true ).show( hero, 2f ) ;
break;
case RETURN:
ScrollOfTeleportation.appear( Dungeon.hero, Dungeon.hero.pos );
break;
case FALL:
Chasm.heroLand();
break;
case DESCEND:
switch (Dungeon.depth) {
case 1:
WndStory.showChapter( WndStory.ID_SEWERS );
break;
case 6:
WndStory.showChapter( WndStory.ID_PRISON );
break;
case 11:
WndStory.showChapter( WndStory.ID_CAVES );
break;
case 16:
WndStory.showChapter( WndStory.ID_METROPOLIS );
break;
case 22:
WndStory.showChapter( WndStory.ID_HALLS );
break;
}
if (Dungeon.hero.isAlive() && Dungeon.depth != 22) {
Badges.validateNoKilling();
}
break;
default:
}
InterlevelScene.mode = InterlevelScene.Mode.CONTINUE;
ArrayList<Item> dropped = Dungeon.droppedItems.get( Dungeon.depth );
if (dropped != null) {
for (Item item : dropped) {
int pos = Dungeon.level.randomRespawnCell();
if (item instanceof Potion) {
((Potion)item).shatter( pos );
} else if (item instanceof Plant.Seed) {
Dungeon.level.plant( (Plant.Seed)item, pos );
} else if (item instanceof Honeypot) {
Dungeon.level.drop(((Honeypot) item).shatter(null, pos), pos);
} else {
Dungeon.level.drop( item, pos );
}
}
Dungeon.droppedItems.remove( Dungeon.depth );
}
Dungeon.hero.next();
Camera.main.target = hero;
fadeIn();
}
public void destroy() {
freezeEmitters = false;
scene = null;
Badges.saveGlobal();
super.destroy();
}
@Override
public synchronized void pause() {
try {
Dungeon.saveAll();
Badges.saveGlobal();
} catch (IOException e) {
//
}
}
@Override
public synchronized void update() {
if (Dungeon.hero == null) {
return;
}
super.update();
if (!freezeEmitters) water.offset( 0, -5 * Game.elapsed );
Actor.process();
if (Dungeon.hero.ready && Dungeon.hero.paralysed == 0) {
log.newLine();
}
if (tagAttack != attack.active || tagLoot != loot.visible || tagResume != resume.visible) {
boolean atkAppearing = attack.active && !tagAttack;
boolean lootAppearing = loot.visible && !tagLoot;
boolean resAppearing = resume.visible && !tagResume;
tagAttack = attack.active;
tagLoot = loot.visible;
tagResume = resume.visible;
if (atkAppearing || lootAppearing || resAppearing)
layoutTags();
}
cellSelector.enable(Dungeon.hero.ready);
}
private boolean tagAttack = false;
private boolean tagLoot = false;
private boolean tagResume = false;
public static void layoutTags() {
if (scene == null) return;
float tagLeft = ShatteredPixelDungeon.flipTags() ? 0 : uiCamera.width - scene.attack.width();
if (ShatteredPixelDungeon.flipTags()) {
scene.log.setRect(scene.attack.width(), scene.toolbar.top(), uiCamera.width - scene.attack.width(), 0);
} else {
scene.log.setRect(0, scene.toolbar.top(), uiCamera.width - scene.attack.width(), 0 );
}
float pos = scene.toolbar.top();
if (scene.tagAttack){
scene.attack.setPos( tagLeft, pos - scene.attack.height());
scene.attack.flip(tagLeft == 0);
pos = scene.attack.top();
}
if (scene.tagLoot) {
scene.loot.setPos( tagLeft, pos - scene.loot.height() );
scene.loot.flip(tagLeft == 0);
pos = scene.loot.top();
}
if (scene.tagResume) {
scene.resume.setPos( tagLeft, pos - scene.resume.height() );
scene.resume.flip(tagLeft == 0);
}
}
@Override
protected void onBackPressed() {
if (!cancel()) {
add( new WndGame() );
}
}
@Override
protected void onMenuPressed() {
if (Dungeon.hero.ready) {
selectItem( null, WndBag.Mode.ALL, null );
}
}
public void brightness( int value ) {
float shift;
if (value >= 0)
shift = value/2f;
else
shift = value/3f;
fog.am = 1f + shift;
fog.aa = 0f - shift;
}
private void addHeapSprite( Heap heap ) {
ItemSprite sprite = heap.sprite = (ItemSprite)heaps.recycle( ItemSprite.class );
sprite.revive();
sprite.link( heap );
heaps.add( sprite );
}
private void addDiscardedSprite( Heap heap ) {
heap.sprite = (DiscardedItemSprite)heaps.recycle( DiscardedItemSprite.class );
heap.sprite.revive();
heap.sprite.link( heap );
heaps.add( heap.sprite );
}
private void addPlantSprite( Plant plant ) {
(plant.sprite = (PlantSprite)plants.recycle( PlantSprite.class )).reset( plant );
}
private void addTrapSprite( Trap trap ) {
(trap.sprite = (TrapSprite)traps.recycle( TrapSprite.class )).reset( trap );
trap.sprite.visible = trap.visible;
}
private void addBlobSprite( final Blob gas ) {
if (gas.emitter == null) {
gases.add( new BlobEmitter( gas ) );
}
}
private void addMobSprite( Mob mob ) {
CharSprite sprite = mob.sprite();
sprite.visible = Dungeon.visible[mob.pos];
mobs.add( sprite );
sprite.link( mob );
}
private void prompt( String text ) {
if (prompt != null) {
prompt.killAndErase();
prompt = null;
}
if (text != null) {
prompt = new Toast( text ) {
@Override
protected void onClose() {
cancel();
}
};
prompt.camera = uiCamera;
prompt.setPos( (uiCamera.width - prompt.width()) / 2, uiCamera.height - 60 );
add( prompt );
}
}
private void showBanner( Banner banner ) {
banner.camera = uiCamera;
banner.x = (uiCamera.width - banner.width) / 2 ;
banner.y = (uiCamera.height - banner.height) / 3 ;
add( banner );
}
// -------------------------------------------------------
public static void add( Plant plant ) {
if (scene != null) {
scene.addPlantSprite( plant );
}
}
public static void add( Trap trap ) {
if (scene != null) {
scene.addTrapSprite( trap );
}
}
public static void add( Blob gas ) {
Actor.add( gas );
if (scene != null) {
scene.addBlobSprite( gas );
}
}
public static void add( Heap heap ) {
if (scene != null) {
scene.addHeapSprite( heap );
}
}
public static void discard( Heap heap ) {
if (scene != null) {
scene.addDiscardedSprite( heap );
}
}
public static void add( Mob mob ) {
Dungeon.level.mobs.add( mob );
Actor.add( mob );
scene.addMobSprite( mob );
}
public static void add( Mob mob, float delay ) {
Dungeon.level.mobs.add( mob );
Actor.addDelayed( mob, delay );
scene.addMobSprite( mob );
}
public static void add( EmoIcon icon ) {
scene.emoicons.add( icon );
}
public static void effect( Visual effect ) {
scene.effects.add( effect );
}
public static Ripple ripple( int pos ) {
Ripple ripple = (Ripple)scene.ripples.recycle( Ripple.class );
ripple.reset( pos );
return ripple;
}
public static SpellSprite spellSprite() {
return (SpellSprite)scene.spells.recycle( SpellSprite.class );
}
public static Emitter emitter() {
if (scene != null) {
Emitter emitter = (Emitter)scene.emitters.recycle( Emitter.class );
emitter.revive();
return emitter;
} else {
return null;
}
}
public static FloatingText status() {
return scene != null ? (FloatingText)scene.statuses.recycle( FloatingText.class ) : null;
}
public static void pickUp( Item item ) {
scene.toolbar.pickup( item );
}
public static void updateMap() {
if (scene != null) {
scene.tiles.updated.set( 0, 0, Level.WIDTH, Level.HEIGHT );
}
}
public static void updateMap( int cell ) {
if (scene != null) {
scene.tiles.updated.union( cell % Level.WIDTH, cell / Level.WIDTH );
}
}
public static void discoverTile( int pos, int oldValue ) {
if (scene != null) {
scene.tiles.discover( pos, oldValue );
}
}
public static void show( Window wnd ) {
cancelCellSelector();
scene.add( wnd );
}
public static void afterObserve() {
if (scene != null) {
scene.fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped );
for (Mob mob : Dungeon.level.mobs) {
mob.sprite.visible = Dungeon.visible[mob.pos];
}
}
}
public static void flash( int color ) {
scene.fadeIn( 0xFF000000 | color, true );
}
public static void gameOver() {
Banner gameOver = new Banner( BannerSprites.get( BannerSprites.Type.GAME_OVER ) );
gameOver.show( 0x000000, 1f );
scene.showBanner( gameOver );
Sample.INSTANCE.play( Assets.SND_DEATH );
}
public static void bossSlain() {
if (Dungeon.hero.isAlive()) {
Banner bossSlain = new Banner( BannerSprites.get( BannerSprites.Type.BOSS_SLAIN ) );
bossSlain.show( 0xFFFFFF, 0.3f, 5f );
scene.showBanner( bossSlain );
Sample.INSTANCE.play( Assets.SND_BOSS );
}
}
public static void handleCell( int cell ) {
cellSelector.select( cell );
}
public static void selectCell( CellSelector.Listener listener ) {
cellSelector.listener = listener;
scene.prompt( listener.prompt() );
}
private static boolean cancelCellSelector() {
if (cellSelector.listener != null && cellSelector.listener != defaultCellListener) {
cellSelector.cancel();
return true;
} else {
return false;
}
}
public static WndBag selectItem( WndBag.Listener listener, WndBag.Mode mode, String title ) {
cancelCellSelector();
WndBag wnd =
mode == Mode.SEED ?
WndBag.getBag( SeedPouch.class, listener, mode, title ) :
mode == Mode.SCROLL ?
WndBag.getBag( ScrollHolder.class, listener, mode, title ) :
mode == Mode.POTION ?
WndBag.getBag( PotionBandolier.class, listener, mode, title ) :
mode == Mode.WAND ?
WndBag.getBag( WandHolster.class, listener, mode, title ) :
WndBag.lastBag( listener, mode, title );
scene.add( wnd );
return wnd;
}
static boolean cancel() {
if (Dungeon.hero.curAction != null || Dungeon.hero.resting) {
Dungeon.hero.curAction = null;
Dungeon.hero.resting = false;
return true;
} else {
return cancelCellSelector();
}
}
public static void ready() {
selectCell( defaultCellListener );
QuickSlotButton.cancel();
}
public static void examineCell( Integer cell ) {
if (cell == null) {
return;
}
if (cell < 0 || cell > Level.LENGTH || (!Dungeon.level.visited[cell] && !Dungeon.level.mapped[cell])) {
GameScene.show( new WndMessage( "You don't know what is there." ) ) ;
return;
}
if (cell == Dungeon.hero.pos) {
GameScene.show( new WndHero() );
return;
}
if (Dungeon.visible[cell]) {
Mob mob = (Mob) Actor.findChar(cell);
if (mob != null) {
GameScene.show(new WndInfoMob(mob));
return;
}
}
Heap heap = Dungeon.level.heaps.get(cell);
if (heap != null && heap.seen) {
if (heap.type == Heap.Type.FOR_SALE && heap.size() == 1 && heap.peek().price() > 0) {
GameScene.show(new WndTradeItem(heap, false));
} else {
GameScene.show(new WndInfoItem(heap));
}
return;
}
Plant plant = Dungeon.level.plants.get( cell );
if (plant != null) {
GameScene.show( new WndInfoPlant( plant ) );
return;
}
Trap trap = Dungeon.level.traps.get( cell );
if (trap != null && trap.visible) {
GameScene.show( new WndInfoTrap( trap ));
return;
}
GameScene.show( new WndInfoCell( cell ) );
}
private static final CellSelector.Listener defaultCellListener = new CellSelector.Listener() {
@Override
public void onSelect( Integer cell ) {
if (Dungeon.hero.handle( cell )) {
Dungeon.hero.next();
}
}
@Override
public String prompt() {
return null;
}
};
}