class Item : ActionRole, LookedAt { static val IMMEDIATELY = 60 static val EAST = 0 static val NORTH = 1 static val WEST = 2 static val SOUTH = 3 static val NORTH_EAST = 4 static val NORTH_WEST = 5 static val SOUTH_WEST = 6 static val SOUTH_EAST = 7 var square : Square = null override fun begin() { (actor.stage as GridStage).squareAtPoint( actor.x, actor.y ).addOccupant( this, isAlternate() ) super.begin() } override fun end() { if (square != null) { square.remove(this) } super.end() } fun gridSize() : int = PlayDirector.instance.mainStage.gridSize override fun createAction() : Action { return this:>myTurn.once().forever() } /** * Called from the Action created by createAction(). * When the Item is moving, then the Action is replaced, so this method won't be called until the movement is comlete. * * The default is to do nothing. */ fun myTurn() {} fun isAlternate() : boolean = true /** * strength : Balloons need strength >= 0. Rocks need strength >= 4 for each rock in the chain. * A "normal" player pushes with strength 4, and therefore can push 1 rock and any number of balloons. */ override fun canPush( direction : int, speed : int, strength : int ) = false /** * You MUST call canPush before doing the push, otherwise weird things will happen. */ override fun push( direction : int, speed : int ) { val next = look( direction, speed ) if (next == this ) { println( "Eek potential stack overflow. " ) } else { if (!next.squashable( direction ) ) { next.push( direction, speed ) } (this as Movable).move(direction, speed) } } fun warpTo( square : Square ) { if ( (this is Movable) && (this as Movable).movement != null ) { (this as Movable).movement.toSquare.entrant = null (this as Movable).movement = null replaceAction( createAction() ) } square.addOccupant( this ) actor.x = square.worldX() actor.y = square.worldY() } fun invading( movement : Movement ) {} /** * Return true if you want the moving Item to complete his move into the new spot, * false if THIS item is taking control of the moving Item's new location. */ fun invaded( movement : Movement ) = true fun kill() {} // For the LookedAt interface override fun item() : Item = this // See Copier. override fun copyable() : boolean = false override fun isEmpty() : boolean = false override fun isOutside() : boolean = false override fun isPlayer() : boolean = false override fun isDeadly() : boolean = false override fun isMoving() : boolean = false override fun squashable( direction : int ) : boolean = false /** * Can the Player collect this item? Note, this also applies to Soil, and other objects which * can be "eaten" by the player, but cannot be "eaten" by Rocks, Balloons etc. */ override fun collectable() : boolean = false override fun rounded( direction : int ) : boolean = false fun lookEast() : LookedAt = lookDelta( 1, 0, IMMEDIATELY ) fun lookEast( speed : int ) : LookedAt = lookDelta( 1, 0, speed ) fun lookWest() : LookedAt = lookDelta( -1, 0, IMMEDIATELY ) fun lookWest( speed : int ) : LookedAt = lookDelta( -1, 0, speed ) fun lookNorth() : LookedAt = lookDelta( 0, 1, IMMEDIATELY ) fun lookNorth( speed : int ) : LookedAt = lookDelta( 0, 1, speed ) fun lookSouth() : LookedAt = lookDelta( 0, -1, IMMEDIATELY ) fun lookSouth( speed : int ) : LookedAt = lookDelta( 0, -1, speed ) fun look( direction : int ) : LookedAt = look( direction, IMMEDIATELY ) fun look( direction : int, speed : int ) : LookedAt = square.neighbour( Item.getDeltaX(direction), Item.getDeltaY(direction) ).look( speed, direction ) fun lookDelta( dx : int, dy :int ) : LookedAt = lookDelta( dx, dy, IMMEDIATELY ) fun lookDelta( dx : int, dy : int, speed : int ) : LookedAt = square.neighbour( dx, dy ).look( speed, Item.getDirection( dx, dy ) ) fun lookAt( x : int , y : int ) : LookedAt = (actor.stage as GridStage).square( x, y ).look( IMMEDIATELY, -1 ) var speechA : Actor = null fun isTalking() : boolean = speechA != null && speechA.stage != null fun speak( text : String, seconds : double ) { speak( text, seconds, Color.white() ) } fun speak( text : String, seconds : double, speechColor : Color ) { if ( speechA != null ) { speechA.die() } speechA = actor.createChild( "speech" ) PlayDirector.instance.extraStage.add( speechA ) (speechA.role as Speech).speak( this, text, seconds, speechColor ) } fun stopSpeaking() { if ( isTalking() ) { (speechA.role as Speech).stop() speechA = null } } static fun getDirection( dx : int, dy : int ) : int { if ( dx < 0 ) { return WEST } else if ( dx > 0 ) { return EAST } else { if ( dy == 0 ) { return -1 } else if (dy < 0) { return SOUTH } else { return NORTH } } } static val deltaXs = intArrayOf(1, 0, -1, 0) static fun getDeltaX( direction : int ) : int = deltaXs[direction] static val deltaYs = intArrayOf(0,1,0,-1) static fun getDeltaY( direction : int ) : int = deltaYs[direction] static val dirAbbreviations = arrayOf("E", "N", "W", "S" ) static fun directionAbbreviation( direction : int ) : String = dirAbbreviations[direction] static val yAbbreviations = arrayOf("S", "", "N") static val xAbbreviations = arrayOf("W", "", "E") // dx and dy are in the range -1 .. 1 static fun directionAbbreviation( dx : int, dy : int ) : String { return yAbbreviations[dy + 1] + xAbbreviations[dx + 1] } override fun toString() = "${this.class.simpleName} @ ${actor.x},${actor.y}" }