From a67634ab086743eb72dfd3081a8de9ed874e4caf Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:52:07 +1000 Subject: [PATCH] Add splitting mobs functionality --- .../csse3200/game/areas/ForestGameArea.java | 20 +-- .../game/components/npc/SplitMoblings.java | 149 ++++++++++++++++++ .../game/entities/factories/NPCFactory.java | 10 ++ 3 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java diff --git a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java index 51dad62d6..1defedc59 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -14,6 +14,8 @@ import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; import com.csse3200.game.components.gamearea.GameAreaDisplay; +import com.csse3200.game.components.npc.SplitMoblings; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Random; @@ -250,12 +252,12 @@ public void create() { // spawnSplitFireWorksFireBall(new Vector2(2, 5), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), 12); // spawnEffectProjectile(new Vector2(2, 6), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.SLOW, false); // spawnXenoGrunts(); -// spawnWeaponTower(); + spawnWeaponTower(); // spawnDragonKnight(); - spawnFireWorm(19, 5); // * TEMPORARY - spawnXenoGrunt(19, 4); - // spawnDemonBoss(); + spawnFireWorm(19, 5); // * TEMPORARY for testing + spawnSplittingXenoGrunt(17, 5); + spawnDemonBoss(); // spawnFireWorm(); //mobBoss1 = spawnMobBoss1(); @@ -495,11 +497,11 @@ private void spawnXenoGrunts() { } // * TEMPORARY FOR TESTING - private void spawnXenoGrunt(int x, int y) { + private void spawnSplittingXenoGrunt(int x, int y) { GridPoint2 pos = new GridPoint2(x, y); - Entity xenoGrint = NPCFactory.createXenoGrunt(player); - xenoGrint.setScale(1.5f, 1.5f); - spawnEntityAt(xenoGrint, pos, true, true); + Entity xenoGrunt = NPCFactory.createSplittingXenoGrunt(); + xenoGrunt.setScale(1.5f, 1.5f); + spawnEntityAt(xenoGrunt, pos, false, true); } private void spawnFireWorm() { @@ -665,7 +667,7 @@ private void spawnSplitFireWorksFireBall(Vector2 position, short targetLayer, in private void spawnWeaponTower() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - + for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { GridPoint2 randomPos1 = RandomUtils.random(minPos, maxPos); GridPoint2 randomPos2 = RandomUtils.random(minPos, maxPos); diff --git a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java new file mode 100644 index 000000000..16b54161a --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java @@ -0,0 +1,149 @@ +package com.csse3200.game.components.npc; + +import com.csse3200.game.components.Component; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.NPCFactory; +import com.csse3200.game.services.ServiceLocator; + +/** + * A component that splits the target mob entity into multiple entities after + * after the mob dies. This class adds a method to the exisiting event listener + * "dieStart". + *

+ * Amount of moblings spawned must be provided in the construcor. + * Scaled size of the moblings can be altered in the X and Y direction if + * desired. If not provided scaling alteration is assumed to be 0.75. + *

+ */ +public class SplitMoblings extends Component { + private int amount; + private float scaleX, scaleY; + public static final float DEFAULT_MINIFIED_SCALE = 0.75f; + public static final double OFFSET_DISTANCE = 1.5; + public static final int FULL_CIRCLE_ANGLE = 360; + public static final float MIN_X_BOUNDS = 1; + public static final float MAX_X_BOUNDS = (float) 18.5; + public static final float MIN_Y_BOUNDS = 0; + public static final float MAX_Y_BOUNDS = 8; + + /** + * Initialises a component that splits mob into multiple moblings. Amount of + * moblings split based on the amount provided param. + * + * @param amount Amount of moblings to be split. + * @require amount > 0 + */ + public SplitMoblings(int amount) { + this.amount = amount; + scaleX = scaleY = DEFAULT_MINIFIED_SCALE; + } + + /** + * Initialises a component that splits mob into multiple moblings. Amount of + * moblings split is based on the amount provided param. + * The overalling scaling (x and y) is also altered in the param. + * + * @param amount Amount of moblings to be split. + * @param scale X and Y scaling of the moblings in respect to the original size + * of the mobs. + * @require amount > 0 + */ + public SplitMoblings(int amount, float scale) { + this.amount = amount; + this.scaleX = this.scaleY = scale; + } + + /** + * Initialises a component that splits mob into multiple moblings. Amount of + * moblings split is based on the amount provided param. + * The individual scaling (x and y) is also altered in the param. + * + * @param amount Amount of moblings to be split. + * @param scaleX X scaling of the moblings compared to original size. + * @param scaleY Y scaling of the moblings compared to original size. + * @require amount > 0 + */ + public SplitMoblings(int amount, float scaleX, float scaleY) { + this.amount = amount; + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + @Override + public void create() { + entity.getEvents().addListener("dieStart", this::onDeath); + } + + /** + * Splits into multiple xeno grunts after death + */ + private void onDeath() { + float initialScaleX = entity.getScale().x; + float initialScaleY = entity.getScale().y; + + // If there's only one amount to be spawned, spawn it 1 x-coordinate to the + // left. + if (amount == 1) { + float newXPosition = (float) (entity.getPosition().x - OFFSET_DISTANCE); + float newYPosition = (float) (entity.getPosition().y); + + if (withinBounds(newXPosition, newYPosition)) + spawnAdditionalMob(newXPosition, newYPosition, initialScaleX, initialScaleY); + } + + // Inspired by: + // https://stackoverflow.com/questions/37145768/distribute-points-evenly-on-circle-circumference-in-quadrants-i-and-iv-only + for (int i = 0; i < amount; i++) { + float currAngle = (360 / amount) * i; + double radians = currAngle * Math.PI / 180; + + float newX = entity.getPosition().x + (float) OFFSET_DISTANCE * + (float) Math.cos(radians); + float newY = entity.getPosition().y + (float) OFFSET_DISTANCE * + (float) Math.sin(radians); + + if (withinBounds(newX, newY)) + spawnAdditionalMob(newX, newY, initialScaleX, initialScaleY); + } + } + + /** + * Helper function that spawns a xeno grunt based on a x and y-coordinate and + * scales down/up the entity based on the initial scale and this object's + * scale. + * + * @param positionX New spawn x-coordinate + * @param positionY New spawn y-coordinate + * @param initialScaleX Initial horizontal scale of the entity + * @param initialScaleY Initial vertical scale of the entity + */ + public void spawnAdditionalMob(float positionX, float positionY, float initialScaleX, float initialScaleY) { + // ? ENTITY PARAM KINDA USELESS? + Entity xenoGrunt = NPCFactory.createXenoGrunt(new Entity()); + xenoGrunt.setPosition(positionX, positionY); + + xenoGrunt.setScale(initialScaleX * scaleX, initialScaleY * scaleY); + + ServiceLocator.getEntityService().register(xenoGrunt); + } + + /** + * Helper to check if the current projectile position is within map bounds. + * Prevents spawning of mobs outof bounds. + * + * @param currX x-coordinate of the gamegrid. + * @param currY y-coordinate of the gamegrid. + * @return true if current position is within bounds of the map constraints. + * False otherwise. + */ + private boolean withinBounds(float currX, float currY) { + if (currX >= MIN_X_BOUNDS + && currX <= MAX_X_BOUNDS + && currY >= MIN_Y_BOUNDS + && currY <= MAX_Y_BOUNDS) { + return true; + } + ; + return false; + } +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java index b2a11badb..16388e165 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java @@ -9,6 +9,7 @@ import com.csse3200.game.components.npc.DragonKnightAnimationController; import com.csse3200.game.components.npc.FireWormAnimationController; import com.csse3200.game.components.npc.GhostAnimationController; +import com.csse3200.game.components.npc.SplitMoblings; import com.csse3200.game.components.npc.XenoAnimationController; import com.csse3200.game.components.tasks.MobAttackTask; import com.csse3200.game.components.tasks.MobWanderTask; @@ -222,6 +223,15 @@ public static Entity createBaseNPC(Entity target) { private NPCFactory() { throw new IllegalStateException("Instantiating static util class"); } + + public static Entity createSplittingXenoGrunt() { + Entity splitXenoGrunt = createXenoGrunt(new Entity()) + // add the scaling yourself. can also scale the X and Y component, + // leading to some very interesting mob designs. + .addComponent(new SplitMoblings(7, 2.25f)); + + return splitXenoGrunt; + } }