Skip to content

Commit

Permalink
Merge pull request #97 from UQcsse3200/Team-2--Alasdair-Branch
Browse files Browse the repository at this point in the history
Team 2  alasdair branch
  • Loading branch information
The-AhmadAA authored Sep 6, 2023
2 parents ab2f9f8 + 23d4e6e commit 4169819
Show file tree
Hide file tree
Showing 23 changed files with 706 additions and 118 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/gradle_release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
GRADLE_DIR: 'source' # Modify this to wherever './gradlew' is

- name: Automatic Release # note the path for the files on this one
uses: marvinpinto/action-automatic-releases@v1.1.1
uses: marvinpinto/action-automatic-releases@v1.2.1
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
Expand All @@ -58,4 +58,4 @@ jobs:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# GRADLE_DIR: 'source' # Modify this to wherever './gradlew' is
# run: ./gradlew sonarqube --info
# run: ./gradlew sonarqube --info
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# UQ 2023 Studio 3
# UQ 2023 Studio 3

## Description

Expand All @@ -12,6 +12,7 @@ You are welcome to use the game engine for your own purposes. It is released und
- [SonarCloud](https://sonarcloud.io/project/overview?id=UQcsse3200_2023-studio-3)


## Team 2 - Tower Branch
## Outworld Outposts - A tower defense game.

Implements defense towers in the Outworld Outposts game. Any questions or discussion, please contact Team 2
This is the main branch for Outworld Outposts, a game developed by Studio 3 of CSSE3200 (Semester 2 2023).
A game developed in the CSSE3200 adaptation of the LibGDX game engine. For details and documentation, refer to the [Wiki](https://github.com/UQcsse3200/2023-studio-3/wiki)
7 changes: 6 additions & 1 deletion source/core/assets/configs/NPCs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"health": 40,
"baseAttack": 10
},
"projectile": {
"ghostKing": {
"health": 100,
"baseAttack": 25,
"spookyFactor": 7
},
"fireBall": {
"health": 100,
"baseAttack": 10
}
Expand Down
128 changes: 97 additions & 31 deletions source/core/src/main/com/csse3200/game/areas/ForestGameArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public class ForestGameArea extends GameArea {
private int bossSpawnInterval = 10000; // 1 minute in milliseconds

private static final int NUM_WEAPON_TOWERS = 3;
private static final GridPoint2 PLAYER_SPAWN = new GridPoint2(10, 10);
private static final GridPoint2 PLAYER_SPAWN = new GridPoint2(0, 15);
// Temporary spawn point for testing
private static final float WALL_WIDTH = 0.1f;

private static final GridPoint2 BOSS_SPAWN = new GridPoint2(5, 5);
Expand Down Expand Up @@ -99,11 +100,15 @@ public class ForestGameArea extends GameArea {
"sounds/stow.mp3"
};
private static final String backgroundMusic = "sounds/Sci-Fi1.ogg";

private static final String[] forestMusic = {backgroundMusic};

private final TerrainFactory terrainFactory;

private Entity player;

// Variables to be used with spawn projectile methods. This is the variable
// that should occupy the direction param.
private static final int towardsMobs = 100;
private Entity bossKing1;
private Entity bossKing2;

Expand Down Expand Up @@ -131,6 +136,12 @@ public void create() {
spawnMountains();
player = spawnPlayer();

playMusic();

// Types of projectile
spawnAoeProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f), 1);
spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f));
spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7);
spawnXenoGrunts();

spawnGhosts();
Expand Down Expand Up @@ -221,6 +232,13 @@ private Entity spawnPlayer() {
return newPlayer;
}

// Spawn player at a specific position
private Entity spawnPlayer(GridPoint2 position) {
Entity newPlayer = PlayerFactory.createPlayer();
spawnEntityAt(newPlayer, position, true, true);
return newPlayer;
}

private void spawnGhosts() {
GridPoint2 minPos = new GridPoint2(0, 0);
GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 2);
Expand All @@ -237,21 +255,59 @@ private void spawnGhosts() {
private Entity spawnBossKing1() {
GridPoint2 minPos = new GridPoint2(0, 0);
GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2);
GridPoint2 randomPos
= new GridPoint2(0, 0);
Entity ghostKing = NPCFactory.createGhostKing(player);
spawnEntityAt(ghostKing, randomPos, true, true);
return ghostKing;
}

