Skip to content

Commit

Permalink
Merge branch 'Team-1--Projectiles' into Projectile-Animation
Browse files Browse the repository at this point in the history
  • Loading branch information
cindyle1 committed Sep 9, 2023
2 parents b4f980a + 96011b8 commit 114591b
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 61 deletions.
68 changes: 63 additions & 5 deletions source/core/src/main/com/csse3200/game/areas/ForestGameArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ public void create() {

// Types of projectile
spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true);
spawnPierceFireBall(new Vector2(2, 3), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f));
spawnRicochetFireball(new Vector2(2, 4), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f));
spawnSplitFireWorksFireBall(new Vector2(2, 5), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), 12);
// spawnProjectileTest(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f));

spawnXenoGrunts();
Expand Down Expand Up @@ -199,23 +202,29 @@ private void spawnTerrain() {
Vector2 worldBounds = new Vector2(tileBounds.x * tileSize, tileBounds.y * tileSize);

// Left
// ! THIS ONE DOESNT WORK. GRIDPOINTS2UTIL.ZERO is (0, 4), not (0, 0)
// spawnEntityAt(
// ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), GridPoint2Utils.ZERO, false, false);
spawnEntityAt(
ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), GridPoint2Utils.ZERO, false, false);
ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), new GridPoint2(1, 0), false, false);
// Right
spawnEntityAt(
ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y),
new GridPoint2(tileBounds.x, 0),
new GridPoint2(tileBounds.x , 0),
false,
false);
// Top
spawnEntityAt(
ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH),
new GridPoint2(0, tileBounds.y),
ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH * 7),
new GridPoint2(0, tileBounds.y - 1),
false,
false);
// Bottom
// spawnEntityAt(
// ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH), GridPoint2Utils.ZERO, false, false);
// * TMPORARY
spawnEntityAt(
ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH), GridPoint2Utils.ZERO, false, false);
ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH * 7), new GridPoint2(0, 0), false, false);
}
private void spawnBuilding1() {
GridPoint2 minPos = new GridPoint2(0, 0);
Expand Down Expand Up @@ -422,6 +431,55 @@ private void spawnEffectProjectile(Vector2 position, short targetLayer, int dire
spawnEntity(Projectile);
}

/**
* Spawns a pierce fireball.
* Pierce fireball can go through targetlayers without disappearing but damage
* will still be applied.
*
* @param position The position of the Entity that's shooting the projectile.
* @param targetLayer The enemy layer of the "shooter".
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
*/
private void spawnPierceFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed) {
Entity projectile = ProjectileFactory.createPierceFireBall(targetLayer, new Vector2(direction, position.y), speed);
projectile.setPosition(position);
spawnEntity(projectile);
}

/**
* Spawns a ricochet fireball
* Ricochet fireballs bounce off targets with a specified maximum count of 3
* Possible extensions: Make the bounce count flexible with a param.
*
* @param position The position of the Entity that's shooting the projectile.
* @param targetLayer The enemy layer of the "shooter".
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
*/
private void spawnRicochetFireball(Vector2 position, short targetLayer, int direction, Vector2 speed) {
// Bounce count set to 0.
Entity projectile = ProjectileFactory.createRicochetFireball(targetLayer, new Vector2(direction, position.y), speed, 0);
projectile.setPosition(position);
spawnEntity(projectile);
}

/**
* Spawns a split firework fireball.
* Splits into mini projectiles that spreads out after collision.
*
* @param position The position of the Entity that's shooting the projectile.
* @param targetLayer The enemy layer of the "shooter".
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
* @param amount The amount of projectiles appearing after collision.
*/
private void spawnSplitFireWorksFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed, int amount) {
Entity projectile = ProjectileFactory.createSplitFireWorksFireball(targetLayer, new Vector2(direction, position.y), speed, amount);
projectile.setPosition(position);
spawnEntity(projectile);
}

