<commit_before>package de.gurkenlabs.litiengine.graphics.animation;

import java.util.ArrayList;
import java.util.List;

import de.gurkenlabs.litiengine.entities.Direction;
import de.gurkenlabs.litiengine.entities.IMovableCombatEntity;
import de.gurkenlabs.litiengine.graphics.Spritesheet;

/**
 * This {@link AnimationController} implementation provides animation rules that use naming conventions to provide {@link Animation}s for
 * {@link IMovableCombatEntity} implementations.
 * 
 * The spritesheet images need to be named according to the following conventions in order to be automatically used by this controller:
 * <ul>
 * <li>{entity-name}-idle-{DIRECTION}.png</li>
 * <li>{entity-name}-walk-{DIRECTION}.png</li>
 * </ul>
 * Where {DIRECTION} refers to a value of the {@link Direction} enum and {entity-name} refers to the
 * name of the entity.
 * 
 * @see de.gurkenlabs.litiengine.entities.IMovableCombatEntity
 * @see de.gurkenlabs.litiengine.entities.Direction
 * @see de.gurkenlabs.litiengine.entities.IEntity#getName()
 */
public class MovableCombatEntityAnimationController extends EntityAnimationController<IMovableCombatEntity> {
  public static final String WALK_IDENTIFIER = "walk";
  public static final String IDLE_IDENTIFIER = "idle";

  public MovableCombatEntityAnimationController(IMovableCombatEntity entity, Animation defaultAnimation) {
    super(entity, defaultAnimation);
    if (this.getEntity().getName() == null || this.getEntity().getName().isEmpty()) {
      throw new IllegalArgumentException("Make sure the name of the entity is set before using a MovableCombatEntityAnimationController.");
    }

    this.getAnimations().addAll(this.initializeAvailableAnimations(this.getEntity()));
  }

  private List<Animation> initializeAvailableAnimations(IMovableCombatEntity entity) {

    List<Animation> animation = new ArrayList<>();

    for (Direction dir : Direction.values()) {

      // initialize walking animations
      final String walkAnimationName = entity.getName().toLowerCase() + "-" + WALK_IDENTIFIER + "-" + dir.toString().toLowerCase();
      Spritesheet walkSprite = Spritesheet.find(walkAnimationName);
      if (walkSprite != null) {
        animation.add(new Animation(walkSprite, true));
        this.addAnimationRule(e -> !e.isIdle() && e.getFacingDirection() == dir, walkAnimationName);
      }

      // initialize idle animations
      final String idleAnimationName = entity.getName().toLowerCase() + "-" + IDLE_IDENTIFIER + "-" + dir.toString().toLowerCase();
      Spritesheet idleSprite = Spritesheet.find(idleAnimationName);
      if (idleSprite != null) {
        animation.add(new Animation(idleSprite, true));
        this.addAnimationRule(e -> e.isIdle() && e.getFacingDirection() == dir, idleAnimationName);
      }
    }

    return animation;
  }
}
<commit_msg>Extend the MovableCombatEntityAnimationController to allow flipping left/right sprites to provide the opposite. Implement update method that sets the acutal animation.<commit_after>package de.gurkenlabs.litiengine.graphics.animation;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import de.gurkenlabs.litiengine.Game;
import de.gurkenlabs.litiengine.IGameLoop;
import de.gurkenlabs.litiengine.entities.Direction;
import de.gurkenlabs.litiengine.entities.IMovableCombatEntity;
import de.gurkenlabs.litiengine.graphics.Spritesheet;
import de.gurkenlabs.util.ImageProcessing;

/**
 * This {@link AnimationController} implementation provides animation rules that
 * use naming conventions to provide {@link Animation}s for
 * {@link IMovableCombatEntity} implementations.
 * 
 * The spritesheet images need to be named according to the following
 * conventions in order to be automatically used by this controller:
 * <ul>
 * <li>{entity-name}-idle-{DIRECTION}.png</li>
 * <li>{entity-name}-walk-{DIRECTION}.png</li>
 * </ul>
 * Where {DIRECTION} refers to a value of the {@link Direction} enum and
 * {entity-name} refers to the name of the entity.
 * 
 * @see de.gurkenlabs.litiengine.entities.IMovableCombatEntity
 * @see de.gurkenlabs.litiengine.entities.Direction
 * @see de.gurkenlabs.litiengine.entities.IEntity#getName()
 */
public class MovableCombatEntityAnimationController extends EntityAnimationController<IMovableCombatEntity> {

  public MovableCombatEntityAnimationController(IMovableCombatEntity entity, Animation defaultAnimation) {
    this(entity, defaultAnimation, true);
  }

  public MovableCombatEntityAnimationController(IMovableCombatEntity entity, Animation defaultAnimation, boolean useFlippedSpritesAsFallback) {
    super(entity, defaultAnimation);
    if (this.getEntity().getName() == null || this.getEntity().getName().isEmpty()) {
      throw new IllegalArgumentException("Make sure the name of the entity is set before using a MovableCombatEntityAnimationController.");
    }

    this.getAnimations().addAll(this.initializeAvailableAnimations());

    if (useFlippedSpritesAsFallback) {
      this.getAnimations().addAll(this.initializeFlippedAnimations());
    }
  }

