feat: add handling for stan and goose
This commit is contained in:
parent
01ccabad14
commit
a416698adf
@ -16,6 +16,17 @@ public class IntVector2 implements Serializable {
|
|||||||
public final static IntVector2 Y = new IntVector2(0, 1);
|
public final static IntVector2 Y = new IntVector2(0, 1);
|
||||||
public final static IntVector2 Zero = new IntVector2(0, 0);
|
public final static IntVector2 Zero = new IntVector2(0, 0);
|
||||||
|
|
||||||
|
public final static IntVector2[] CardinalDirections = {
|
||||||
|
new IntVector2(1, 0),
|
||||||
|
new IntVector2(0, 1),
|
||||||
|
new IntVector2(1, 1),
|
||||||
|
new IntVector2(-1, 0),
|
||||||
|
new IntVector2(0, -1),
|
||||||
|
new IntVector2(-1, -1),
|
||||||
|
new IntVector2(1, -1),
|
||||||
|
new IntVector2(-1, 1),
|
||||||
|
};
|
||||||
|
|
||||||
private final static float nearZero = 0.000000001f;
|
private final static float nearZero = 0.000000001f;
|
||||||
|
|
||||||
public IntVector2(int x, int y) {
|
public IntVector2(int x, int y) {
|
||||||
|
@ -12,12 +12,13 @@ import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/** Contains game logic handling. */
|
/** Contains game logic handling. */
|
||||||
class GameLogic {
|
class GameLogic {
|
||||||
|
private static final Random rand = new Random();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces resulting {@link Event}s from a given {@link Request}.
|
* Produces resulting {@link Event}s from a given {@link Request}.
|
||||||
* @param state The game state to execute on
|
* @param state The game state to execute on
|
||||||
@ -423,20 +424,60 @@ class GameLogic {
|
|||||||
* Verifies that there is a line of sight between two positions.
|
* Verifies that there is a line of sight between two positions.
|
||||||
*/
|
*/
|
||||||
private static void requireLineOfSight(GameState state, IntVector2 start, IntVector2 end) throws InvalidRequestException {
|
private static void requireLineOfSight(GameState state, IntVector2 start, IntVector2 end) throws InvalidRequestException {
|
||||||
|
if(!checkLineOfSight(state, start, end)) {
|
||||||
|
throw new InvalidRequestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a line of sight exists between the two positions
|
||||||
|
* @param state The game state to work on
|
||||||
|
* @param start The first position
|
||||||
|
* @param end The second position
|
||||||
|
* @return Whether or not the light of sight exists
|
||||||
|
*/
|
||||||
|
private static boolean checkLineOfSight(GameState state, IntVector2 start, IntVector2 end) {
|
||||||
//naive code for the win!!! \o/
|
//naive code for the win!!! \o/
|
||||||
//at least its early exit and probably only O(ln(n*m))
|
//at least its early exit and probably only O(ln(n*m))
|
||||||
//TODO: implement proper line rasterization algorithm in GameLogic.requireLineOfSight
|
//TODO: implement proper line rasterization algorithm in GameLogic.checkLineOfSight
|
||||||
Line2D line = new Line2D.Float(start.getX(), start.getY(), end.getX(), end.getY());
|
Line2D line = new Line2D.Float(start.getX(), start.getY(), end.getX(), end.getY());
|
||||||
for(int i = start.getX(); i <= end.getX(); i++) {
|
for(int i = start.getX(); i <= end.getX(); i++) {
|
||||||
for(int j = start.getY(); j <= end.getY(); j++) {
|
for(int j = start.getY(); j <= end.getY(); j++) {
|
||||||
var cell = new Rectangle.Float(i - 0.5f, j - 0.5f, 1, 1);
|
var cell = new Rectangle.Float(i - 0.5f, j - 0.5f, 1, 1);
|
||||||
if(line.intersects(cell)) {
|
if(line.intersects(cell)) {
|
||||||
if(state.entities.blocksVision(new IntVector2(i, j))) {
|
if(state.entities.blocksVision(new IntVector2(i, j))) {
|
||||||
throw new InvalidRequestException();
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds free neighbour options from a starting field.
|
||||||
|
* @param state The game state to work on
|
||||||
|
* @param start The starting position
|
||||||
|
* @return A list of free neighbour field options
|
||||||
|
*/
|
||||||
|
private static ArrayList<IntVector2> getFreeNeighbour(GameState state, IntVector2 start) {
|
||||||
|
ArrayList<IntVector2> options = new ArrayList<>();
|
||||||
|
|
||||||
|
if(start.getX() < 0 || start.getX() >= state.mapSize.getX() || start.getY() < 0 || start.getY() >= state.mapSize.getY()) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(IntVector2 dir: IntVector2.CardinalDirections) {
|
||||||
|
if(state.entities.findByPosition(start.add(dir)).size() == 0) {
|
||||||
|
options.add(start.add(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.size() == 0) {
|
||||||
|
return getFreeNeighbour(state, start.add(IntVector2.CardinalDirections[rand.nextInt(IntVector2.CardinalDirections.length)]));
|
||||||
|
}else {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -519,14 +560,13 @@ class GameLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int index = alive.indexOf(state.activeCharacter);
|
int index = alive.indexOf(state.activeCharacter);
|
||||||
AtomicInteger turnOrderSize = new AtomicInteger(alive.size());
|
|
||||||
if(index == alive.size() - 1) {
|
if(index == alive.size() - 1) {
|
||||||
result.addAll(handleRoundStart(state, turnOrderSize));
|
result.addAll(handleRoundStart(state));
|
||||||
}else {
|
}else {
|
||||||
state.activeCharacter = alive.get(index + 1);
|
state.activeCharacter = alive.get(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.addAll(handleTurnStart(state, turnOrderSize.get()));
|
result.addAll(handleTurnStart(state));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -536,39 +576,141 @@ class GameLogic {
|
|||||||
* @param state The game state to work on
|
* @param state The game state to work on
|
||||||
* @return The list of resulting {@link Event}s
|
* @return The list of resulting {@link Event}s
|
||||||
*/
|
*/
|
||||||
public static ArrayList<Event> handleRoundStart(GameState state, AtomicInteger turnOrderSize) {
|
public static ArrayList<Event> handleRoundStart(GameState state) {
|
||||||
ArrayList<Event> result = new ArrayList<>();
|
ArrayList<Event> result = new ArrayList<>();
|
||||||
|
|
||||||
state.roundNumber++;
|
state.roundNumber++;
|
||||||
|
|
||||||
|
if(state.roundNumber >= 1 && state.roundNumber <= 6) {
|
||||||
|
result.addAll(handleGoose(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<EntityID> revived = new HashSet<>();
|
||||||
|
if(state.roundNumber == 7) {
|
||||||
|
result.addAll(handleStan(state, revived));
|
||||||
|
}
|
||||||
|
|
||||||
Collections.shuffle(state.turnOrder);
|
Collections.shuffle(state.turnOrder);
|
||||||
|
|
||||||
ArrayList<EntityID> alive = new ArrayList<>();
|
|
||||||
for (EntityID id: state.turnOrder) {
|
for (EntityID id: state.turnOrder) {
|
||||||
Character character = ((Character)state.entities.findEntity(id));
|
if(revived.contains(id) || ((Character)state.entities.findEntity(id)).hp.getValue() > 0){
|
||||||
if(character.hp.getValue() > 0){
|
state.activeCharacter = id;
|
||||||
alive.add(id);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
turnOrderSize.set(alive.size());
|
|
||||||
|
|
||||||
state.activeCharacter = alive.get(0);
|
|
||||||
|
|
||||||
result.add(new EventBuilder(EventType.RoundSetupEvent)
|
result.add(new EventBuilder(EventType.RoundSetupEvent)
|
||||||
.withRoundCount(state.roundNumber)
|
.withRoundCount(state.roundNumber)
|
||||||
.withCharacterOrder(alive.toArray(new EntityID[0]))
|
.withCharacterOrder(state.turnOrder.toArray(new EntityID[0]))
|
||||||
.buildGameEvent());
|
.buildGameEvent());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the actions of Goose at rounds 1-6.
|
||||||
|
* @param state The game state to work on
|
||||||
|
* @return The list of resulting {@link Event}s
|
||||||
|
*/
|
||||||
|
public static ArrayList<Event> handleGoose(GameState state) {
|
||||||
|
ArrayList<Event> result = new ArrayList<>();
|
||||||
|
|
||||||
|
StoneType[] available = state.unvomitedStones.toArray(new StoneType[0]);
|
||||||
|
StoneType stone = available[rand.nextInt(available.length)];
|
||||||
|
state.unvomitedStones.remove(stone);
|
||||||
|
|
||||||
|
ArrayList<IntVector2> free = new ArrayList<>();
|
||||||
|
for(int x = 0; x < state.mapSize.getX(); x++) {
|
||||||
|
for(int y = 0; y < state.mapSize.getY(); y++) {
|
||||||
|
IntVector2 pos = new IntVector2(x, y);
|
||||||
|
if(state.entities.findByPosition(pos).size() == 0) {
|
||||||
|
free.add(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IntVector2 position = free.get(rand.nextInt(free.size()));
|
||||||
|
|
||||||
|
EntityID goose = new EntityID(EntityType.NPC, 0);
|
||||||
|
result.add(new EventBuilder(EventType.SpawnEntityEvent)
|
||||||
|
.withEntity(new NPC(goose, position))
|
||||||
|
.buildEntityEvent());
|
||||||
|
result.add(new EventBuilder(EventType.SpawnEntityEvent)
|
||||||
|
.withEntity(new InfinityStone(new EntityID(EntityType.InfinityStones, stone.getID()), position, stone))
|
||||||
|
.buildEntityEvent());
|
||||||
|
result.add(new EventBuilder(EventType.DestroyedEntityEvent)
|
||||||
|
.withTargetEntity(goose)
|
||||||
|
.withTargetField(position)
|
||||||
|
.buildEntityEvent());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the actions of Stan at round 7.
|
||||||
|
* @param state The game state to work on
|
||||||
|
* @return The list of resulting {@link Event}s
|
||||||
|
*/
|
||||||
|
public static ArrayList<Event> handleStan(GameState state, HashSet<EntityID> revived) {
|
||||||
|
ArrayList<Event> result = new ArrayList<>();
|
||||||
|
|
||||||
|
ArrayList<Character> characters = new ArrayList<>();
|
||||||
|
|
||||||
|
ArrayList<IntVector2> targetOptions = new ArrayList<>();
|
||||||
|
int lowest = -1;
|
||||||
|
for(EntityID id: state.turnOrder) {
|
||||||
|
Character character = (Character)state.entities.findEntity(id);
|
||||||
|
characters.add(character);
|
||||||
|
|
||||||
|
if(lowest == -1 || character.hp.getValue() < lowest) {
|
||||||
|
lowest = character.hp.getValue();
|
||||||
|
targetOptions.clear();
|
||||||
|
}
|
||||||
|
if(lowest == character.hp.getValue()) {
|
||||||
|
targetOptions.add(character.getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IntVector2 targetPosition = targetOptions.get(rand.nextInt(targetOptions.size()));
|
||||||
|
|
||||||
|
ArrayList<IntVector2> spawnOptions = getFreeNeighbour(state, targetPosition);
|
||||||
|
if(spawnOptions.size() == 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
IntVector2 spawnPosition = spawnOptions.get(rand.nextInt(spawnOptions.size()));
|
||||||
|
|
||||||
|
EntityID stan = new EntityID(EntityType.NPC, 1);
|
||||||
|
result.add(new EventBuilder(EventType.SpawnEntityEvent)
|
||||||
|
.withEntity(new NPC(stan, spawnPosition))
|
||||||
|
.buildEntityEvent());
|
||||||
|
|
||||||
|
for(Character character: characters) {
|
||||||
|
if(checkLineOfSight(state, spawnPosition, character.getPosition())) {
|
||||||
|
if(character.hp.getValue() == 0) {
|
||||||
|
revived.add(character.id);
|
||||||
|
}
|
||||||
|
if(character.hp.getValue() != character.hp.max) {
|
||||||
|
result.add(new EventBuilder(EventType.HealedEvent)
|
||||||
|
.withTargetEntity(character.id)
|
||||||
|
.withTargetField(character.getPosition())
|
||||||
|
.withAmount(character.hp.max - character.hp.getValue())
|
||||||
|
.buildEntityEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(new EventBuilder(EventType.DestroyedEntityEvent)
|
||||||
|
.withTargetEntity(stan)
|
||||||
|
.withTargetField(spawnPosition)
|
||||||
|
.buildEntityEvent());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles everything that happens at the beginning of a turn.
|
* Handles everything that happens at the beginning of a turn.
|
||||||
* @param state The game state to work on
|
* @param state The game state to work on
|
||||||
* @return The list of resulting {@link Event}s
|
* @return The list of resulting {@link Event}s
|
||||||
*/
|
*/
|
||||||
public static ArrayList<Event> handleTurnStart(GameState state, int turnOrderSize) {
|
public static ArrayList<Event> handleTurnStart(GameState state) {
|
||||||
ArrayList<Event> result = new ArrayList<>();
|
ArrayList<Event> result = new ArrayList<>();
|
||||||
|
|
||||||
state.turnNumber++;
|
state.turnNumber++;
|
||||||
@ -590,7 +732,7 @@ class GameLogic {
|
|||||||
.buildGameEvent());
|
.buildGameEvent());
|
||||||
}
|
}
|
||||||
result.add(new EventBuilder(EventType.TurnEvent)
|
result.add(new EventBuilder(EventType.TurnEvent)
|
||||||
.withTurnCount(turnOrderSize)
|
.withTurnCount(state.turnOrder.size())
|
||||||
.withNextCharacter(state.activeCharacter)
|
.withNextCharacter(state.activeCharacter)
|
||||||
.buildGameEvent());
|
.buildGameEvent());
|
||||||
|
|
||||||
@ -616,7 +758,7 @@ class GameLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the victory of thanos after all characters are knocked out.
|
* Handles the special victory conditions after all characters are knocked out.
|
||||||
* @param state The game state to work on
|
* @param state The game state to work on
|
||||||
* @return The list of resulting {@link Event}s
|
* @return The list of resulting {@link Event}s
|
||||||
*/
|
*/
|
||||||
|
@ -5,7 +5,9 @@ import uulm.teamname.marvelous.gamelibrary.entities.*;
|
|||||||
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
import uulm.teamname.marvelous.gamelibrary.Tuple;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
/** Represents the state of a game instance. */
|
/** Represents the state of a game instance. */
|
||||||
class GameState {
|
class GameState {
|
||||||
@ -15,6 +17,9 @@ class GameState {
|
|||||||
/** The list of {@link Entity}s inside the game */
|
/** The list of {@link Entity}s inside the game */
|
||||||
public final EntityManager entities = new EntityManager();
|
public final EntityManager entities = new EntityManager();
|
||||||
|
|
||||||
|
/** The set of stones that are yet to be placed on the map */
|
||||||
|
public HashSet<StoneType> unvomitedStones = new HashSet<>(Arrays.asList(StoneType.values()));
|
||||||
|
|
||||||
/** The total amount of full turn cycles that occurred */
|
/** The total amount of full turn cycles that occurred */
|
||||||
public int roundNumber = 0;
|
public int roundNumber = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user