for (int i = 0; i < NUM_BOSS; i++) {
int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate
int randomY = MathUtils.random(0, maxPos.y);
GridPoint2 randomPos = new GridPoint2(fixedX, randomY);
bossKing1 = BossKingFactory.createBossKing1(player);
spawnEntityAt(bossKing1,
randomPos,
true,
false);
}
return bossKing1;
/**
* Spawns a projectile that only heads towards the enemies in its lane.
*
* @param position The position of the Entity that's shooting the projectile.
* @param target The enemy entities of the "shooter".
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
*
*/
private void spawnProjectile(Vector2 position, Entity target, int direction, Vector2 speed) {
Entity Projectile = ProjectileFactory.createFireBall(target, new Vector2(direction, position.y), speed);
Projectile.setPosition(position);
spawnEntity(Projectile);
}

/**
* Spawns a projectile to be used for multiple projectile function.
*
* @param position The position of the Entity that's shooting the projectile.
* @param target The enemy entities of the "shooter".
* @param space The space between the projectiles' destination.
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
*
*/
private void spawnProjectile(Vector2 position, Entity target, int space, int direction, Vector2 speed) {
Entity Projectile = ProjectileFactory.createFireBall(target, new Vector2(direction, position.y + space), speed);
Projectile.setPosition(position);
spawnEntity(Projectile);
}

// private Entity spawnBossKing() {
// for (int i = 0; i < NUM_BOSS; i++) {
// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate
// int randomY = MathUtils.random(0, maxPos.y);
// GridPoint2 randomPos = new GridPoint2(fixedX, randomY);
// bossKing1 = BossKingFactory.createBossKing1(player);
// spawnEntityAt(bossKing1,
// randomPos,
// true,
// false);
// }
// return bossKing1;

// }

