/* * Pixel Dungeon * Copyright (C) 2012-2015 Oleg Dolya * * Shattered Pixel Dungeon * Copyright (C) 2014-2019 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 com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.watabou.noosa.Camera; import com.shatteredpixel.shatteredpixeldungeon.input.GameAction; import com.shatteredpixel.shatteredpixeldungeon.input.PDInputProcessor; import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.watabou.input.NoosaInputProcessor; import com.watabou.noosa.TouchArea; import com.watabou.utils.GameMath; import com.watabou.utils.Point; import com.watabou.utils.PointF; public class CellSelector extends TouchArea<GameAction> { public Listener listener = null; public boolean enabled; private float mouseZoom; private float dragThreshold; private NoosaInputProcessor.Key<GameAction> pressedKey; private float pressedKeySpeedFactor = 0.05f; public CellSelector( DungeonTilemap map ) { super( map ); camera = map.camera(); mouseZoom = camera.zoom; dragThreshold = PixelScene.defaultZoom * DungeonTilemap.SIZE / 2; } @Override protected void onClick( NoosaInputProcessor.Touch touch ) { if (dragging) { dragging = false; } else { PointF p = Camera.main.screenToCamera( (int)touch.current.x, (int)touch.current.y ); for (Char mob : Dungeon.level.mobs.toArray(new Mob[0])){ if (mob.sprite != null && mob.sprite.overlapsPoint( p.x, p.y)){ select( mob.pos ); return; } } for (Heap heap : Dungeon.level.heaps.valueList()){ if (heap.sprite != null && heap.sprite.overlapsPoint( p.x, p.y)){ select( heap.pos ); return; } } select( ((DungeonTilemap)target).screenToTile( (int)touch.current.x, (int)touch.current.y, true ) ); } } @Override public boolean onKeyDown(NoosaInputProcessor.Key<GameAction> key) { switch (key.action) { case ZOOM_IN: zoom( camera.zoom + 1 ); return true; case ZOOM_OUT: zoom( camera.zoom - 1 ); return true; case ZOOM_DEFAULT: zoom( PixelScene.defaultZoom ); return true; } boolean handled = true; int x = 0, y = 0; switch (key.action) { case MOVE_UP: y = -1; break; case MOVE_DOWN: y = 1; break; case MOVE_LEFT: x = -1; break; case MOVE_RIGHT: x = 1; break; case MOVE_TOP_LEFT: x = -1; y = -1; break; case MOVE_TOP_RIGHT: x = 1; y = -1; break; case MOVE_BOTTOM_LEFT: x = -1; y = 1; break; case MOVE_BOTTOM_RIGHT: x = 1; y = 1; break; case OPERATE: break; default: handled = false; break; } if (handled) { CharSprite.setMoveInterval(Math.max(0.1f, 0.1f + pressedKeySpeedFactor)); Point point = DungeonTilemap.tileToPoint(Dungeon.hero.pos); point.x += x; point.y += y; pressedKey = key; select(DungeonTilemap.pointToTile(point)); } return handled; } @Override public boolean onKeyUp( PDInputProcessor.Key<GameAction> key ) { if (pressedKey != null && key.action == pressedKey.action) { resetKeyHold(); } switch (key.code) { case PDInputProcessor.MODIFIER_KEY: mouseZoom = zoom( Math.round( mouseZoom ) ); return true; default: return false; } } public void processKeyHold(){ if (pressedKey != null) { enabled = true; pressedKeySpeedFactor -= 0.025f; CharSprite.setMoveInterval(Math.max(0.1f, 0.1f + pressedKeySpeedFactor)); onKeyDown(pressedKey); } } public void resetKeyHold(){ pressedKeySpeedFactor = 0.05f; pressedKey = null; CharSprite.setMoveInterval(0.1f); } private float zoom( float value ) { value = GameMath.gate( PixelScene.minZoom, value, PixelScene.maxZoom ); SPDSettings.zoom((int) (value - PixelScene.defaultZoom)); camera.zoom( value ); //Resets character sprite positions with the new camera zoom //This is important as characters are centered on a 16x16 tile, but may have any sprite size //This can lead to none-whole coordinate, which need to be aligned with the zoom for (Char c : Actor.chars()){ if (c.sprite != null && !c.sprite.isMoving){ c.sprite.point(c.sprite.worldToCamera(c.pos)); } } return value; } public void select( int cell ) { if (enabled && listener != null && cell != -1) { listener.onSelect( cell ); GameScene.ready(); } else { GameScene.cancel(); } } private boolean pinching = false; private NoosaInputProcessor.Touch another; private float startZoom; private float startSpan; @Override protected void onTouchDown( NoosaInputProcessor.Touch t ) { if (t != touch && another == null) { if (!touch.down) { touch = t; onTouchDown( t ); return; } pinching = true; another = t; startSpan = PointF.distance( touch.current, another.current ); startZoom = camera.zoom; dragging = false; } else if (t != touch) { reset(); } } @Override protected void onTouchUp( NoosaInputProcessor.Touch t ) { if (pinching && (t == touch || t == another)) { pinching = false; zoom(Math.round( camera.zoom )); dragging = true; if (t == touch) { touch = another; } another = null; lastPos.set( touch.current ); } } private boolean dragging = false; private PointF lastPos = new PointF(); @Override public boolean onMouseScroll(int scroll) { mouseZoom -= scroll / 3f; if (PDInputProcessor.modifier) { mouseZoom = zoom( mouseZoom ); } else { zoom( Math.round( mouseZoom ) ); mouseZoom = GameMath.gate( PixelScene.minZoom, mouseZoom, PixelScene.maxZoom ); } return true; } @Override protected void onDrag( NoosaInputProcessor.Touch t ) { if (pinching) { float curSpan = PointF.distance( touch.current, another.current ); float zoom = (startZoom * curSpan / startSpan); camera.zoom( GameMath.gate( PixelScene.minZoom, zoom - (zoom % 0.1f), PixelScene.maxZoom ) ); } else { if (!dragging && PointF.distance( t.current, t.start ) > dragThreshold) { dragging = true; lastPos.set( t.current ); } else if (dragging) { camera.shift( PointF.diff( lastPos, t.current ).invScale( camera.zoom ) ); lastPos.set( t.current ); } } } public void cancel() { if (listener != null) { listener.onSelect( null ); } GameScene.ready(); } @Override public void reset() { super.reset(); another = null; if (pinching){ pinching = false; zoom( Math.round( camera.zoom ) ); } } public void enable(boolean value){ if (enabled != value){ enabled = value; } } public interface Listener { void onSelect( Integer cell ); String prompt(); } }