diff --git a/src/main/java/org/terasology/behaviors/actions/CheckAttackStopAction.java b/src/main/java/org/terasology/behaviors/actions/CheckAttackStopAction.java index ff01d42c..12496d1e 100644 --- a/src/main/java/org/terasology/behaviors/actions/CheckAttackStopAction.java +++ b/src/main/java/org/terasology/behaviors/actions/CheckAttackStopAction.java @@ -15,6 +15,7 @@ */ package org.terasology.behaviors.actions; +import org.terasology.behaviors.components.AttackInProximityComponent; import org.terasology.behaviors.components.AttackOnHitComponent; import org.terasology.logic.behavior.BehaviorAction; import org.terasology.logic.behavior.core.Actor; @@ -40,9 +41,15 @@ public class CheckAttackStopAction extends BaseAction { public BehaviorState modify(Actor actor, BehaviorState state) { BehaviorState status = getBehaviorStateWithoutReturn(actor); if (status == BehaviorState.FAILURE) { - AttackOnHitComponent attackOnHitComponent = actor.getComponent(AttackOnHitComponent.class); - attackOnHitComponent.instigator = null; - actor.getEntity().saveComponent(attackOnHitComponent); + if (actor.hasComponent(AttackOnHitComponent.class)) { + AttackOnHitComponent attackOnHitComponent = actor.getComponent(AttackOnHitComponent.class); + attackOnHitComponent.instigator = null; + actor.getEntity().saveComponent(attackOnHitComponent); + } else if (actor.hasComponent(AttackInProximityComponent.class)) { + AttackInProximityComponent attackInProximityComponent = actor.getComponent(AttackInProximityComponent.class); + attackInProximityComponent.instigator = null; + actor.getEntity().saveComponent(attackInProximityComponent); + } actor.getEntity().removeComponent(FollowComponent.class); } return status; @@ -54,7 +61,12 @@ private BehaviorState getBehaviorStateWithoutReturn(Actor actor) { return BehaviorState.FAILURE; } Vector3f actorPosition = actorLocationComponent.getWorldPosition(); - float maxDistance = actor.hasComponent(AttackOnHitComponent.class) ? actor.getComponent(AttackOnHitComponent.class).maxDistance : this.maxDistance; + float maxDistance = this.maxDistance; + if (actor.hasComponent(AttackOnHitComponent.class)) { + maxDistance = actor.getComponent(AttackOnHitComponent.class).maxDistance; + } else if (actor.hasComponent(AttackInProximityComponent.class)) { + maxDistance = actor.getComponent(AttackInProximityComponent.class).maxDistance; + } float maxDistanceSquared = maxDistance * maxDistance; FollowComponent followWish = actor.getComponent(FollowComponent.class); diff --git a/src/main/java/org/terasology/behaviors/components/AttackInProximityComponent.java b/src/main/java/org/terasology/behaviors/components/AttackInProximityComponent.java new file mode 100644 index 00000000..ab9d83d5 --- /dev/null +++ b/src/main/java/org/terasology/behaviors/components/AttackInProximityComponent.java @@ -0,0 +1,31 @@ +/* + * Copyright 2020 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.behaviors.components; + +import org.terasology.entitySystem.Component; +import org.terasology.entitySystem.entity.EntityRef; + +/** + * Represents an NPC that will attack players when they enter {@code maxDistance} range. + */ +public class AttackInProximityComponent implements Component { + // Maximum distance from instigator after which the animal will stop chasing to attack + public float maxDistance = 10f; + // Speed factor by which attack speed increases + public float speedMultiplier = 1.2f; + public EntityRef instigator; + public long timeWhenHit; +} diff --git a/src/main/java/org/terasology/behaviors/system/FindNearbyPlayersSystem.java b/src/main/java/org/terasology/behaviors/system/FindNearbyPlayersSystem.java index bbf584f0..eaaf86ae 100644 --- a/src/main/java/org/terasology/behaviors/system/FindNearbyPlayersSystem.java +++ b/src/main/java/org/terasology/behaviors/system/FindNearbyPlayersSystem.java @@ -15,7 +15,6 @@ */ package org.terasology.behaviors.system; -import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.entitySystem.entity.EntityManager; @@ -31,9 +30,15 @@ import org.terasology.registry.In; import org.terasology.behaviors.components.FindNearbyPlayersComponent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @RegisterSystem(RegisterMode.AUTHORITY) public class FindNearbyPlayersSystem extends BaseComponentSystem implements UpdateSubscriberSystem { @@ -46,47 +51,48 @@ public class FindNearbyPlayersSystem extends BaseComponentSystem implements Upda @Override public void update(float delta) { + Iterable clients = entityManager.getEntitiesWith(ClientComponent.class); + Map clientLocationMap = new HashMap<>(); + + for (EntityRef client : clients) { + ClientComponent clientComponent = client.getComponent(ClientComponent.class); + EntityRef character = clientComponent.character; + AliveCharacterComponent aliveCharacterComponent = character.getComponent(AliveCharacterComponent.class); + if (aliveCharacterComponent == null) { + continue; + } + LocationComponent locationComponent = character.getComponent(LocationComponent.class); + if (locationComponent == null) { + continue; + } + clientLocationMap.put(locationComponent.getWorldPosition(), character); + } + Set locationSet = clientLocationMap.keySet(); + for (EntityRef entity : entityManager.getEntitiesWith(FindNearbyPlayersComponent.class)) { Vector3f actorPosition = entity.getComponent(LocationComponent.class).getWorldPosition(); FindNearbyPlayersComponent findNearbyPlayersComponent = entity.getComponent(FindNearbyPlayersComponent.class); float maxDistance = findNearbyPlayersComponent.searchRadius; float maxDistanceSquared = maxDistance * maxDistance; - Iterable clients = entityManager.getEntitiesWith(ClientComponent.class); - List charactersWithinRange = Lists.newArrayList(); - - EntityRef closestCharacter = EntityRef.NULL; - float minDistanceFromCharacter = 0.0f; - for (EntityRef client : clients) { - ClientComponent clientComponent = client.getComponent(ClientComponent.class); - EntityRef character = clientComponent.character; - AliveCharacterComponent aliveCharacterComponent = character.getComponent(AliveCharacterComponent.class); - if (aliveCharacterComponent == null) { - continue; - } - LocationComponent locationComponent = character.getComponent(LocationComponent.class); - if (locationComponent == null) { - continue; - } - if (locationComponent.getWorldPosition().distanceSquared(actorPosition) <= maxDistanceSquared) { - if (charactersWithinRange.size() == 0) { - closestCharacter = character; - minDistanceFromCharacter = locationComponent.getWorldPosition().distanceSquared(actorPosition); - } else { - if (locationComponent.getWorldPosition().distanceSquared(actorPosition) < minDistanceFromCharacter) { - closestCharacter = character; - minDistanceFromCharacter = locationComponent.getWorldPosition().distanceSquared(actorPosition); - } - } - charactersWithinRange.add(character); - } + List inRange = locationSet.stream() + .filter(loc -> loc.distanceSquared(actorPosition) <= maxDistanceSquared) + .sorted(Comparator.comparingDouble(v3f -> v3f.distanceSquared(actorPosition))) + .collect(Collectors.toList()); + if (inRange.isEmpty()) { + findNearbyPlayersComponent.charactersWithinRange = Collections.emptyList(); + findNearbyPlayersComponent.closestCharacter = EntityRef.NULL; + entity.saveComponent(findNearbyPlayersComponent); + continue; } + List charactersWithinRange = + inRange.stream().map(clientLocationMap::get).collect(Collectors.toList()); + if (!isEqual(charactersWithinRange, findNearbyPlayersComponent.charactersWithinRange)) { findNearbyPlayersComponent.charactersWithinRange = charactersWithinRange; - findNearbyPlayersComponent.closestCharacter = closestCharacter; + findNearbyPlayersComponent.closestCharacter = charactersWithinRange.get(0); entity.saveComponent(findNearbyPlayersComponent); - } } }