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.
437 lines
10 KiB
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();
|
|
}; |