private void spawnWeaponTower() {
GridPoint2 minPos = new GridPoint2(0, 0);
GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.csse3200.game.components;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.physics.BodyUserData;
import com.csse3200.game.physics.PhysicsLayer;

/**
* Entities with this component will self destruct after hitting the grid edge
* upon collision.
*/
public class DeleteOnMapEdgeComponent extends Component {

@Override
public void create() {
entity.getEvents().addListener("collisionEnd", this::onCollisionEnd);
}

private void onCollisionEnd(Fixture me, Fixture other) {
Entity selfEntity = ((BodyUserData) me.getBody().getUserData()).entity;

// * Should change the PhysicLayer to WALL / BOUNDARIES when established
if (!PhysicsLayer.contains(PhysicsLayer.WALL, other.getFilterData().categoryBits))
return;

Vector2 position = selfEntity.getPosition();

if (position.x <= 1 || position.x >= 18 || position.y < 0 || position.y >= 6.5) {
System.out.println("DELETION POSITION: " + position);
selfEntity.setFlagForDelete(true);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,81 @@
package com.csse3200.game.components;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.csse3200.game.ai.tasks.AITaskComponent;
import com.csse3200.game.components.tasks.TrajectTask;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.entities.factories.ProjectileFactory;
import com.csse3200.game.physics.BodyUserData;
import com.csse3200.game.physics.PhysicsLayer;
import com.csse3200.game.physics.components.HitboxComponent;
import com.csse3200.game.physics.components.PhysicsComponent;
import com.csse3200.game.services.ServiceLocator;

/**
* Ricochet based on target layers.
* Basically a bouncing effect that continues to bounce off entities
* Possible extensions: Have a limitied amount of bounce until self-destruct.
* A bouncing effect that continues to bounce off desired entities.
*/
public class RicochetComponent extends Component {
private short targetLayer;
private HitboxComponent hitBoxComponent;
private int bounceCount;
private static int MAX_BOUNCE_Y_DIRECTION = 250;
private static int MIN_BOUNCE_Y_DIRECTION = -250;

public RicochetComponent(short targetLayer) {
/**
* Initialise a RicochetComponent that spawns another projectile upon collision.
* Projectile has a chance to head upwards or downwards and upon spawning,
* it will be slighlty up or down in respect to original disappearance.
* @param targetLayer Target layer upon collision
* @param bounceCount Keeps track of the bounce count upon initial collision
* Stops self-spawning when bounce count is greater or equal than two.
*/
public RicochetComponent(short targetLayer, int bounceCount) {
this.targetLayer = targetLayer;
this.bounceCount = bounceCount;
}

@Override
public void create() {
entity.getEvents().addListener("collisionEnd", this::onCollisionEnd);
hitBoxComponent = entity.getComponent(HitboxComponent.class);
}

/**
* After collision ends, make another fireball that spawns just before the original one. This assumes
* that the original fireball is already deleted. Set TouchAttackComponent disposeOnHit to true.
* After collision ends, make another fireball that spawns just before the
* original one. This assumes
* that the original fireball is already deleted. Set TouchAttackComponent
* disposeOnHit to true.
*
* @param me
* @param other
*/
private void onCollisionEnd(Fixture me, Fixture other) {
if(hitBoxComponent.getFixture() != me) return;

if(!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) return;

if (hitBoxComponent.getFixture() != me
|| !PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)
|| bounceCount >= 3) // BounceCount base case of 3
return;

Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity;

Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, projectile.getPosition().y + getRandomNumFrom(-250, 250)), new Vector2(2f, 2f));
newProjectile.setPosition((float) (projectile.getPosition().x -1.5), (float) (projectile.getPosition().y));
// Projectile heads upwards or downwards.
int randomDirection = getRandomNumFrom(MIN_BOUNCE_Y_DIRECTION, MAX_BOUNCE_Y_DIRECTION);

// Spawning of the projectile to be above (+ve) or below (-ve) upon
// collision
int up_or_down = randomDirection <= 0 ? -1 : 1;

float newXPosition = (float) (projectile.getPosition().x - 0.75);
float newYPosition = (float) (projectile.getPosition().y + (0.65 * up_or_down));

// Prevent spawn of new projectile if it goes out of boundaries.
if (newYPosition >= 8 || newYPosition <= 1 || newXPosition >= 17 || newXPosition <= 1)
return;

// * RIGHT NOW TARGET IS NPC, SUBJECT TO CHANGE
Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC,
new Vector2(100, projectile.getPosition().y + randomDirection), new Vector2(2f, 2f), ++bounceCount); // Increment bounceCount

newProjectile.setPosition(newXPosition, newYPosition);
newProjectile.setScale(0.75f, 0.75f);

// projectile.getComponent(AITaskComponent.class).addTask(new TrajectTask(new Vector2(100, projectile.getPosition().y + 50)));


ServiceLocator.getEntityService().register(newProjectile);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.csse3200.game.components;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.entities.factories.ProjectileFactory;
import com.csse3200.game.physics.BodyUserData;
import com.csse3200.game.physics.PhysicsLayer;
import com.csse3200.game.physics.components.HitboxComponent;
import com.csse3200.game.services.ServiceLocator;

/**
* A component that splits the projectile into multiple mini projectiles.
* Assumes projectile has a disposesOnHit functionality.
*/
public class SplitFireworksComponent extends Component {
private short targetLayer;
private HitboxComponent hitboxComponent;
private int amount;
private static int TOTAL_RANGE = 450;

/**
* Initialises a component that splits the projectile into multiple fireballs
* upon collision on a specified target layer.
* The spawned projectiles will be spawned just before original projectile
* and spread out in multiple direction set by a certain range.
* Assumes amount of split projectiles is greater or equal than 2.
*
* @param targetLayer Target layer upon collision.
* @param amount Amount of projectiles that is split after collision event.
*/
public SplitFireworksComponent(short targetLayer, int amount) {
this.targetLayer = targetLayer;
this.amount = amount;
}

@Override
public void create() {
entity.getEvents().addListener("collisionEnd", this::onCollisionEnd);
hitboxComponent = entity.getComponent(HitboxComponent.class);
}

private void onCollisionEnd(Fixture me, Fixture other) {
if (hitboxComponent.getFixture() != me
|| !PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)
|| amount < 2) // Amount of split projectiles must be >= 2
return;

Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity;

for (int i = 0; i < amount; i++) {
int newDirection = (i * TOTAL_RANGE) / (amount - 1);

// Boundaries
float newXPosition = (float) (projectile.getPosition().x + 1.75);
if (newXPosition >= 18 || newXPosition <= 1)
return;

// * RIGHT NOW TARGET IS NPC, SUBJECT TO CHANGE
// Speed is a bit faster than normal but can change.
Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC,
new Vector2(100, projectile.getPosition().y + (newDirection - (TOTAL_RANGE/2))), new Vector2(3f, 3f));

newProjectile.setPosition(newXPosition, (float) projectile.getPosition().y);

newProjectile.setScale(0.5f, 0.5f);

ServiceLocator.getEntityService().register(newProjectile);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

/**
* When this entity touches a valid enemy's hitbox, deal damage to them and apply a knockback.
* Has an optional disposeOnHit property that disposes projectile upon collision.
*
* <p>Requires CombatStatsComponent, HitboxComponent on this entity.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import com.csse3200.game.ai.tasks.DefaultTask;
import com.csse3200.game.ai.tasks.PriorityTask;
import com.csse3200.game.ai.tasks.Task;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.entities.factories.ProjectileFactory;
import com.csse3200.game.services.ServiceLocator;
import com.csse3200.game.physics.PhysicsLayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -15,7 +19,7 @@ public class RangeBossMovementTask extends DefaultTask implements PriorityTask {
private static final Logger logger = LoggerFactory.getLogger(RangeBossMovementTask.class);

private final float waitTime;
private Vector2 startPos;
private Vector2 currentPos;
private MovementTask movementTask;
private WaitTask waitTask;
private Task currentTask;
Expand All @@ -36,11 +40,11 @@ public int getPriority() {
@Override
public void start() {
super.start();
startPos = owner.getEntity().getPosition();
currentPos = owner.getEntity().getPosition();

waitTask = new WaitTask(waitTime);
waitTask.create(owner);
movementTask = new MovementTask(startPos.sub(2,0));
movementTask = new MovementTask(currentPos.sub(2,0));
movementTask.create(owner);

movementTask.start();
Expand All @@ -54,6 +58,11 @@ public void start() {
public void update() {
if (currentTask.getStatus() != Status.ACTIVE) {
if (currentTask == movementTask) {
Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.OBSTACLE, new Vector2(0, currentPos.y + 0.75f), new Vector2(2f,2f));

newProjectile.scaleHeight(-0.4f);
newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f));
ServiceLocator.getEntityService().register(newProjectile);
startWaiting();
} else {
startMoving();
Expand All @@ -69,7 +78,7 @@ private void startWaiting() {

private void startMoving() {
logger.debug("Starting moving");
movementTask.setTarget(startPos.sub(2,0));
movementTask.setTarget(currentPos.sub(2,0));
swapTask(movementTask);
}

Expand All @@ -81,4 +90,4 @@ private void swapTask(Task newTask) {
currentTask.start();
}

}
}
Loading

0 comments on commit 114591b

Please sign in to comment.