Iterator<Map.Entry<Monster, Integer>> monstersIterator = roundRobin.entrySet()
.iterator();
while ( monstersIterator.hasNext() ) {
final Map.Entry<Monster, Integer> monsterWithSpeed = monstersIterator.next();
final int remainingMonsterSpeed = monsterWithSpeed.getValue();
final Monster mob = monsterWithSpeed.getKey();
if ( remainingMonsterSpeed > 0 ) {
monsterWithSpeed.setValue( remainingMonsterSpeed - 1 );
final Point monsterPosition = mob.getPosition();
if ( actualPlayerPosition.equals( monsterPosition ) ) {
deadMan = true;
}
final PathAnimation mobAnimation;
@Nullable
PERPENDICULAR_DIRECTION monsterDirection;
if ( this.firstStep ) {
monsterDirection = null;
} else {
monsterDirection = mob.getActualDirection();
}
MapGridElement monsterFooting = this.monstersPosition.get( mob );
assert monsterFooting != null;
if ( monsterFooting instanceof Junction || monsterDirection == null ) {
//Monster is in a junction, must change direction
monsterDirection = mob.getNewDirection( monsterDirection, false );
}
if ( mob instanceof AnimatedElement ) {
//This is an animated monster, an animations must be applied
PathAnimation animation = this.monstersAndAnimations.get( mob );
if ( animation == null ) {
//If the animations wasn't created yet, create it
animation = new PathAnimationImpl();
animation.startNewPart( monsterDirection );
animation.addPoint( monsterPosition );
this.monstersAndAnimations.put( ( (AnimatedElement) mob ), animation );
if ( mob.modifiesColor() ) {
utilPushAnimation( entitiesColoringTrace,
monsterFooting,
animation,
mob.isColoring(),
animation.getNewPart() );
}
} else if ( monsterDirection != animation.getNewPartDirection() ) {
animation.startNewPart( monsterDirection );
animation.addPoint( monsterPosition );
if ( mob.modifiesColor() ) {
utilPushAnimation( entitiesColoringTrace,
monsterFooting,
animation,
mob.isColoring(),
animation.getNewPart() );
}
}
mobAnimation = animation;
} else {
mobAnimation = null;
}
switch ( monsterDirection ) {
case LEFT:
monsterPosition.x--;
break;
case RIGHT:
monsterPosition.x++;
break;
case UP:
monsterPosition.y--;
break;
case DOWN:
monsterPosition.y++;
break;
}
mob.setPosition( monsterPosition );
if ( mobAnimation != null ) {
mobAnimation.addPoint( monsterPosition );
}
if ( ! monsterFooting.contains( monsterPosition ) ) {
//The monster changed grid element, the animations must be applied to this element too
monsterFooting = findElementRelativeTo( this.levelGraph,
monsterFooting,
monsterDirection );
this.monstersPosition.put( mob, monsterFooting );
if ( mob.modifiesColor() && mobAnimation != null ) {
utilPushAnimation( entitiesColoringTrace,
monsterFooting,
mobAnimation,
mob.isColoring(),
mobAnimation.getNewPart() );
}
}
if ( mob.modifiesColor() ) {
//(De)Colors this part of the grid
final boolean isMobColoring = mob.isColoring();
final boolean wasColoredHere = monsterFooting.isColoredAt( monsterPosition );
if ( wasColoredHere != isMobColoring ) {
if ( wasColoredHere ) {
this.addScore( - COLORING_SCORE );
} else {
this.addScore( COLORING_SCORE );
}
}
this.utilColorGridElementAndUpdateChanges( monsterFooting, monsterPosition,
isMobColoring );
}
if ( actualPlayerPosition.equals( monsterPosition ) ) {
deadMan = true;
}
} else {
//Monster remaining speed is 0, remove it from the moving monsters
stillMonsters.add( mob );
monstersIterator.remove();
}
}
}
//Computes the end time of all the animations
final long scheduledAnimationEndTime = millisCallTime + GameSettings.getInstance()
.getValue( GameSettings.INTEGER_SETTINGS_TYPE.MOVE_MILLIS_TIMEOUT );
if ( playerPath != null && player instanceof AnimatedElement ) {
//Applies the previously computed path animations to the Player entity as an entity animations
playerPath.startAnimation( millisCallTime, scheduledAnimationEndTime );
( (AnimatedElement) player )
.applyAnimation( new PathBasedMovementAnimation( playerPath ) );
}
for (Map.Entry<AnimatedElement, PathAnimation> animatedEntry : this.monstersAndAnimations
.entrySet()) {
//Applies the previously computed path animations to the Monster entity as an entity animations
final PathAnimation entryAnimation = animatedEntry.getValue();
if ( entryAnimation != null ) {
entryAnimation.startAnimation( millisCallTime, scheduledAnimationEndTime );
animatedEntry.getKey()
.applyAnimation( new PathBasedMovementAnimation( entryAnimation ) );
}
}
for (Map.Entry<MapGridElement, Map<PathAnimation, Pair<Boolean, Collection<Segment>>>> mapGridElementEntry : entitiesColoringTrace
.entrySet()) {
//The key of the outer map keeps the grid element the animations refer to
final MapGridElement mapElement = mapGridElementEntry.getKey();
if ( mapElement instanceof Pipe ) {
final Collection<PipeColoringAnimation> resultElementAnimations = new LinkedList<>();
for (Map.Entry<PathAnimation, Pair<Boolean, Collection<Segment>>> pathAnimationEntry : mapGridElementEntry
.getValue().entrySet()) {
/*
The inner map keeps the mapping path animations: <coloring/decoloring flag; list of segments>.
The list of segments usually keeps only one segment because entities speeds are in the range 0-3 so
the same entity can't enter and, exit and then enter another time the same grid element is a single step
*/
final PathAnimation animation = pathAnimationEntry.getKey();
final boolean isColoring = pathAnimationEntry.getValue().getFirst();
for (Segment segment : pathAnimationEntry.getValue().getSecond()) {
resultElementAnimations.add( new PathBasedPipeColoringAnimation( animation,
segment,
( (Pipe) mapElement )
.getSegment(),
isColoring ) );
}
}
( (Pipe) mapElement ).applyAnimations( resultElementAnimations );
} else /*if ( mapElement instanceof Junction )*/ {
final Collection<JunctionColoringAnimation> resultElementAnimations = new LinkedList<>();
for (Map.Entry<PathAnimation, Pair<Boolean, Collection<Segment>>> pathAnimationEntry : mapGridElementEntry
.getValue().entrySet()) {
final PathAnimation animation = pathAnimationEntry.getKey();
final boolean isColoring = pathAnimationEntry.getValue().getFirst();
//Segment list is not needed when creating the JunctionColoringAnimation
resultElementAnimations.add( new PathBasedJunctionColoringAnimation( animation,
( (Junction) mapElement )
.getPosition(),
isColoring ) );
}
( (Junction) mapElement ).applyAnimations( resultElementAnimations );
}
}
if ( ! this.playerHitted ) {
if ( deadMan ) {
this.hitPlayer();
} else if ( this.checkWinCondition(this.modifiedMapElements) ) {
this.state = LEVEL_STATE.LEVEL_WIN;
}
}
if ( this.coloredFaces > previousColoredFacesNumber ) {
final int coloredFacesThisStep = this.coloredFaces - previousColoredFacesNumber;
this.addScore( FACE_COLORING_SCORE * ( 2 * coloredFacesThisStep - 1 ) );
}
for (Monster mob : levelMonsters) {
mob.startStep();
}
player.startStep();
this.firstStep = false;