|
|
|
#define OLC_PGE_APPLICATION
|
|
|
|
#include "pixelGameEngine.h"
|
|
|
|
#include "data.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
enum GAMESTATE{
|
|
|
|
CUTSCENE_1,
|
|
|
|
CUTSCENE_2,
|
|
|
|
CUTSCENE_3,
|
|
|
|
GAMEWORLD,
|
|
|
|
WAITING_FOR_CUTSCENE_3,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum TILES{
|
|
|
|
DIRT,
|
|
|
|
GRASS,
|
|
|
|
WATER,
|
|
|
|
PLANTS
|
|
|
|
};
|
|
|
|
|
|
|
|
enum CUTSCENE{
|
|
|
|
PAN_DOME,
|
|
|
|
PAUSE_TO_CUTSCENE_3,
|
|
|
|
CUTSCENE_4,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define WIDTH 256
|
|
|
|
#define HEIGHT 224
|
|
|
|
#define ALPHA_SCREEN1 128
|
|
|
|
#define ALPHA_SCREEN2 20
|
|
|
|
#define FADE_SPD 6
|
|
|
|
#define MOVE_SPD 0.2
|
|
|
|
#define MESSAGE_SCROLL_WAIT_SPD 2 //Number of frames to wait.
|
|
|
|
|
|
|
|
class Object{
|
|
|
|
public:
|
|
|
|
float x,y;
|
|
|
|
olc::Decal*spr;
|
|
|
|
};
|
|
|
|
|
|
|
|
class SeasonsOfLoneliness : public olc::PixelGameEngine
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SeasonsOfLoneliness()
|
|
|
|
{
|
|
|
|
sAppName = "Seasons of Loneliness";
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
GAMESTATE GAME_STATE=GAMEWORLD;
|
|
|
|
int textInd=0;
|
|
|
|
int cursorX=0;
|
|
|
|
int transitionTime=0;
|
|
|
|
bool fade=false;
|
|
|
|
int transparency=0;
|
|
|
|
int frameCount=0;
|
|
|
|
float elapsedTime=0;
|
|
|
|
const float TARGET_RATE = 1/60.0;
|
|
|
|
std::string CUTSCENE_CONSOLE_TEXT = "";
|
|
|
|
int**MAP=NULL;
|
|
|
|
int MAP_WIDTH=-1;
|
|
|
|
int MAP_HEIGHT=-1;
|
|
|
|
olc::Decal*TILES;
|
|
|
|
float PLAYER_COORDS[2] = {};
|
|
|
|
std::vector<Object> OBJECTS;
|
|
|
|
bool CUTSCENE_ACTIVE=false;
|
|
|
|
CUTSCENE CURRENT_CUTSCENE=PAN_DOME;
|
|
|
|
int CUTSCENE_TIMER=0;
|
|
|
|
bool CUTSCENE_FLAGS[8];
|
|
|
|
bool messageBoxVisible;
|
|
|
|
int messageBoxCursor;
|
|
|
|
std::string messageBoxSpeaker;
|
|
|
|
std::string messageBoxText;
|
|
|
|
|
|
|
|
olc::Decal*DOME_DECAL;
|
|
|
|
std::map<std::string,olc::Decal*> BASE_OBJECTS;
|
|
|
|
|
|
|
|
void LoadMap(char*mapName) {
|
|
|
|
std::ifstream f(mapName);
|
|
|
|
std::string data;
|
|
|
|
MAP_WIDTH=MAP_HEIGHT=-1;
|
|
|
|
if (MAP!=NULL) {
|
|
|
|
for (int y=0;y<MAP_HEIGHT;y++) {
|
|
|
|
free(MAP[y]);
|
|
|
|
}
|
|
|
|
free(MAP);
|
|
|
|
MAP=NULL;
|
|
|
|
}
|
|
|
|
OBJECTS.clear();
|
|
|
|
|
|
|
|
int y=0;
|
|
|
|
if (f.is_open()) {
|
|
|
|
while (f.good()) {
|
|
|
|
f>>data;
|
|
|
|
if (MAP_WIDTH==-1) {
|
|
|
|
std::stringstream stream(data);
|
|
|
|
stream>>MAP_WIDTH;
|
|
|
|
} else
|
|
|
|
if (MAP_HEIGHT==-1) {
|
|
|
|
std::stringstream stream(data);
|
|
|
|
stream>>MAP_HEIGHT;
|
|
|
|
} else
|
|
|
|
if (y<MAP_HEIGHT) {
|
|
|
|
if (MAP==NULL) {
|
|
|
|
MAP=(int**)malloc(sizeof(int**)*MAP_HEIGHT);
|
|
|
|
}
|
|
|
|
MAP[y]=(int*)malloc(sizeof(int*)*MAP_WIDTH);
|
|
|
|
for (int i=0;i<data.length();i++) {
|
|
|
|
MAP[y][i]=data[i]-'0';
|
|
|
|
}
|
|
|
|
y++;
|
|
|
|
} else {
|
|
|
|
Object obj;
|
|
|
|
std::stringstream split1(data.substr(0,data.find(';')));
|
|
|
|
split1>>obj.x;
|
|
|
|
std::stringstream split2(data.substr(data.find(';')+1,data.find(';',data.find(";")+1)-(data.find(';')+1)));
|
|
|
|
split2>>obj.y;
|
|
|
|
std::string split3 = data.substr(data.find(';',data.find(";")+1)+1,data.length()-(data.find(';',data.find(";")+1)+1));
|
|
|
|
obj.spr=BASE_OBJECTS[split3];
|
|
|
|
printf("Loaded object %s: (%f,%f)\n",split3.c_str(),obj.x,obj.y);
|
|
|
|
OBJECTS.push_back(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("Loaded %dx%d map:\n",MAP_WIDTH,MAP_HEIGHT);
|
|
|
|
for (int y=0;y<MAP_HEIGHT;y++) {
|
|
|
|
for (int x=0;x<MAP_WIDTH;x++) {
|
|
|
|
printf("%d",MAP[y][x]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnUserCreate() override
|
|
|
|
{
|
|
|
|
SetPixelMode(olc::Pixel::ALPHA);
|
|
|
|
//ConsoleCaptureStdOut(true);
|
|
|
|
// Called once at the start, so create things here
|
|
|
|
TILES=new olc::Decal(new olc::Sprite("assets/tiles.png"));
|
|
|
|
DOME_DECAL=new olc::Decal(new olc::Sprite("assets/dome.png"));
|
|
|
|
BASE_OBJECTS["DOME"]=DOME_DECAL;
|
|
|
|
LoadMap("assets/maps/map2");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetAnyKey() override {
|
|
|
|
switch (GAME_STATE) {
|
|
|
|
case CUTSCENE_1:{
|
|
|
|
if (textInd>=STORY_TEXT1.length()) {
|
|
|
|
fadeOut();
|
|
|
|
}
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override
|
|
|
|
{
|
|
|
|
elapsedTime+=fElapsedTime;
|
|
|
|
while (elapsedTime>TARGET_RATE) {
|
|
|
|
elapsedTime-=TARGET_RATE;
|
|
|
|
updateGame();
|
|
|
|
}
|
|
|
|
|
|
|
|
drawGame();
|
|
|
|
// called once per frame
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fadeOutCompleted() {
|
|
|
|
switch (GAME_STATE) {
|
|
|
|
case CUTSCENE_1:{
|
|
|
|
GAME_STATE=CUTSCENE_2;
|
|
|
|
PlayCutscene(PAN_DOME);
|
|
|
|
fadeIn();
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_2:{
|
|
|
|
fadeIn();
|
|
|
|
PlayCutscene(CUTSCENE_4);
|
|
|
|
GAME_STATE=GAMEWORLD;
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fadeInCompleted() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayCutscene(CUTSCENE scene) {
|
|
|
|
switch (scene) {
|
|
|
|
case PAN_DOME:{
|
|
|
|
PLAYER_COORDS[0]=14;
|
|
|
|
PLAYER_COORDS[1]=18+(64/2/32);
|
|
|
|
}break;
|
|
|
|
case PAUSE_TO_CUTSCENE_3:{
|
|
|
|
CUTSCENE_CONSOLE_TEXT.clear();
|
|
|
|
textInd=0;
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_4:{
|
|
|
|
LoadMap("assets/maps/map2");
|
|
|
|
PLAYER_COORDS[0]=16;
|
|
|
|
PLAYER_COORDS[1]=6;
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
for (int i=0;i<8;i++) {
|
|
|
|
CUTSCENE_FLAGS[i]=false;
|
|
|
|
}
|
|
|
|
CUTSCENE_ACTIVE=true;
|
|
|
|
CUTSCENE_TIMER=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateGame(){
|
|
|
|
frameCount++;
|
|
|
|
if (CUTSCENE_ACTIVE) {
|
|
|
|
CUTSCENE_TIMER++;
|
|
|
|
}
|
|
|
|
if (fade&&transparency<255) {
|
|
|
|
transparency=clamp(transparency+FADE_SPD,0,255);
|
|
|
|
if (transparency==255) {
|
|
|
|
fadeOutCompleted();
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (!fade&&transparency>0) {
|
|
|
|
transparency=clamp(transparency-FADE_SPD,0,255);
|
|
|
|
if (transparency==0) {
|
|
|
|
fadeInCompleted();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetKey(olc::F1).bPressed) {
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CUTSCENE_ACTIVE&&!messageBoxVisible) {
|
|
|
|
if (GetKey(olc::D).bHeld||GetKey(olc::RIGHT).bHeld) {
|
|
|
|
PLAYER_COORDS[0]=clamp(PLAYER_COORDS[0]+MOVE_SPD,0.0,(double)MAP_WIDTH);
|
|
|
|
}
|
|
|
|
if (GetKey(olc::A).bHeld||GetKey(olc::LEFT).bHeld) {
|
|
|
|
PLAYER_COORDS[0]=clamp(PLAYER_COORDS[0]-MOVE_SPD,0.0,(double)MAP_WIDTH);
|
|
|
|
}
|
|
|
|
if (GetKey(olc::W).bHeld||GetKey(olc::UP).bHeld) {
|
|
|
|
PLAYER_COORDS[1]=clamp(PLAYER_COORDS[1]-MOVE_SPD,0.0,(double)MAP_HEIGHT);
|
|
|
|
}
|
|
|
|
if (GetKey(olc::S).bHeld||GetKey(olc::DOWN).bHeld) {
|
|
|
|
PLAYER_COORDS[1]=clamp(PLAYER_COORDS[1]+MOVE_SPD,0.0,(double)MAP_HEIGHT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (GAME_STATE) {
|
|
|
|
case CUTSCENE_1:
|
|
|
|
case CUTSCENE_3:{
|
|
|
|
std::string refText;
|
|
|
|
switch (GAME_STATE) {
|
|
|
|
case CUTSCENE_1:{
|
|
|
|
refText=STORY_TEXT1;
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_3:{
|
|
|
|
refText=STORY_TEXT2;
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
if (GAME_STATE==CUTSCENE_3&&frameCount%4!=0) {break;}
|
|
|
|
if (textInd<refText.length()) {
|
|
|
|
char c = refText[textInd++];
|
|
|
|
CUTSCENE_CONSOLE_TEXT+=c;
|
|
|
|
if (c!='\n') {
|
|
|
|
cursorX++;
|
|
|
|
} else {
|
|
|
|
cursorX=0;
|
|
|
|
}
|
|
|
|
if (GetTextSize(CUTSCENE_CONSOLE_TEXT).x>WIDTH-32) {
|
|
|
|
int tempIndex=textInd;
|
|
|
|
while (CUTSCENE_CONSOLE_TEXT[--tempIndex]!=' ') {
|
|
|
|
CUTSCENE_CONSOLE_TEXT.erase(tempIndex);
|
|
|
|
cursorX--;
|
|
|
|
}
|
|
|
|
CUTSCENE_CONSOLE_TEXT.erase(tempIndex++);
|
|
|
|
CUTSCENE_CONSOLE_TEXT+='\n';
|
|
|
|
cursorX=0;
|
|
|
|
while (tempIndex<textInd) {
|
|
|
|
CUTSCENE_CONSOLE_TEXT+=refText[tempIndex++];
|
|
|
|
cursorX++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_2:{
|
|
|
|
//FLAG 0 - When flipped on, camera stops.
|
|
|
|
if (!CUTSCENE_FLAGS[0]) {
|
|
|
|
PLAYER_COORDS[0]+=0.06;
|
|
|
|
} else {
|
|
|
|
if (CUTSCENE_TIMER>120) {
|
|
|
|
fadeOut();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (PLAYER_COORDS[0]>38+(128/2/32)) {
|
|
|
|
PLAYER_COORDS[0]=38+(128/2/32);
|
|
|
|
CUTSCENE_FLAGS[0]=true;
|
|
|
|
CUTSCENE_TIMER=0;
|
|
|
|
}
|
|
|
|
}break;
|
|
|
|
case WAITING_FOR_CUTSCENE_3:{
|
|
|
|
if (transparency>200) {
|
|
|
|
GAME_STATE=CUTSCENE_3;
|
|
|
|
}
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawGame(){
|
|
|
|
switch (GAME_STATE) {
|
|
|
|
case CUTSCENE_1:{
|
|
|
|
DrawStringDecal({16,16},CUTSCENE_CONSOLE_TEXT,olc::GREEN,{1,1});
|
|
|
|
if (textInd<STORY_TEXT1.length()) {
|
|
|
|
FillRectDecal({(float)(16+(cursorX)*8%(WIDTH-32)),(float)(8+GetTextSize(CUTSCENE_CONSOLE_TEXT).y+((cursorX==28)?8:0))},{4,8},olc::GREEN);
|
|
|
|
} else {
|
|
|
|
FillRectDecal({(float)(16+(cursorX)*8%(WIDTH-32)),(float)(8+GetTextSize(CUTSCENE_CONSOLE_TEXT).y+((cursorX==28)?8:0))},{4,8},olc::Pixel(0,255,0,(0.5*sin(frameCount*4/60.0)+0.5)*256));
|
|
|
|
}
|
|
|
|
GradientFillRectDecal({0,0},{WIDTH/2,HEIGHT/2},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN2},{20, 28, 22,ALPHA_SCREEN1});
|
|
|
|
GradientFillRectDecal({WIDTH/2,0},{WIDTH/2,HEIGHT/2},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN2},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1});
|
|
|
|
GradientFillRectDecal({0,HEIGHT/2},{WIDTH/2,HEIGHT/2},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN2});
|
|
|
|
GradientFillRectDecal({WIDTH/2,HEIGHT/2},{WIDTH/2,HEIGHT/2},{20, 28, 22,ALPHA_SCREEN2},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1},{20, 28, 22,ALPHA_SCREEN1});
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_2:
|
|
|
|
case GAMEWORLD:{
|
|
|
|
DrawGameWorld();
|
|
|
|
}break;
|
|
|
|
case CUTSCENE_3:{
|
|
|
|
DrawStringDecal({48,16},CUTSCENE_CONSOLE_TEXT,olc::Pixel(100, 10, 255),{2,2});
|
|
|
|
if (textInd<STORY_TEXT2.length()) {
|
|
|
|
FillRectDecal({(float)(48+(cursorX)*16%(WIDTH-32)),(float)(GetTextSize(CUTSCENE_CONSOLE_TEXT).y*2+((cursorX==28)?16:0))},{4*2,8*2},olc::Pixel(100, 10, 255));
|
|
|
|
} else {
|
|
|
|
FillRectDecal({(float)(48+(cursorX)*16%(WIDTH-32)),(float)(GetTextSize(CUTSCENE_CONSOLE_TEXT).y*2+((cursorX==28)?16:0))},{4*2,8*2},olc::Pixel(100, 10, 255,(0.5*sin(frameCount*4/60.0)+0.5)*256));
|
|
|
|
}
|
|
|
|
GradientFillRectDecal({0,0},{WIDTH/2,HEIGHT/2},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN2},{100, 10, 255,ALPHA_SCREEN1});
|
|
|
|
GradientFillRectDecal({WIDTH/2,0},{WIDTH/2,HEIGHT/2},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN2},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1});
|
|
|
|
GradientFillRectDecal({0,HEIGHT/2},{WIDTH/2,HEIGHT/2},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN2});
|
|
|
|
GradientFillRectDecal({WIDTH/2,HEIGHT/2},{WIDTH/2,HEIGHT/2},{100, 10, 255,ALPHA_SCREEN2},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1},{100, 10, 255,ALPHA_SCREEN1});
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
FillRectDecal({0,0},{WIDTH,HEIGHT},olc::Pixel(0,0,0,transparency));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawGameWorld() {
|
|
|
|
for (int y=-1;y<=HEIGHT/32;y++) {
|
|
|
|
for (int x=-1;x<=WIDTH/32;x++) {
|
|
|
|
float xOffset=PLAYER_COORDS[0]-(WIDTH/32/2);
|
|
|
|
float yOffset=PLAYER_COORDS[1]-(HEIGHT/32/2);
|
|
|
|
DrawPartialDecal({(x-(PLAYER_COORDS[0]-(int)PLAYER_COORDS[0]))*32,(y-(PLAYER_COORDS[1]-(int)PLAYER_COORDS[1]))*32},{32,32},TILES,{0,0},{32,32},TILE_COLORS[MAP[clamp((int)(yOffset+y),0,MAP_HEIGHT-1)][clamp((int)(xOffset+x),0,MAP_WIDTH-1)]]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto&obj:OBJECTS) {
|
|
|
|
DrawDecal({(obj.x-PLAYER_COORDS[0])*32+WIDTH/2,(obj.y-PLAYER_COORDS[1])*32+HEIGHT/2},obj.spr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fadeOut() {
|
|
|
|
fade=true;
|
|
|
|
}
|
|
|
|
void fadeIn() {
|
|
|
|
fade=false;
|
|
|
|
}
|
|
|
|
void DisplayMessageBox(int dialogNumber) {
|
|
|
|
messageBoxVisible=true;
|
|
|
|
messageBoxCursor=0;
|
|
|
|
messageBoxSpeaker="";
|
|
|
|
messageBoxText="";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
SeasonsOfLoneliness demo;
|
|
|
|
if (demo.Construct(256, 224, 4, 4))
|
|
|
|
demo.Start();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|