Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
229 changes: 229 additions & 0 deletions GW_v1.06/src/entities/enemy/EnemySnake.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package entities.enemy;

import static org.lwjgl.opengl.GL11.*;

import java.awt.Rectangle;
import java.util.ArrayList;

import entities.Entity;
import helpers.Texture;
import helpers.TextureHelper;

/**
* EnemySnake
* Head is the only damageable part.
* Body follows the head trail with a sinusoidal oscillation.
*/
public class EnemySnake extends EnemyEntity {

private Texture headTex;
private Texture bodyTex;

private static final int BODY_SEGMENTS = 15;
private static final double SEGMENT_DISTANCE = 14.0;
private static final double BODY_OSC_AMPLITUDE = 8.0;
private static final double HEAD_WIGGLE_ANGLE = 0.00; // radians
private static final int MAX_TRAIL_POINTS = 320;

// If the head sprite faces the wrong direction, 90.0 or 180.0
private static final double ROTATION_OFFSET = 270.0;

private double time = 0.0;

private ArrayList<SnakeTrailPoint> trail = new ArrayList<SnakeTrailPoint>();

public EnemySnake(double x, double y, double width, double height) {
super(x, y, width, height);

texHelper = new TextureHelper();

headTex = texHelper.loadTexture("res/EnemySnake_Head.png", "PNG");
bodyTex = texHelper.loadTexture("res/EnemySnake_Body.png", "PNG");

super.enemyTex = headTex;
super.setEnemyID("EnemySnake");

// Current Game collision logic kills when health <= 0 before the hit.
// health = 2 means roughly 3 bullet impacts.
setHealth(1);

// Slightly slower than basic follower, because the body makes it more threatening.
setXVelocity(0.14);
setYVelocity(0.14);

for (int i = 0; i < MAX_TRAIL_POINTS; i++) {
trail.add(new SnakeTrailPoint(x, y, 0.0));
}
}

@Override
public void update(int delta, double player_x, double player_y) {
time += delta * 0.015;

double distance_x = player_x - this.x;
double distance_y = player_y - this.y;

double baseAngle = Math.atan2(distance_y, distance_x);

// Small detour / wiggle around the direct follower direction.
double wiggle = Math.sin(time) * HEAD_WIGGLE_ANGLE;
double moveAngle = baseAngle + wiggle;

setEnemyAngle(Math.toDegrees(moveAngle) + ROTATION_OFFSET);

comp_x = vel_x * Math.cos(-moveAngle);
this.x += delta * comp_x;

comp_y = vel_y * Math.sin(-moveAngle);
this.y -= delta * comp_y;

addTrailPoint(this.x, this.y, moveAngle);
}

private void addTrailPoint(double x, double y, double angle) {
trail.add(0, new SnakeTrailPoint(x, y, angle));

while (trail.size() > MAX_TRAIL_POINTS) {
trail.remove(trail.size() - 1);
}
}

@Override
public void draw() {
glColor3f(1.0f, 1.0f, 1.0f);
glEnable(GL_BLEND);

// Draw tail first, then head on top.
for (int i = BODY_SEGMENTS; i >= 1; i--) {
SnakeTrailPoint point = getPointAtDistance(i * SEGMENT_DISTANCE);

double dirX = Math.cos(-point.angle);
double dirY = -Math.sin(-point.angle);

double perpX = -dirY;
double perpY = dirX;

double osc = Math.sin(time - i * 0.75) * BODY_OSC_AMPLITUDE;

float bodyX = (float)(point.x + perpX * osc);
float bodyY = (float)(point.y + perpY * osc);

float bodyRot = (float)(Math.toDegrees(point.angle) + ROTATION_OFFSET);

drawQuadTex(
bodyX,
bodyY,
(float)(width * 0.82),
(float)(height * 0.82),
bodyRot,
bodyTex
);
}

drawQuadTex(
(float)x,
(float)y,
(float)width,
(float)height,
(float)super.enemy_angle,
headTex
);
}

private SnakeTrailPoint getPointAtDistance(double targetDistance) {
if (trail.isEmpty()) {
return new SnakeTrailPoint(x, y, 0.0);
}

double travelled = 0.0;

for (int i = 0; i < trail.size() - 1; i++) {
SnakeTrailPoint a = trail.get(i);
SnakeTrailPoint b = trail.get(i + 1);

double dx = b.x - a.x;
double dy = b.y - a.y;
double segmentLength = Math.sqrt(dx * dx + dy * dy);

if (segmentLength <= 0.0001) {
continue;
}

if (travelled + segmentLength >= targetDistance) {
double t = (targetDistance - travelled) / segmentLength;

double px = a.x + dx * t;
double py = a.y + dy * t;

return new SnakeTrailPoint(px, py, a.angle);
}

travelled += segmentLength;
}

return trail.get(trail.size() - 1);
}

public boolean intersectsHead(Entity other) {
Rectangle headRect = new Rectangle(
(int)(x - width / 2.0),
(int)(y - height / 2.0),
(int)width,
(int)height
);

Rectangle otherRect = new Rectangle(
(int)(other.getX() - other.getWidth() / 2.0),
(int)(other.getY() - other.getHeight() / 2.0),
(int)other.getWidth(),
(int)other.getHeight()
);

return headRect.intersects(otherRect);
}

@Override
public void drawQuadTex(float x, float y, float width, float height, float rot, Texture texture) {
texture.bind();

glLoadIdentity();

glPushMatrix();
{
width /= 2;
height /= 2;

glTranslatef(x, y, 0);
glRotatef(rot, 0, 0, 1);

glBegin(GL_QUADS);
{
glTexCoord2f(0, 0);
glVertex2f(-width, -height);

glTexCoord2f(1, 0);
glVertex2f(-width, height);

glTexCoord2f(1, 1);
glVertex2f(width, height);

glTexCoord2f(0, 1);
glVertex2f(width, -height);
}
glEnd();
}
glPopMatrix();
}

private static class SnakeTrailPoint {
double x;
double y;
double angle;

SnakeTrailPoint(double x, double y, double angle) {
this.x = x;
this.y = y;
this.angle = angle;
}
}
}
104 changes: 85 additions & 19 deletions GW_v1.06/src/game/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
import entities.Background;
import entities.Bullet;
import entities.Player;
import entities.enemy.EnemyA;
import entities.enemy.EnemyB;
import entities.enemy.EnemyC;
import entities.enemy.EnemyD;
import entities.enemy.EnemyEntity;
import entities.enemy.*;
import entities.particle_system.BlackholeSystem;
import entities.particle_system.ParticleSystem_Death;
import helpers.FontRenderer;
Expand Down Expand Up @@ -297,7 +293,7 @@ public void checkCollision() throws IndexOutOfBoundsException {
}

