Java tutorial
/** * This file is part of Aion-Lightning <aion-lightning.org>. * * Aion-Lightning 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 3 of the License, or * (at your option) any later version. * * Aion-Lightning is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Aion-Lightning. * If not, see <http://www.gnu.org/licenses/>. */ package com.aionemu.gameserver.spawnengine; import com.aionemu.gameserver.ai2.AI2Logger; import com.aionemu.gameserver.ai2.AIState; import com.aionemu.gameserver.ai2.AISubState; import com.aionemu.gameserver.ai2.NpcAI2; import com.aionemu.gameserver.ai2.manager.WalkManager; import com.aionemu.gameserver.model.gameobjects.Npc; import com.aionemu.gameserver.model.templates.spawns.SpawnTemplate; import com.aionemu.gameserver.model.templates.zone.Point2D; import org.apache.commons.lang.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import static ch.lambdaj.Lambda.*; /** * @author vlog * @modified Rolandas */ public class WalkerGroup { private static final Logger log = LoggerFactory.getLogger(WalkerGroup.class); private List<ClusteredNpc> members; private WalkerGroupType type; private final float walkerXpos; private final float walkerYpos; private int[] memberSteps; private volatile int groupStep; private final String versionId; private boolean isSpawned; public WalkerGroup(List<ClusteredNpc> members) { this.members = sort(members, on(ClusteredNpc.class).getWalkerIndex()); memberSteps = new int[members.size()]; walkerXpos = members.get(0).getX(); walkerYpos = members.get(0).getY(); type = members.get(0).getWalkTemplate().getType(); versionId = members.get(0).getWalkTemplate().getVersionId(); } public void form() { if (getWalkType() == WalkerGroupType.SQUARE) { int[] rows = members.get(0).getWalkTemplate().getRows(); if (sum(ArrayUtils.toObject(rows), on(Integer.class)) != members.size()) { log.warn("Invalid row sizes for walk cluster " + members.get(0).getWalkTemplate().getRouteId()); } if (rows.length == 1) { // Line formation: distance 2 meters from each other (divide by 2 and multiple by 2) // negative at left hand and positive at the right hand float bounds = sum(members, on(ClusteredNpc.class).getNpc().getObjectTemplate().getBoundRadius().getSide()); float distance = (1 - members.size()) / 2f * (WalkerGroupShift.DISTANCE + bounds); Point2D origin = new Point2D(walkerXpos, walkerYpos); Point2D destination = new Point2D(members.get(0).getWalkTemplate().getRouteStep(2).getX(), members.get(0).getWalkTemplate().getRouteStep(2).getY()); for (int i = 0; i < members.size(); i++, distance += WalkerGroupShift.DISTANCE) { WalkerGroupShift shift = new WalkerGroupShift(distance, 0); Point2D loc = getLinePoint(origin, destination, shift); members.get(i).setX(loc.getX()); members.get(i).setY(loc.getY()); Npc member = members.get(i).getNpc(); member.setWalkerGroup(this); member.setWalkerGroupShift(shift); // distance += npc.getObjectTemplate().getBoundRadius().getSide(); } } else if (rows.length != 0) { float rowDistances[] = new float[rows.length - 1]; float coronalDist = 0; for (int i = 0; i < rows.length - 1; i++) { if (rows[i] % 2 != rows[i + 1] % 2) { rowDistances[i] = 0.86602540378443864676372317075294f * WalkerGroupShift.DISTANCE; } else { rowDistances[i] = WalkerGroupShift.DISTANCE; } coronalDist -= rowDistances[i]; } Point2D origin = new Point2D(walkerXpos, walkerYpos); Point2D destination = new Point2D(members.get(0).getWalkTemplate().getRouteStep(2).getX(), members.get(0).getWalkTemplate().getRouteStep(2).getY()); int index = 0; for (int i = 0; i < rows.length; i++) { float sagittalDist = (1 - rows[i]) / 2f * WalkerGroupShift.DISTANCE; for (int j = 0; j < rows[i]; j++, sagittalDist += WalkerGroupShift.DISTANCE) { if (index > members.size() - 1) { break; } WalkerGroupShift shift = new WalkerGroupShift(sagittalDist, coronalDist); Point2D loc = getLinePoint(origin, destination, shift); ClusteredNpc cnpc = members.get(index++); cnpc.setX(loc.getX()); cnpc.setY(loc.getY()); cnpc.getNpc().setWalkerGroup(this); cnpc.getNpc().setWalkerGroupShift(shift); } if (i < rows.length - 1) { coronalDist += rowDistances[i]; } } } } else if (getWalkType() == WalkerGroupType.CIRCLE) { // TODO: if needed } else if (getWalkType() == WalkerGroupType.POINT) { log.warn("No formation specified for walk cluster " + members.get(0).getWalkTemplate().getRouteId()); } } @SuppressWarnings("unused") private float getSidesExtra(int[] rows, int startIndex, int endIndex) { return 0; } /** * Returns coordinates of NPC in 2D from the initial spawn location * * @param origin - initial spawn location * @param destination - point of next move * @param shift - distance from origin located in lines perpendicular to * destination; for SagittalShift if negative then located to the left from * origin, otherwise, to the right for CoronalShift if negative then located * to back, otherwise to the front * @category TODO: move to MathUtil when all kinds of WalkerGroupType are * implemented. */ public static Point2D getLinePoint(Point2D origin, Point2D destination, WalkerGroupShift shift) { // TODO: implement angle shift WalkerGroupShift dir = getShiftSigns(origin, destination); Point2D result = null; if (origin.getY() - destination.getY() == 0) { return new Point2D(origin.getX() + dir.getCoronalShift() * shift.getCoronalShift(), origin.getY() - dir.getSagittalShift() * shift.getSagittalShift()); } else if (origin.getX() - destination.getX() == 0) { return new Point2D(origin.getX() + dir.getCoronalShift() * shift.getSagittalShift(), origin.getY() + dir.getCoronalShift() * shift.getCoronalShift()); } else { double slope = (origin.getX() - destination.getX()) / (origin.getY() - destination.getY()); double dx = Math.abs(shift.getSagittalShift()) / Math.sqrt(1 + slope * slope); if (shift.getSagittalShift() * dir.getCoronalShift() < 0) { result = new Point2D((float) (origin.getX() - dx), (float) (origin.getY() + dx * slope)); } else { result = new Point2D((float) (origin.getX() + dx), (float) (origin.getY() - dx * slope)); } } if (shift.getCoronalShift() != 0) { Point2D rotatedShift = null; if (shift.getSagittalShift() != 0) { rotatedShift = getLinePoint(origin, destination, new WalkerGroupShift( Math.signum(shift.getSagittalShift()) * Math.abs(shift.getCoronalShift()), 0)); } else { rotatedShift = getLinePoint(origin, destination, new WalkerGroupShift(Math.abs(shift.getCoronalShift()), 0)); } // since it's rotated, and perpendicular, dx and dy are reciprocal when not rotated float dx = Math.abs(origin.getX() - rotatedShift.getX()); float dy = Math.abs(origin.getY() - rotatedShift.getY()); if (shift.getCoronalShift() < 0) { if (dir.getSagittalShift() < 0 && dir.getCoronalShift() < 0) { result = new Point2D(result.getX() + dy, result.getY() + dx); } else if (dir.getSagittalShift() > 0 && dir.getCoronalShift() > 0) { result = new Point2D(result.getX() - dy, result.getY() - dx); } else if (dir.getSagittalShift() < 0 && dir.getCoronalShift() > 0) { result = new Point2D(result.getX() + dy, result.getY() - dx); } else if (dir.getSagittalShift() > 0 && dir.getCoronalShift() < 0) { result = new Point2D(result.getX() - dy, result.getY() + dx); } } else { if (dir.getSagittalShift() < 0 && dir.getCoronalShift() < 0) { result = new Point2D(result.getX() - dy, result.getY() - dx); } else if (dir.getSagittalShift() > 0 && dir.getCoronalShift() > 0) { result = new Point2D(result.getX() + dy, result.getY() + dx); } else if (dir.getSagittalShift() < 0 && dir.getCoronalShift() > 0) { result = new Point2D(result.getX() - dy, result.getY() + dx); } else if (dir.getSagittalShift() > 0 && dir.getCoronalShift() < 0) { result = new Point2D(result.getX() + dy, result.getY() - dx); } } } return result; } /* * Return a normalized direction vector */ private static WalkerGroupShift getShiftSigns(Point2D origin, Point2D destination) { float dx = Math.signum(destination.getX() - origin.getX()); float dy = Math.signum(destination.getY() - origin.getY()); return new WalkerGroupShift(dx, dy); } public void setStep(Npc member, int step) { int currentStep = 0; for (int i = 0; i < members.size(); i++) { if (memberSteps[i] > currentStep) { currentStep = memberSteps[i]; } if (members.get(i).getNpc().equals(member)) { AI2Logger.info(members.get(i).getNpc().getAi2(), "Setting step to " + step); memberSteps[i] = step; } } if (step > currentStep || step == 1) { groupStep = step; } } public void targetReached(NpcAI2 npcAI) { synchronized (members) { npcAI.setSubStateIfNot(AISubState.WALK_WAIT_GROUP); boolean allArrived = true; for (ClusteredNpc snpc : members) { allArrived &= snpc.getNpc().getAi2().getSubState() == AISubState.WALK_WAIT_GROUP; if (!allArrived) { break; } } for (int i = 0; i < members.size(); i++) { ClusteredNpc snpc = members.get(i); if ((memberSteps[i] == groupStep) && !allArrived) { npcAI.getOwner().getMoveController().abortMove(); npcAI.setStateIfNot(AIState.WALKING); npcAI.setSubStateIfNot(AISubState.WALK_WAIT_GROUP); continue; } npcAI = (NpcAI2) (snpc.getNpc().getAi2()); if (npcAI.getSubState() == AISubState.WALK_WAIT_GROUP) { WalkManager.targetReached(npcAI); } } } } public boolean isSpawned() { return isSpawned; } public void spawn() { for (ClusteredNpc snpc : members) { float height = getHeight(snpc.getX(), snpc.getY(), snpc.getNpc().getSpawn()); snpc.spawn(height); } isSpawned = true; } public void respawn(Npc npc) { for (int index = 0; index < members.size(); index++) { ClusteredNpc snpc = members.get(index); if (snpc.getWalkerIndex() == npc.getSpawn().getWalkerIndex() && snpc.getNpc().getNpcId() == npc.getNpcId()) { synchronized (members) { snpc.setNpc(npc); memberSteps[index] = 1; } break; } } } public void despawn() { for (ClusteredNpc snpc : members) { snpc.despawn(); // reset positions form(); for (int index = 0; index < memberSteps.length; index++) { memberSteps[index] = 1; } groupStep = 1; } isSpawned = false; } public ClusteredNpc getClusterData(Npc npc) { for (ClusteredNpc snpc : members) { if (snpc.getNpc().equals(npc)) { return snpc; } } return null; } private float getHeight(float x, float y, SpawnTemplate template) { /* * if (GeoService.getInstance().isGeoOn()) { return GeoService.getInstance().getZ(template.getWorldId(), x, y, z, ); * } */ return template.getZ(); } public int getPool() { return members.size(); } /** * @return the type */ public WalkerGroupType getWalkType() { return type; } public boolean isLinearlyPositioned(Npc npc) { if (type != WalkerGroupType.SQUARE) { return false; } for (ClusteredNpc snpc : members) { if (snpc.getNpc().equals(npc)) { return snpc.getWalkTemplate().getRows().length == 1; } } return false; } /** * @return the groupStep */ public int getGroupStep() { return groupStep; } public String getVersionId() { return versionId; } }