Package games.stendhal.server.core.pathfinder

Source Code of games.stendhal.server.core.pathfinder.EntityPathfinder$ResistanceMap

/* $Id: EntityPathfinder.java,v 1.6 2011/02/12 13:42:00 nhnb Exp $ */
/***************************************************************************
*                   (C) Copyright 2003-2010 - Stendhal                    *
***************************************************************************
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************/
package games.stendhal.server.core.pathfinder;

import games.stendhal.server.core.engine.StendhalRPZone;
import games.stendhal.server.entity.Entity;
import games.stendhal.server.entity.mapstuff.portal.Portal;
import games.stendhal.server.entity.player.Player;

import java.awt.Point;
import java.awt.geom.Rectangle2D;

import marauroa.common.game.RPObject;

/**
* Server side path finder.
*/
class EntityPathfinder extends games.stendhal.server.core.pathfinder.Pathfinder {
  /**
   * The entity searching a path.
   */
  private final Entity entity;

  /**
   * The zone a path is searched.
   */
  private final StendhalRPZone zone;

  private final boolean checkEntities;

  /**
   * Contains the resistance data for entities.
   */
  private ResistanceMap resistanceMap;

  EntityPathfinder(final Entity entity, final StendhalRPZone zone, final int startX, final int startY,
      final Rectangle2D destination, final double maxDist, final boolean checkEntities) {
    super(startX, startY, destination, maxDist);
    this.entity = entity;
    this.zone = zone;
    this.checkEntities = checkEntities;
  }

  @Override
  protected void init() {
    super.init();
    if (checkEntities) {
      createEntityCollisionMap();
    }
  }

  /**
   * Creates resistance data for entities.
   * <p>The positions with entities are only
   * considered as not valid if they:
   * <li> are next to the start position or
   * <li> have stopped
   */
  private void createEntityCollisionMap() {
    Point targetPoint = new Point(goalNode.getX(), goalNode.getY());
    resistanceMap = new ResistanceMap(zone.getWidth(), zone.getHeight());
    for (final RPObject obj : zone) {
      final Entity otherEntity = (Entity) obj;
      if (!entity.getID().equals(otherEntity.getID())
          && (otherEntity.stopped() || otherEntity.nextTo(
              startNode.getX(), startNode.getY(), 0.25))) {
        final Rectangle2D area = otherEntity.getArea();
        // Hack: Allow players to move onto portals as destination
        if ((entity instanceof Player) && (otherEntity instanceof Portal) && area.contains(targetPoint)) {
          continue;
        }
        int resistance = otherEntity.getResistance(entity);
        resistanceMap.addResistance(area, resistance);
      }
    }
  }

  @Override
  public TreeNode createNode(int x, int y) {
    return new PathTreeNode(x, y);
  }

  /**
   * Pathfinder node
   */
  private class PathTreeNode extends TreeNode {
    private final double cost;

    protected PathTreeNode(int x, int y) {
      super(x, y);

      /*
       * Modify movement cost by resistance
       */
      if (resistanceMap != null) {
        int resistance = resistanceMap.getResistance(x, y , entity.getWidth(), entity.getHeight());
        cost = 100.0 / (100 - resistance);
      } else {
        cost = 1.0;
      }
    }

    @Override
    protected double getCost() {
      return cost;
    }

    @Override
    public TreeNode createNode(int x, int y) {
      return new PathTreeNode(x, y);
    }

    @Override
    protected int createNodeID(int x, int y) {
      return x + y * zone.getWidth();
    }

    @Override
    public boolean isValid(int x, int y) {
      boolean result = !zone.simpleCollides(entity, x, y, entity.getWidth(), entity.getHeight());
      if (checkEntities && result) {
        result = !resistanceMap.collides(x, y, entity.getWidth(), entity.getHeight());
      }

      return result;
    }
  }

  /**
   * Resistance data for entities.
   */
  private static class ResistanceMap {
    /** Resistance that corresponds to collision */
    private static final int COLLISION = 100;
    /** Minimum resistance that is considered a collision */
    private static final int COLLIDE_THRESHOLD = 95;

    private final int width, height;
    private final int[][] map;

    /**
     * Create a new ResistanceMap.
     *
     * @param width width of the area
     * @param height height of the area
     */
    public ResistanceMap(int width, int height) {
      this.width = width;
      this.height = height;
      map = new int[width][height];
    }

    /**
     * Check if an area is impassable for the entity.
     *
     * @param x the x coordinate of the upper left corner of the rectangle to be checked
     * @param y the y coordinate of the upper left corner of the rectangle to be checked
     * @param w the width of the rectangle to be checked
     * @param h the height of the rectangle to be checked
     * @return <code>true</code> if area can not be occupied,
     *   <code>false</code> otherwise
     */
    public boolean collides(final double x, final double y, double w, double h) {
      return getResistance(x, y, w, h) > COLLIDE_THRESHOLD;
    }

    /**
     * Add resistance of an area to the entity.
     *
     * @param area affected area
     * @param resistance value between 0 and 100
     */
    public void addResistance(Rectangle2D area, int resistance) {
      final double x = area.getX();
      final double y = area.getY();
      double w = area.getWidth();
      double h = area.getHeight();

      final int startx = (int) Math.max(0, x);
      final int endx = (int) Math.min(width, x + w);
      final int starty = (int) Math.max(0, y);
      final int endy = (int) Math.min(height, y + h);

      // Fill the area
      for (int k = startx; k < endx; k++) {
        for (int i = starty; i < endy; i++) {
          /*
           * There can be multiple entities covering an area. (Such
           * as blood covering a grower). Can we have multiple
           * non-zero resistances? Cover the case anyway, in case we
           * want to give something like corpses some resistance to
           * make it harder to wade through a pile of bodies.
           */
          int old = map[k][i];
          /*
           * Add up like probabilities. Several slightly resistant
           * entities can still add up to a completely impassable
           * barrier, when the resistance grows over
           * COLLIDE_THRESHOLD.
           */
          map[k][i] = 100 - ((100 - old) * (100 - resistance)) / 100;
        }
      }
    }

    /**
     * Get resistance for placing the entity to an area.
     *
     * @param x the x coordinate of the upper left corner of the rectangle to be checked
     * @param y the y coordinate of the upper left corner of the rectangle to be checked
     * @param w the width of the rectangle to be checked
     * @param h the height of the rectangle to be checked
     * @return resistance
     */
    public int getResistance(final double x, final double y, double w, double h) {
      if ((x < 0) || (x >= width)) {
        return COLLISION;
      }

      if ((y < 0) || (y >= height)) {
        return COLLISION;
      }

      final int startx = (int) Math.max(0, x);
      final int endx = (int) Math.min(width, x + w);
      final int starty = (int) Math.max(0, y);
      final int endy = (int) Math.min(height, y + h);

      final int entitySize = (int) (w * h);
      int resistance = 0;
      for (int k = startx; k < endx; k++) {
        for (int i = starty; i < endy; i++) {
          int r = map[k][i];
          if (r > COLLIDE_THRESHOLD) {
            /*
             * A full collision is always collision, regardless of
             * the other tiles.
             */
            return COLLISION;
          } else {
            /*
             * A large creature will find walking over partial
             * collision easier than small one. It can step over it
             * or just push through using force. On the other hand
             * a smaller entity can possibly run between the
             * resistant areas.
             */
            resistance += r / entitySize;
          }
        }
      }

      return resistance;
    }
  }
}
TOP

Related Classes of games.stendhal.server.core.pathfinder.EntityPathfinder$ResistanceMap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.