  @Override
  public void update(final IGameLoop gameLoop) {
    super.update(gameLoop);

    if (Game.getEnvironment() == null || Game.getEnvironment().getMap() == null) {
      return;
    }
    if (this.getCurrentAnimation() != null && !this.getCurrentAnimation().isLoop()) {
      return;
    }
    if (this.getEntity() == null) {
      return;
    }

    String animationName = this.getEntity().isIdle() ? this.getIdleSpriteName(this.getEntity().getFacingDirection()) : this.getWalkSpriteName(this.getEntity().getFacingDirection());
    if (this.getCurrentAnimation() == null || animationName != null && !animationName.isEmpty() && !this.getCurrentAnimation().getName().equalsIgnoreCase(animationName)) {
      this.playAnimation(animationName);
    }
  }

  private List<Animation> initializeAvailableAnimations() {
    List<Animation> animations = new ArrayList<>();

    for (Direction dir : Direction.values()) {

      // initialize walking animations
      Spritesheet walkSprite = getWalkSprite(dir);
      if (walkSprite != null) {
        animations.add(new Animation(walkSprite, true));
        this.addAnimationRule(e -> !e.isIdle() && e.getFacingDirection() == dir, walkSprite.getName());
      }

      // initialize idle animations
      Spritesheet idleSprite = getIdleSprite(dir);
      if (idleSprite != null) {
        animations.add(new Animation(idleSprite, true));
        this.addAnimationRule(e -> e.isIdle() && e.getFacingDirection() == dir, idleSprite.getName());
      }
    }

    return animations;
  }

  private List<Animation> initializeFlippedAnimations() {
    List<Animation> animations = new ArrayList<>();
    String leftIdle = getIdleSpriteName(Direction.LEFT);
    String leftWalk = getWalkSpriteName(Direction.LEFT);
    Optional<Animation> leftIdleAnimation = this.getAnimations().stream().filter(x -> x.getName().equals(leftIdle)).findFirst();
    Optional<Animation> leftWalkAnimation = this.getAnimations().stream().filter(x -> x.getName().equals(leftWalk)).findFirst();

    String rightIdle = getIdleSpriteName(Direction.RIGHT);
    String rightWalk = getWalkSpriteName(Direction.RIGHT);
    Optional<Animation> rightIdleAnimation = this.getAnimations().stream().filter(x -> x.getName().equals(rightIdle)).findFirst();
    Optional<Animation> rightWalkAnimation = this.getAnimations().stream().filter(x -> x.getName().equals(rightWalk)).findFirst();

    if (!leftIdleAnimation.isPresent() && rightIdleAnimation.isPresent()) {
      animations.add(flipAnimation(rightIdleAnimation.get().getSpritesheet(), leftIdle));
    }

    if (!leftWalkAnimation.isPresent() && rightWalkAnimation.isPresent()) {
      animations.add(flipAnimation(rightWalkAnimation.get().getSpritesheet(), leftWalk));
    }

    if (!rightIdleAnimation.isPresent() && leftIdleAnimation.isPresent()) {
      animations.add(flipAnimation(leftIdleAnimation.get().getSpritesheet(), rightIdle));
    }

    if (!rightWalkAnimation.isPresent() && leftWalkAnimation.isPresent()) {
      animations.add(flipAnimation(leftWalkAnimation.get().getSpritesheet(), rightWalk));
    }

    return animations;
  }

  private Animation flipAnimation(Spritesheet spriteToFlip, String newSpriteName) {
    final BufferedImage leftIdleSprite = ImageProcessing.flipSpritesHorizontally(spriteToFlip);
    Spritesheet leftIdleSpritesheet = Spritesheet.load(leftIdleSprite, newSpriteName, spriteToFlip.getSpriteWidth(), spriteToFlip.getSpriteHeight());
    return new Animation(leftIdleSpritesheet, true);
  }

  private Spritesheet getIdleSprite(Direction dir) {
    return Spritesheet.find(getIdleSpriteName(dir));
  }

  private Spritesheet getWalkSprite(Direction dir) {
    return Spritesheet.find(getWalkSpriteName(dir));
  }

  private String getIdleSpriteName(Direction dir) {
    final String spriteIdle = "%s-idle-%s";
    return getSpriteName(spriteIdle, dir);
  }

  private String getWalkSpriteName(Direction dir) {
    final String spriteWalk = "%s-walk-%s";
    return getSpriteName(spriteWalk, dir);
  }

  private String getSpriteName(String formatString, Direction dir) {
    return String.format(formatString, this.getEntity().getName().toLowerCase(), dir.toString().toLowerCase());
  }
}

{'fraction_non_alphanumeric': 0.11898625429553264, 'fraction_numerical': 0.0, 'mean_word_length': 4.831559173450219, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7155369', 'n_tokens_mistral': 2703, 'n_tokens_neox': 2683, 'n_words': 545}