A modern take on the SNES' FaceBall 2000 title using the PGE.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
Faceball2030/Faceball2030/main.h

437 lines
10 KiB

#pragma once
#include "pixelGameEngine.h"
#include "Editor.h"
#include "geometry2d.h"
#include <optional>
using namespace olc;
const float PI = 3.14159f;
enum GAMEMODE {
GAME,
EDITOR,
LEVELCOMPLETE
};
enum Direction {
NORTH = 1,
EAST = 2,
SOUTH = 4,
WEST = 8
};
struct vec2d
{
float u = 0;
float v = 0;
float w = 1;
};
struct vec3d
{
float x = 0;
float y = 0;
float z = 0;
float w = 1;
friend std::ostream& operator << (std::ostream& os, const vec3d& rhs) { os << rhs.x << "," << rhs.y << "," << rhs.z; return os; }
};
struct Player {
private:
vec3d pos;
olc::utils::geom2d::circle<float> playerR;
public:
Player(vec3d pos,olc::utils::geom2d::circle<float>r):pos(pos), playerR(r) {}
void UpdatePos(vec3d pos) {
this->pos = pos;
playerR.pos = { pos.x,pos.z };
}
float GetRadius() {
return playerR.radius;
}
vec3d&GetPos() {
return pos;
}
};
struct Triangle
{
vec3d p[3];
vec2d uv[3];
Pixel col[3];
Decal* tex;
};
struct MapSquare {
Decal* wallN = NULL;
Decal* wallE = NULL;
Decal* wallS = NULL;
Decal* wallW = NULL;
};
struct Mesh
{
std::vector<Triangle> tris;
Decal* texture = NULL;
Mesh() {}
Mesh(std::string filename, Decal* tex)
:texture(tex) {
LoadFromObjectFile(filename,tex);
}
private:
void Parse(std::string str, int& v, int& uv) {
//std::cout << str << "\n";
std::stringstream s(str.substr(0, str.find("/") + 1));
s >> v;
str.erase(0, str.find("/") + 1);
std::stringstream s2(str.substr(0, str.find("/") + 1));
s2 >> uv;
//std::cout<<" "<<v<<"/"<<uv<<"\n";
}
bool LoadFromObjectFile(std::string sFilename,Decal*tex)
{
std::ifstream f(sFilename);
if (!f.is_open())
return false;
// Local cache of verts
std::vector<vec3d> verts;
std::vector<vec2d> uvs;
std::string data;
while (f.good()) {
f >> data;
if (data == "v") {
float x, y, z;
f >> x >> y >> z;
verts.push_back({ x,y,z });
//std::cout << x << " " << y << " " << z << "\n";
}
else
if (data == "vt") {
float u, v;
f >> u >> v;
uvs.push_back({ u,1 - v });
//std::cout << u << " " << v << "\n";
}
else
if (data == "f") {
//std::cout<<"face\n";
std::string t1, t2, t3;
f >> t1 >> t2 >> t3;
int v1, v2, v3, uv1, uv2, uv3;
Parse(t1, v1, uv1);
Parse(t2, v2, uv2);
Parse(t3, v3, uv3);
tris.push_back({ verts[v1 - 1],verts[v2 - 1],verts[v3 - 1],
uvs[uv1 - 1],uvs[uv2 - 1],uvs[uv3 - 1],{WHITE,WHITE,WHITE},tex });
}
}
return true;
}
};
struct Object {
Mesh mesh;
vec3d pos = { 0,0 };
float rot = 0;
float radius = 0.2f;
};
struct Bullet : Object{
vf2d spd = { 0,0 };
Pixel col = GREEN;
bool friendly=true;
EnemyID shooterID;
int shooterIndex = -1;
bool shooterBlinking = false;
bool Update(float fElapsedTime);
};
struct Powerup : Object {
PowerupType type;
Powerup(Mesh mesh, vec3d pos, float rot, PowerupType type);
Pixel col;
float colorCycle=1;
float aliveTime=0;
bool opened = false;
bool Update(float fElapsedTime);
};
struct PowerupData {
std::string name;
Pixel col;
};
struct EnemyData {
std::string name;
Mesh mesh;
Pixel col=YELLOW;
int health = 1;
float movSpd = 1;
float rotSpd = PI / 8;
int ammo = 2;
float fireDelay = 1;
float radius = 0.2f;
bool explosive = false; //Explodes on contact.
PowerupType powerupDrop=PowerupType::NONE;
bool blinking = false;
};
struct mat4x4
{
float m[4][4] = { 0 };
};
enum class Phase {
DEFAULT,
TURNING,
};
struct Enemy : public Object {
private:
EnemyID id;
int health=1;
std::vector<float> shots;
float fireDelay=0;
float deathTimer=0;
bool exploded = false; //If contacted and explosive, then explosion flag is set instead.
float colorFactor = 0;
Phase phase=Phase::DEFAULT;
float lastHitTime = 0;
bool blinking = false;
float aliveTime = 0;
public:
float turnAmt = 0;
bool flippedTriangles = false;
bool finishedAnimation = false;
float blinkingAmt = 1; //>0.5 the enemy is visible.
Enemy(EnemyID id, vec3d pos, float rot, float radius);
EnemyID GetID();
//Can set the damage to 0 to cause just a visual hit.
void Hurt(int damage=1);
bool isDead();
void increaseDeathTimer(float fElapsedTime);
bool deathAnimationOver();
void increaseColorFactor(float fElapsedTime);
float getColorFactor();
void decreaseColorFactor();
Phase GetPhase();
void SetPhase(Phase phase);
bool CanShoot();
void ShootBullet(int myIndex);
void ReloadBullet(float fElapsedTime);
bool isLastHitTimerActive();
void reduceLastHitTimer(float fElapsedTime);
bool isExplosive();
void setExploded(bool exploded);
bool isBlinking();
bool isExploded();
bool Update(float fElapsedTime);
void OnDeathEvent();
vf2d GetPlayerPosition();
Enemy freshCopy(bool randomizeLoc=false);
};
class FaceBall : public PixelGameEngine
{
bool freeRoam = false;
public:
FaceBall()
{
sAppName = "3D Demo";
}
EnemyData GetData(EnemyID id);
Decal* circle,*arrow,*YAZAWA;
std::map<EnemyID, EnemyData>enemyData;
std::map<PowerupType, PowerupData>powerupData;
std::vector<Enemy>enemies;
std::vector<Enemy>wave2Enemies;
std::vector<Enemy>wave3Enemies;
std::vector<Powerup>powerups;
private:
Mesh mapWalls,mapFloor,enemy_ShootMe,undefined,
enemy_Sonar, mapExit,enemy_ShootMe2,powerup,powerup2,
enemy_IShoot;
Sprite*mapSpr;
Decal*mapDecal;
Decal* dot, * enemy_ShootMe_tex,*bullet_tex,*wall_tex,*floor_tex,
*enemy_Sonar_tex,*hud,*exit_wall_tex,*enemy_ShootMe2_tex,*enemy_IShoot_tex,
*life4,*life3,*life2,*life1,*crosshair,*hudmeter,*powerup_tex,*powerup2_tex,
*powerups_tex;
vi2d exitCoords = { 0,0 };
std::vector<vf2d>knownGoodPositions;
std::vector<std::vector<MapSquare>>map;
std::vector<Object>objects;
GAMEMODE mode=GAMEMODE::GAME;
Editor editor;
int MapWallsObjectIndex = -1;
bool exitWallsCleared = false;
int level=1;
int tagsRemaining = 10;
int lives = 3;
double gameTimer = 0;
int lastPowerupCollidedWith = -1;
std::string hudDisplayText = "";
float stopDuration = 0,shieldDuration=0,camoDuration=0;
bool hasMapUpgrade = false;
std::array<Pixel,14>colorCycle={
RED,
BLACK,
YELLOW,
WHITE,
GREEN,
WHITE,
CYAN,
BLACK,
MAGENTA,
BLACK,
GREY,
WHITE,
BLUE,
BLACK
};
int topTargetColInd,botTargetColInd;
Pixel topTargetCol,botTargetCol;
Pixel topCurrentCol,botCurrentCol;
Pixel topCol,botCol;
float colTransferAmt=0;
mat4x4 matProj;
vec3d vLookDir;
float zOffset = 2;
float fTheta = 0;
float fYaw = 0;
float pitch = -PI / 6;
std::array<vf2d,4> meter_ARMOR = {
vf2d{1039,218},
vf2d{1040,260},
vf2d{1166,244},
vf2d{1164,195}
},
meter_SPEED = {
vf2d{1038,360},
vf2d{1038,405},
vf2d{1167,408},
vf2d{1167,360}
},
meter_SHOTS = {
vf2d{1035,512},
vf2d{1032,558},
vf2d{1162,575},
vf2d{1163,525}
};
Player player = { {3.7,0.3,0.7}, {{0.5,0.5},0.2} };
const int baseHP = 3;
int hp = baseHP;
int maxHP=hp;
int score = 0;
int lastAwardedScore = 0;
Object walls;
Object exit;
vec3d freeRoamCamera = { 1,0.5,1 };
float freeRoamCamera_pitch = pitch;
float freeRoamCamera_yaw = fYaw;
int armorUpgrades = 0, maxArmorUpgrades=20;
int speedUpgrades = 0, maxSpeedUpgrades=5;
int shotsUpgrades = 0, maxShotsUpgrades=3;
const float baseMoveSpd = 2.0f;
const float baseTurnSpd = 2.0f;
const float baseShotSpd = 4.0f;
int baseShotLimit = 2;
int shotLimit = baseShotLimit;
float moveSpd = baseMoveSpd;
float turnSpd = baseTurnSpd;
float hudOffset = 0;
float hudOffsetAcc = 0;
float hudShakeAmt = 0;
Pixel screenCol = WHITE;
float screenAlpha = 0;
float screenAlpha2 = 255.f;
float respawnTimer = 0;
vec3d spawnLoc = { 0,0.3,0 };
FacingDirection spawnFacingDir = FacingDirection::NORTH;
EnemyID lastHitBy=EnemyID::NONE;
bool lastHitByBlinking = false;
int triRenderCount = 0;
vec3d Matrix_MultiplyVector(mat4x4& m, vec3d& i);
mat4x4 Matrix_MakeIdentity();
mat4x4 Matrix_MakeRotationX(float fAngleRad);
mat4x4 Matrix_MakeRotationY(float fAngleRad);
mat4x4 Matrix_MakeRotationZ(float fAngleRad);
mat4x4 Matrix_MakeTranslation(float x, float y, float z);
mat4x4 Matrix_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar);
mat4x4 Matrix_MultiplyMatrix(mat4x4& m1, mat4x4& m2);
mat4x4 Matrix_PointAt(vec3d& pos, vec3d& target, vec3d& up);
mat4x4 Matrix_QuickInverse(mat4x4& m);
vec3d Vector_Add(vec3d& v1, vec3d& v2);
vec3d Vector_Sub(vec3d& v1, vec3d& v2);
vec3d Vector_Mul(vec3d& v1, float k);
vec3d Vector_Div(vec3d& v1, float k);
float Vector_DotProduct(vec3d& v1, vec3d& v2);
float Vector_Length(vec3d& v);
vec3d Vector_Normalise(vec3d& v);
vec3d Vector_CrossProduct(vec3d& v1, vec3d& v2);
vec3d Vector_IntersectPlane(vec3d& plane_p, vec3d& plane_n, vec3d& lineStart, vec3d& lineEnd, float& t);
int Triangle_ClipAgainstPlane(vec3d plane_p, vec3d plane_n, Triangle& in_tri, Triangle& out_tri1, Triangle& out_tri2);
void RenderWorld();
void HandleKeys(float fElapsedTime);
void AddWall(int dir, vi2d gridSquare);
bool OnUserCreate() override;
bool OnUserUpdate(float fElapsedTime) override;
void OnTextEntryComplete(const std::string& sText) override;
void InitializeEnemyData();
void InitializePowerupColors();
void LoadLevel(int level);
void RenderBulletMesh(mat4x4& matView, std::vector<Triangle>& vecTrianglesToRaster, Bullet& b,bool translucent=true);
void RenderMesh(mat4x4&matView, std::vector<Triangle>&vecTrianglesToRaster,Object&o,bool translucent=false);
void RenderMeshDeathScreen(mat4x4& matView, std::vector<Triangle>& vecTrianglesToRaster, Object& o);
void RenderPowerupMesh(mat4x4& matView, std::vector<Triangle>& vecTrianglesToRaster, Powerup&p);
void RunEnemyAI(Enemy& e,float fElapsedTime,int myIndex);
void RenderHud(float fElapsedTime);
void ConvertBulletColor(Mesh& bullet, Pixel col);
void Display3DKillerModel();
int CheckPowerupCollision(vec3d movementVector, vf2d pos, float radius);
void ResetScore();
int EnemiesAlive();
public:
vi2d MAP_SIZE;
float restingTriangleYDepth = 0.f;
std::vector<Bullet>bullets;
float hudShakeTime = 0;
Mesh bullet;
float shotSpd = baseShotSpd;
static bool CheckCollision(vec3d movementVector,vf2d pos,float radius);
static int CheckEnemyCollision(vec3d movementVector, vf2d pos, float radius, int ignoreIndex = -1);
static int CheckExplosiveEnemyCollision(vec3d movementVector, vf2d pos, float radius, int ignoreIndex = -1);
static bool CheckPlayerCollision(vec3d movementVector, vf2d pos, float radius);
void SubtractTag();
void HurtPlayer(EnemyID id, int damage=1,bool blinking = false);
void SpawnPowerup(PowerupType type, vec3d pos);
bool PlayerHasCamo();
vf2d GetPlayerPos();
void AddScore(int score);
vf2d GetRandomizedSpawnPosition();
};