/** The base class for Play and DollsHouse. */ abstract class DollsHouseDirector : AbstractDirector(), MouseListener { /** * The scene when the escape key is pressed to end the game. * The default is to show the main menu, but other scenes may show a difference menu. * For example, the turorial scene will return to the tutorial menu. */ @Attribute( about="The name of the scene when the escape key is pressed" ) var menuName = "menu" @Attribute( about="The gravity used on this scene by the physics engine" ) val gravity = Vector2() @Attribute( about="Initial scale of the main view" ) var scale = 1f // Set by the [BoundBottomLeft] and [BoundsTopRight] roles. val bottomLeft = Vector2(0, 0) val topRight = Vector2(1280,400) /** * Pans the main view when a launcher is selected. */ val panAction = ActionHolder() var mainView: StageView = null var glassView: StageView = null var startTime = 0.0 // Inputs. Initialised in begin(). See tick() and onKey() var escape: Input var restart: Input var inputLeft: Input var inputRight: Input var inputUp: Input var inputDown: Input var zoomIn: Input var zoomOut: Input var inputPause: Input var mousePosition = Vector2() val scrollSpeed = 15 val zoomAction = ActionHolder() static var instance : DollsHouseDirector init { instance = this } override fun sceneLoaded() { super.sceneLoaded() gravity.set( Game.instance.resources.gameInfo.physicsInfo.gravity ) mainView = findView("main") as StageView glassView = findView("glass") as StageView escape = findInput("escape") restart = findInput("restart") inputLeft = findInput("left") inputRight = findInput("right") inputUp = findInput("up") inputDown = findInput("down") zoomIn = findInput("zoomIn") zoomOut = findInput("zoomOut") inputPause = findInput("pause") } override fun begin() { super.begin() AbstractLauncher.dollZOrder = 50 constrainView() } override fun activated() { super.activated() mainView.stage.world.addContactListener(RoleContactManager()) } override fun layout() { super.layout() // DollsHouseProducer called arrangeViews(), when sets the scale based soley on the // window size, and the info in GameInfo. We now scale by our "preferred" scale, // which is an @Attribute, but also changed by the zoomIn/Out inputs. findStageView( "main" ).scale *= scale constrainView() } override fun tick() { super.tick() panAction.act() if (inputUp.isPressed() == true) { mainView.worldFocal.y += scrollSpeed / mainView.scale.y } if (inputDown.isPressed() == true) { mainView.worldFocal.y -= scrollSpeed / mainView.scale.y } if (inputLeft.isPressed() == true) { mainView.worldFocal.x -= scrollSpeed / mainView.scale.x } if (inputRight.isPressed() == true) { mainView.worldFocal.x += scrollSpeed / mainView.scale.x } edgePanning() zoomAction.act() constrainView() } fun edgePanning() { // Check if the mouse is near edges, and if so, pan the view. // But don't do this if we are panning the view by dragging (see onMouseButton onMouseMove) // The margins is scaled by mainView.scale so that on high DPI displays, the behaviour is // the same as low DPI displays. if (panStart == null && Options.instance.edgePanMargin > 0 && Options.instance.edgePanSpeed > 0 && isMouseWithinWindow() ) { getWindowMousePosition(mousePosition) val margin = Options.instance.edgePanMargin val adjustment = Options.instance.edgePanSpeed / mainView.scale.x if ( mousePosition.x <= margin ) { mainView.worldFocal.x -= adjustment * mainView.scale.x } if ( mousePosition.x >= mainView.rect.right - margin ) { mainView.worldFocal.x += adjustment * mainView.scale.x } if ( mousePosition.y <= margin ) { mainView.worldFocal.y += adjustment * mainView.scale.y } if ( mousePosition.y >= mainView.rect.top - margin ) { mainView.worldFocal.y -= adjustment * mainView.scale.y } } } override fun postTick() { super.postTick() constrainView() } fun showNewReward( reward : Reward ) { if (reward is Doll) { ensureVisible( (reward as Doll).parts[0] ) Star.createRing( (reward as Doll).parts[0] ) } else { ensureVisible( reward.actor ) Star.createRing( reward.actor ) } } /** Pan the main view, to ensure that the actor is visible. */ fun ensureVisible( actor : Actor ) { if (actor == null) { return } // The x and y margins, so that the actor doesn't end up touching the edge of the window. val margin = Vector2(200, 100) val viewRect = mainView.worldRect() val actorRect = actor.appearance.worldRect() if ( actorRect.left < viewRect.left + margin.x ) { mainView.worldFocal.x += actorRect.left - viewRect.left - margin.x } else if ( actorRect.right > viewRect.right - margin.x ) { mainView.worldFocal.x += actorRect.right - viewRect.right + margin.x } if (actorRect.bottom < viewRect.bottom + margin.y ) { mainView.worldFocal.y += actorRect.bottom - viewRect.bottom - margin.y } else if ( actorRect.top > viewRect.top - margin.y ) { mainView.worldFocal.y += actorRect.top - viewRect.top + margin.y } } var panStart : Vector2 = null override fun onMouseButton( event : MouseEvent ) { // Right click and drag to pan the view. if ( event.state == ButtonState.PRESSED && event.button != 0 ) { mainView.windowToWorld( event.windowPosition, event.worldPosition ) panStart = Vector2( event.worldPosition ) } else { panStart = null } } override fun onMouseMove( event : MouseEvent ) { if (panStart != null) { mainView.windowToWorld( event.windowPosition, event.worldPosition ) mainView.worldFocal -= event.worldPosition - panStart constrainView() } } override fun onKey(event: KeyEvent) { if (inputPause.matches(event) == true) { Game.instance.paused = !Game.instance.paused } if (escape.matches(event) == true) { Game.instance.startScene(menuName) } if (restart.matches(event) == true) { Game.instance.startScene(Game.instance.sceneName) } if (zoomIn.matches(event) ) { zoomAction.then( ScaleBy( mainView.scale, 0.2, 1.2, Eases.easeOut ) ) scale *= 1.2 } if (zoomOut.matches(event) ) { zoomAction.then( ScaleBy( mainView.scale, 0.2, 1.0/1.2, Eases.easeOut ) ) scale /= 1.2 } } /** Ensures that panning does not move out of the range of the bottomLeft->topRight, which are supplied by the roles BoundsBottomLeft and BoundsTopRight (if present on the scene). */ fun constrainView() { if (mainView) == null return val worldRect = mainView.worldRect() if ( worldRect.width >= topRight.x - bottomLeft.x ) { // zoomed out very far - center the view mainView.worldFocal.x = ( topRight.x - bottomLeft.x - worldRect.width ) / 2 + bottomLeft.x } else { if ( worldRect.left < bottomLeft.x ) { mainView.worldFocal.x -= worldRect.left - bottomLeft.x } if ( worldRect.right > topRight.x ) { mainView.worldFocal.x -= worldRect.right - topRight.x } } if ( worldRect.height >= topRight.y - bottomLeft.y ) { // zoomed out very far - use bottomLeft.y mainView.worldFocal.y = bottomLeft.y// ( topRight.y - bottomLeft.y - worldRect.height ) / 2 + bottomLeft.y } else { if ( worldRect.bottom < bottomLeft.y ) { mainView.worldFocal.y -= worldRect.bottom - bottomLeft.y } if ( worldRect.top > topRight.y ) { mainView.worldFocal.y -= worldRect.top - topRight.y } } } fun knockedFragile() { // Do nothing. Overridden in Play. Knocking over vases etc is okay in the Doll's House. } fun onDollHit( doll : Doll, hard : bool ) {} fun onDollZapped( doll : Doll ) {} }