untitledTextAdventure/src/main/java/game/logic/actionsystem/PlayerAction.java

122 lines
4.6 KiB
Java

package game.logic.actionsystem;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import game.logic.GameLogic;
import game.state.Entity;
import game.state.EntitySet;
/**
* A player action represents a single action which operates on any number of
* entities.
*
* It has an identifier, e.g. <code>"combine"</code>, a signature of needed
* entities, and an executor. The signature consists multiple entries, each one
* being either a single entity or a group of entities. To execute a player
* action an <a href="#EntitySet"><code>EntitySet</code></a> must be given which
* can be matched against its signature.
*/
public class PlayerAction {
private record SignatureEntry(EntitySet entities, int count) {
}
private final String id;
private final PlayerActionExecutor executor;
private final List<SignatureEntry> signatureEntries = new LinkedList<>();
private int neededEntitiesTotalCount = 0;
/**
* Constructs a player action by providing an id and an executor.
*
* @param id The action id
* @param executor The executor which is called when
* <a href="#tryExecute">tryExecute</a> is called with an entity
* set which can be matched against the signature.
*/
public PlayerAction(String id, PlayerActionExecutor executor) {
this.id = id;
this.executor = executor;
}
/**
* Getter for the action's id.
*
* @return The action id
*/
public String getId() {
return this.id;
}
/**
* Pushes a new entry to this action' signature consisting of a single entity
* which is needed for execution.
*
* @param entity The needed entity to push
*/
public void pushNeededEntity(Entity entity) {
this.signatureEntries.add(new SignatureEntry(EntitySet.createTemporary(entity), 1));
++this.neededEntitiesTotalCount;
}
/**
* Pushes a new entry to this action's signature consisting of a set of entities
* of which a specific count is needed for execution.
*
* @param entities The set of needed entities
* @param count How many entities of the given set are needed for execution
*/
public void pushVaryingNeededEntites(EntitySet entities, int count) {
this.signatureEntries.add(new SignatureEntry(entities, count));
this.neededEntitiesTotalCount += count;
}
/**
* Tries to match the given entities to this action's signature and executes it
* if successful.
*
* @param primaryEntity An optional primary entity which will be matched
* against the first signature entry.
* @param secondaryEntities A set of entities which will be matched against the
* signature (excl. the first entry if
* <code>primaryEntity</code> was not
* <code>null</code>)
* @param logic The game logic
* @return <code>true</code>, if given entities match the signature and the
* action was executed
*/
public boolean tryExecute(Entity primaryEntity, EntitySet secondaryEntities, GameLogic logic) {
if ((primaryEntity == null ? 0 : 1)
+ (secondaryEntities == null ? 0 : secondaryEntities.getSize()) != this.neededEntitiesTotalCount) {
return false;
}
int entityCounts[] = new int[this.signatureEntries.size()];
Entity entitiesToUse[] = new Entity[this.neededEntitiesTotalCount];
if (primaryEntity != null) {
if (this.signatureEntries.isEmpty() || !this.signatureEntries.get(0).entities().contains(primaryEntity)) {
return false;
}
entitiesToUse[entityCounts[0]++] = primaryEntity;
}
if (secondaryEntities != null) {
for (Entity e : secondaryEntities.getAll()) {
int idx = 0;
for (int i = 0; i < this.signatureEntries.size(); ++i) {
SignatureEntry signatureEntry = this.signatureEntries.get(i);
if (entityCounts[i] != signatureEntry.count() && signatureEntry.entities().contains(e)) {
entitiesToUse[idx + entityCounts[i]++] = e;
break;
}
idx += signatureEntry.count();
}
}
}
if (Arrays.stream(entitiesToUse).anyMatch(Objects::isNull)) {
return false;
}
return this.executor.execute(logic, entitiesToUse);
}
}