// collide bullet with enemy -- for now ...
for (int i = 0; i < weaponManager.bulletArray.size(); i++) {
/* for (int i = 0; i < weaponManager.bulletArray.size(); i++) {
for (int j = 0; j < enemyArray.size(); j++) {
if (weaponManager.bulletArray.get(i).intersects(enemyArray.get(j)) && enemyArray.get(j).getHealth() <= 0) {
weaponManager.bulletArray.remove(i);
Expand All @@ -312,6 +308,30 @@ public void checkCollision() throws IndexOutOfBoundsException {
//System.out.println("ENEMY " + j + " HAS " + enemyArray.get(j).getHealth());
enemyArray.get(j).setHealth( enemyArray.get(j).getHealth() - 1 );
weaponManager.bulletArray.remove(i);
break;
}
}
}*/

for (int i = 0; i < weaponManager.bulletArray.size(); i++) {
Bullet bullet = weaponManager.bulletArray.get(i);

for (int j = 0; j < enemyArray.size(); j++) {
EnemyEntity enemy = enemyArray.get(j);

if (bulletHitsEnemy(bullet, enemy)) {
weaponManager.bulletArray.remove(i);

if (enemy.getHealth() <= 0) {
addPlayerScore(enemy);

spawnDeathParticles(enemyArray.get(j).getX(), enemyArray.get(j).getY());

enemyArray.remove(j);
} else {
enemy.setHealth(enemy.getHealth() - 1);
}

break;
}
}
Expand Down Expand Up @@ -511,6 +531,9 @@ public void updateEnemy(int delta) {
case "EnemyD":
enemy.update(delta, player.getX(), player.getY());
break;
case "EnemySnake":
enemy.update(delta, player.getX(), player.getY());
break;
default:
break;
}
Expand All @@ -531,8 +554,11 @@ public void generateWave() {
}
if (waveCounter%4==0 && waveCounter!=0) {
blackholeArrayList.add(createRandomBlackholeInsideGrid());
}
canSpawnWave = false;
}
if (waveCounter%8==0 && waveCounter!=0) {
spawnSnakeCornerWave();
}
canSpawnWave = false;
}
if (randSkip == 0) {
iterativeWave();
Expand All @@ -544,17 +570,23 @@ public void generateWave() {

public void iterativeWave() {
iterativeWaveCounter++;
if (iterativeWaveCounter%20 == 0) {
for (int i = 0; i < MAX_ENEMY; i++) {
max_x = (int)spawnArray03.get(i).getX() + offset;
min_x = (int)spawnArray03.get(i).getX() - offset;
int randXPos = rand.nextInt(max_x-min_x) + min_x;
max_y = (int)spawnArray03.get(i).getY() + offset;
min_y = (int)spawnArray03.get(i).getY() - offset;
int randYPos = rand.nextInt(max_y-min_y) + min_y;
enemyArray.add(new EnemyD(randXPos, randYPos, enemy_size, enemy_size));
}

if (iterativeWaveCounter % 20 == 0) {
for (int i = 0; i < MAX_ENEMY; i++) {
max_x = (int)spawnArray03.get(i).getX() + offset;
min_x = (int)spawnArray03.get(i).getX() - offset;

int randXPos = rand.nextInt(max_x - min_x) + min_x;

max_y = (int)spawnArray03.get(i).getY() + offset;
min_y = (int)spawnArray03.get(i).getY() - offset;

int randYPos = rand.nextInt(max_y - min_y) + min_y;

enemyArray.add(new EnemyD(randXPos, randYPos, enemy_size, enemy_size));
}
}

// spawn 15 enemy each corner (300/20=15)
// 4 corner = 60 enemy on screen
if (iterativeWaveCounter >= 200) {
Expand Down Expand Up @@ -601,14 +633,17 @@ public void addPlayerScore(EnemyEntity enemy) {
case "EnemyD":
player.setScore(player.getScore()+50);
break;
case "EnemySnake":
player.setScore(player.getScore()+150);
break;
default:
break;
}
}

// --- check for weapon upgrade
public void checkUpgrade() {
if (player.getScore() > 4000) {
if (player.getScore() > 1800) {
weaponManager.weaponIndex = 2;
}
}
Expand Down Expand Up @@ -803,4 +838,35 @@ private void startGame() {

canSpawnWave = true;
}

// Collision head only EnemySnake and normal for other
private boolean bulletHitsEnemy(Bullet bullet, EnemyEntity enemy) {
if (enemy instanceof EnemySnake) {
return ((EnemySnake) enemy).intersectsHead(bullet);
}

return bullet.intersects(enemy);
}

private void spawnSnakeCornerWave() {
System.out.println("spawnSnakeCornerWave");

for (int i = 0; i < MAX_ENEMY; i++) {
int snakeCount = 1 + randomIntInclusive(0, 1);

for (int s = 0; s < snakeCount; s++) {
max_x = (int)spawnArray03.get(i).getX() + offset;
min_x = (int)spawnArray03.get(i).getX() - offset;

int randXPos = rand.nextInt(max_x - min_x) + min_x;

max_y = (int)spawnArray03.get(i).getY() + offset;
min_y = (int)spawnArray03.get(i).getY() - offset;

int randYPos = rand.nextInt(max_y - min_y) + min_y;

enemyArray.add(new EnemySnake(randXPos, randYPos, enemy_size, enemy_size));
}
}
}
}