package org.gbcpainter.game.model.monsters;
import net.jcip.annotations.GuardedBy;
import org.gbcpainter.env.GraphicsEnv;
import org.gbcpainter.game.view.AbstractResourceDrawable;
import org.gbcpainter.loaders.textures.TextureLoader;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
/**
* Implementation of the {@link org.gbcpainter.game.model.monsters.Bomb} interface.
* <p/>
* The bomb has a countdown start time, an explosion time and an explosion persistence time.
* <p/>
* The explosion area is a square whose size is 10
*
* @author Lorenzo Pellegrini
*/
public class BombImpl extends AbstractResourceDrawable implements Bomb {
private static final long NOT_ABORTED_ANIMATION_TIME = 0;
@GuardedBy ("this")
private long stoppedAnimationTime = NOT_ABORTED_ANIMATION_TIME;
private static final java.util.List<String> countdownTextures = Arrays.asList( "Bomb3", "Bomb2", "Bomb1" );/*NON-NLS*/
private static final java.util.List<String> explosionTextures = Arrays.asList( "Explosion1", "Explosion2", "Explosion3", "Explosion4" );/*NON-NLS*/
@NonNls
private static final String BEFORE_ACTIVATION_TEXTURE = "Bomb3";
private static final double EXPLOSION_AREA = 5.0;
private final long animationStartTime;
private final long explosionTime;
private final long animationEndTime;
@NotNull
private final Point position;
/**
* Creates the bomb with the given countdown start time, explosion time and an explosion persistence end time
* <p/>
* All the parameters must be in milliseconds based on the {@link System#currentTimeMillis()} method.
*
* @param startTime The time to start the countdown
* @param explosionTime The time the bomb will explode
* @param endTime The time the explosion vanishes
* @param gameGridPosition The position of the bomb
*
* @throws java.lang.IllegalArgumentException If explosionTime <= startTime or endTime <= explosionTime
* @throws Exception If an error occurs while loading textures
*/
public BombImpl( long startTime, long explosionTime, long endTime, @NotNull Point gameGridPosition ) throws Exception {
super( /*gameGridPosition*/ );
this.position = new Point( gameGridPosition );
this.animationStartTime = startTime;
this.explosionTime = explosionTime;
this.animationEndTime = endTime;
if ( this.animationStartTime >= this.explosionTime ) {
throw new IllegalArgumentException();
}
if ( this.explosionTime >= this.animationEndTime ) {
throw new IllegalArgumentException();
}
final TextureLoader loader = GraphicsEnv.getInstance().getTextureLoader();
for (String countdownTexture : countdownTextures) {
loader.loadTexture( countdownTexture, true );
}
for (String explosionTexture : explosionTextures) {
loader.loadTexture( explosionTexture, true );
}
loader.loadTexture( BEFORE_ACTIVATION_TEXTURE, true );
}
@Override
@NonNls
public synchronized String toString() {
return "Bomb{" +
"animationStartTime=" + this.animationStartTime +
"explosionTime=" + this.explosionTime +
", animationEndTime=" + this.animationEndTime +
( ( this.stoppedAnimationTime != NOT_ABORTED_ANIMATION_TIME ) ? "Locked at: " + this.stoppedAnimationTime : "" ) +
'}';
}
@NotNull
@Override
public Point getPosition() {
return new Point( this.position );
}
@Nullable
@Override
protected synchronized String getResourceName() {
final long currentTime = this.getTime();
if ( currentTime < this.animationEndTime ) {
if ( currentTime < this.animationStartTime ) {
return BEFORE_ACTIVATION_TEXTURE;
}
/*
If the bomb is activated, select the texture to use using the percentage elapsed
time as the index of the textures list
*/
else if ( currentTime < this.explosionTime ) {
final int nTextures = countdownTextures.size();
final double totalTime = this.explosionTime - this.animationStartTime;
final double elapsedTime = currentTime - this.animationStartTime;
int selectAnimation = (int) Math.floor( ( elapsedTime / totalTime ) * (double) ( nTextures ) );
selectAnimation = Math.min( selectAnimation, nTextures - 1 );
return countdownTextures.get( selectAnimation );
} else {
final int nTextures = explosionTextures.size();
final double totalTime = ( this.animationEndTime - this.explosionTime );
final double elapsedTime = ( currentTime - this.explosionTime );
int selectAnimation = (int) Math.round( ( elapsedTime / totalTime ) * (double) ( nTextures ) );
selectAnimation = Math.min( selectAnimation, nTextures - 1 );
return explosionTextures.get( selectAnimation );
}
} else {
return null;
}
}
@Nullable
@Override
public synchronized Shape getExplosion( @NotNull final Point2D positionCenter ) {
final long currentTime = this.getTime();
if ( currentTime >= this.explosionTime && currentTime < this.animationEndTime ) {
/* The explosion are area is a square */
return new Rectangle2D.Double( positionCenter.getX() - ( EXPLOSION_AREA / 2.0 ),
positionCenter.getY() - ( EXPLOSION_AREA / 2.0 ),
EXPLOSION_AREA, EXPLOSION_AREA );
} else {
return null;
}
}
@Override
public synchronized void lockAnimation( final long stopAtTime ) {
this.stoppedAnimationTime = stopAtTime;
}
@Override
public synchronized void unlockAnimation() {
this.stoppedAnimationTime = NOT_ABORTED_ANIMATION_TIME;
}
/**
* Gets the time used when calculating the texture and the shape to use
*
* This must take into account locks set using {@link #lockAnimation(long)}.
*
* @return The time, in milliseconds, to use
*/
protected synchronized long getTime() {
final long localStoppedAnimationTime = this.stoppedAnimationTime;
if ( localStoppedAnimationTime == NOT_ABORTED_ANIMATION_TIME ) {
return System.currentTimeMillis();
} else {
return localStoppedAnimationTime;
}
}
}