private void spawnXenoGrunts() {
GridPoint2 minPos = terrain.getMapBounds(0).sub(1, 5);
GridPoint2 maxPos = terrain.getMapBounds(0).sub(1, 25);
Expand Down Expand Up @@ -293,27 +349,37 @@ private Entity spawnBossKing2() {
}

/**
* Spawns a projectile currently just in the center of the game
*
* @return a new projectile
* Creates multiple projectiles that travel simultaneous. They all have same
* the starting point but different destinations.
*
* @param position The position of the Entity that's shooting the projectile.
* @param target The enemy entities of the "shooter".
* @param direction The direction the projectile should head towards.
* @param space The space between the projectiles' destination.
* @param speed The speed of the projectiles.
* @param quantity The amount of projectiles to spawn.
*/
private void spawnProjectile(Vector2 speed) {
Entity newProjectile = ProjectileFactory.createProjectile(bossKing1, player, new Vector2(100, bossKing1.getPosition().x), speed);
newProjectile.setPosition(bossKing1.getPosition());
spawnEntity(newProjectile);
private void spawnMultiProjectile(Vector2 position, Entity target, int direction, int space, Vector2 speed, int quantity) {
int half = quantity / 2;
for (int i = 0; i < quantity; i++) {
spawnProjectile(position, target, space * half, direction, speed);
--half;
}
}

private void spawnMultiProjectile(Vector2 speed) {
Entity newTopProjectile = ProjectileFactory.createProjectile(bossKing1, player, new Vector2(100, player.getPosition().x + 30), speed);
newTopProjectile.setPosition(player.getPosition());
Entity newMiddleProjectile = ProjectileFactory.createProjectile(bossKing1, player, new Vector2(100, player.getPosition().x), speed);
newMiddleProjectile.setPosition(player.getPosition());
Entity newBottomProjectile = ProjectileFactory.createProjectile(bossKing1, player, new Vector2(100, player.getPosition().x - 30), speed);
newBottomProjectile.setPosition(player.getPosition());

spawnEntity(newTopProjectile);
spawnEntity(newMiddleProjectile);
spawnEntity(newBottomProjectile);
/**
* Returns projectile that can do an area of effect damage
*
* @param position The position of the Entity that's shooting the projectile.
* @param target The enemy entities of the "shooter".
* @param direction The direction the projectile should head towards.
* @param speed The speed of the projectiles.
* @param aoeSize The size of the area of effect.
*/
private void spawnAoeProjectile(Vector2 position, Entity target, int direction, Vector2 speed, int aoeSize) {
Entity Projectile = ProjectileFactory.createAOEFireBall(target, new Vector2(direction, position.y), speed, aoeSize);
Projectile.setPosition(position);
spawnEntity(Projectile);
}

private void spawnWeaponTower() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.csse3200.game.components;

import com.badlogic.gdx.physics.box2d.Fixture;
import com.csse3200.game.entities.Entity;
import com.csse3200.game.physics.BodyUserData;
import com.csse3200.game.physics.components.HitboxComponent;
import com.csse3200.game.services.ServiceLocator;

import com.badlogic.gdx.utils.Array;

public class AoeComponent extends Component {
private final float radius;
private HitboxComponent hitboxComponent;

/**
* Constructor for the AoEComponent.
*
* @param radius The radius of the area-of-effect.
*/
public AoeComponent(float radius) {
this.radius = radius;
}

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

private void onCollisionStart(Fixture me, Fixture other) {
// Nothing to do on collision start
}

private void onCollisionEnd(Fixture me, Fixture other) {
if (hitboxComponent.getFixture() != me) {
// Not triggered by hitbox, ignore
return;
}
applyAoeDamage();
}
/**
* Apply damage to all entities within the area of effect (radius).
*/
public void applyAoeDamage() {
Entity hostEntity = getEntity();
CombatStatsComponent hostCombatStats = hostEntity.getComponent(CombatStatsComponent.class);

if (hostCombatStats == null) {
// The host entity does not have a CombatStatsComponent to deal damage
return;
}

Array<Entity> nearbyEntities = ServiceLocator.getEntityService().getNearbyEntities(hostEntity, radius);

for (Entity targetEntity : nearbyEntities) {
CombatStatsComponent targetCombatStats = targetEntity.getComponent(CombatStatsComponent.class);
if (targetCombatStats != null) {
targetCombatStats.hit(hostCombatStats);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class CombatStatsComponent extends Component {
private static final Logger logger = LoggerFactory.getLogger(CombatStatsComponent.class);
private int health;
private int baseAttack;
private final int fullHealth;
private int fullHealth;
private String state;
private ArrayList<Integer> drops;
private ArrayList<Melee> closeRangeAbilities;
Expand Down Expand Up @@ -99,6 +99,25 @@ public void addHealth(int health) {
changeState();
}

/**
* Returns the entity's fullHealth value (note that this does not influence the ability to set its actual health)
*
* @return The entity's fullHealth variable
*/
public int getMaxHealth() {
return fullHealth;
}

/**
* Sets the entity's fullHealth variable.
* Intended for when the entity's maximum health must be changed after creation, like upgrading a turret's HP.
*
* @param newMaxHealth The new value fullHealth should be set to
*/
public void setMaxHealth(int newMaxHealth) {
fullHealth = newMaxHealth;
}

/**
* Returns the entity's base attack damage.
*
Expand Down Expand Up @@ -130,6 +149,12 @@ public void hit(Integer damage) {
changeState();
}

// Default CombatStatsComponent that relies on the attacker's combatStatsComponent.
public void hit(CombatStatsComponent attacker) {
int newHealth = getHealth() - attacker.getBaseAttack();
setHealth(newHealth);
}

/**
* pick a random number from range 0 to the size of the list provided
* */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
public class TouchAttackComponent extends Component {
private short targetLayer;
private float knockbackForce = 0f;
private boolean disposeOnHit = false;
private int aoeSize = 0;
private CombatStatsComponent combatStats;
private HitboxComponent hitboxComponent;

Expand All @@ -41,9 +43,22 @@ public TouchAttackComponent(short targetLayer, float knockback) {
this.knockbackForce = knockback;
}

/**
* Create a component which attacks entities on collision, with knockback and self-dispose.
* @param targetLayer The physics layer of the target's collider.
* @param knockback The magnitude of the knockback applied to the entity.
* @param disposeOnHit Whether this entity should be disposed on hit.
*/
public TouchAttackComponent(short targetLayer, float knockback, boolean disposeOnHit) {
this.targetLayer = targetLayer;
this.knockbackForce = knockback;
this.disposeOnHit = disposeOnHit;
}

@Override
public void create() {
entity.getEvents().addListener("collisionStart", this::onCollisionStart);
entity.getEvents().addListener("collisionEnd", this::onCollisionEnd);
combatStats = entity.getComponent(CombatStatsComponent.class);
hitboxComponent = entity.getComponent(HitboxComponent.class);
}
Expand All @@ -70,7 +85,6 @@ private void onCollisionStart(Fixture me, Fixture other) {
targetStats.hit(combatStats.getBaseAttack());
}
}

// Apply knockback
PhysicsComponent physicsComponent = target.getComponent(PhysicsComponent.class);
if (physicsComponent != null && knockbackForce > 0f) {
Expand All @@ -79,5 +93,15 @@ private void onCollisionStart(Fixture me, Fixture other) {
Vector2 impulse = direction.setLength(knockbackForce);
targetBody.applyLinearImpulse(impulse, targetBody.getWorldCenter(), true);
}

if (disposeOnHit) {
Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity;
projectile.setFlagForDelete(true);
}
}

private void onCollisionEnd(Fixture me, Fixture other) {
// Nothing to do on collision end
}
}

Loading

0 comments on commit 4169819

Please sign in to comment.