#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=false; int messageBoxCursor; std::string messageBoxSpeaker; std::string messageBoxText; std::string messageBoxRefText; bool firstNewline=false; bool secondNewline=false; bool foodMeterVisible=false; int foodCount=3; bool oxygenMeterVisible=false; int oxygenQualityLevel=34; olc::Decal*DOME_DECAL,*FOOD_METER_DECAL,*OXYGEN_METER_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")); FOOD_METER_DECAL=new olc::Decal(new olc::Sprite("assets/corn.png")); OXYGEN_METER_DECAL=new olc::Decal(new olc::Sprite("assets/co2.png")); BASE_OBJECTS["DOME"]=DOME_DECAL; LoadMap("assets/maps/map1"); return true; } void GetAnyKeyPress() override { if (messageBoxVisible) { if (messageBoxCursor!=messageBoxRefText.length()) { while (messageBoxCursor<messageBoxRefText.length()) { advanceMessageBox(); } } else { messageBoxVisible=false; } } switch (GAME_STATE) { case CUTSCENE_1:{ if (textInd>=STORY_TEXT1.length()) { fadeOut(); } }break; } } 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) { CURRENT_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 (messageBoxVisible) { if (frameCount%MESSAGE_SCROLL_WAIT_SPD==0) { if (messageBoxCursor<messageBoxRefText.length()) { advanceMessageBox(); } } } 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.1,(double)MAP_WIDTH); } if (GetKey(olc::A).bHeld||GetKey(olc::LEFT).bHeld) { PLAYER_COORDS[0]=clamp(PLAYER_COORDS[0]-MOVE_SPD,0.1,(double)MAP_WIDTH); } if (GetKey(olc::W).bHeld||GetKey(olc::UP).bHeld) { PLAYER_COORDS[1]=clamp(PLAYER_COORDS[1]-MOVE_SPD,0.1,(double)MAP_HEIGHT); } if (GetKey(olc::S).bHeld||GetKey(olc::DOWN).bHeld) { PLAYER_COORDS[1]=clamp(PLAYER_COORDS[1]+MOVE_SPD,0.1,(double)MAP_HEIGHT); } } switch (CURRENT_CUTSCENE) { case CUTSCENE_4:{ if (!messageBoxVisible) { if (!CUTSCENE_FLAGS[0]) { CUTSCENE_FLAGS[0]=true; DisplayMessageBox(0); } else if (!CUTSCENE_FLAGS[1]) { CUTSCENE_FLAGS[1]=true; DisplayMessageBox(1); } else if (!CUTSCENE_FLAGS[2]) { CUTSCENE_FLAGS[2]=true; DisplayMessageBox(2); foodMeterVisible=true; } else if (!CUTSCENE_FLAGS[3]) { CUTSCENE_FLAGS[3]=true; DisplayMessageBox(3); oxygenMeterVisible=true; } } }break; } 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 advanceMessageBox() { char c = messageBoxRefText[messageBoxCursor++]; printf("%c",c); if (c=='\n') { if (!firstNewline) { firstNewline=true; return; } else if (!secondNewline) { secondNewline=true; return; } } if (firstNewline&&!secondNewline) { messageBoxSpeaker+=c; return; } messageBoxText+=c; if (GetTextSizeProp(messageBoxText).x>WIDTH-16) { int tempIndex=messageBoxCursor; while (messageBoxText[--tempIndex]!=' ') { messageBoxText.erase(tempIndex); } messageBoxText.erase(tempIndex++); messageBoxText+='\n'; while (tempIndex<messageBoxCursor) { messageBoxText+=messageBoxRefText[tempIndex++]; } } } 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; } int meterYOffset=2; if (foodMeterVisible) { DrawStringDecal({WIDTH-36*0.4-GetTextSize(to_string(foodCount)).x*1-8,meterYOffset+2},to_string(foodCount),olc::BLUE,{1,1}); DrawStringDecal({WIDTH-36*0.4-GetTextSize(to_string(foodCount)).x*1-7,meterYOffset+2},to_string(foodCount),olc::BLACK,{1,1}); DrawDecal({WIDTH-52*0.4,meterYOffset},FOOD_METER_DECAL,{0.4,0.4}); meterYOffset+=(2+48*0.4); } if (oxygenMeterVisible) { DrawStringDecal({WIDTH-36*0.4-GetTextSize(to_string(oxygenQualityLevel)+"%").x*1-8,meterYOffset+2},to_string(oxygenQualityLevel)+"%",olc::BLUE,{1,1}); DrawStringDecal({WIDTH-36*0.4-GetTextSize(to_string(oxygenQualityLevel)+"%").x*1-7,meterYOffset+2},to_string(oxygenQualityLevel)+"%",olc::BLACK,{1,1}); DrawDecal({WIDTH-52*0.4,meterYOffset},OXYGEN_METER_DECAL,{0.4,0.4}); meterYOffset+=(2+48*0.4); } if (messageBoxVisible) { DrawDialogBox({4,HEIGHT-60},{WIDTH/2,16},olc::Pixel(18, 0, 33,180)); DrawDialogBox({0,HEIGHT-48},{WIDTH,48},olc::Pixel(18, 0, 33,180)); DrawStringPropDecal({8,HEIGHT-40},messageBoxText); DrawStringPropDecal({8,HEIGHT-57},messageBoxSpeaker); if (messageBoxCursor==messageBoxRefText.length()) { DrawStringPropDecal({WIDTH-16-(float)sin(frameCount*8/60.0)*3,HEIGHT-8+(float)(cos(frameCount*6/60.0)*0.6)},"v",olc::Pixel(173, 74, 255,(0.5*sin(frameCount*8/60.0)+0.5)*128+128),{(float)sin(frameCount*8/60.0),0.5}); } } FillRectDecal({0,0},{WIDTH,HEIGHT},olc::Pixel(0,0,0,transparency)); } void DrawGameWorld() { for (int y=-1;y<=HEIGHT/32+1;y++) { for (int x=-1;x<=WIDTH/32+1;x++) { float xOffset=PLAYER_COORDS[0]-(WIDTH/32/2); float yOffset=PLAYER_COORDS[1]-(HEIGHT/32/2); srand((int)(yOffset+y)*MAP_WIDTH+(int)(xOffset+x)); int tileX=0; int tileRot=0; while (tileX<3&&rand()%4==0) { tileX++; } while (tileRot<3&&rand()%8<5) { tileRot++; } if ((int)(xOffset+x)>=0&&(int)(xOffset+x)<MAP_WIDTH&&(int)(yOffset+y)>=0&&(int)(yOffset+y)<MAP_HEIGHT) { DrawPartialRotatedDecal({(x-(PLAYER_COORDS[0]-(int)PLAYER_COORDS[0]))*32,(y-(PLAYER_COORDS[1]-(int)PLAYER_COORDS[1]))*32},TILES,tileRot*M_PI_2,{16,16},{tileX*32,0},{32,32},{1,1},TILE_COLORS[MAP[(int)(yOffset+y)][(int)(xOffset+x)]]); } } } for (auto&obj:OBJECTS) { DrawDecal({(obj.x-PLAYER_COORDS[0])*32+WIDTH/2,(obj.y-PLAYER_COORDS[1])*32+HEIGHT/2},obj.spr); } } void DrawDialogBox(const olc::vi2d &pos, const olc::vi2d &size, olc::Pixel p = olc::WHITE, olc::Pixel p2 = olc::DARK_GREY, olc::Pixel p3 = olc::VERY_DARK_GREY) { FillRectDecal({(float)pos.x,(float)pos.y},size,p2); FillRectDecal({(float)pos.x+1,(float)pos.y+1},{(float)size.x-2,(float)size.y-2},p); FillRectDecal({(float)pos.x+2,(float)pos.y+2},{(float)size.x-4,(float)size.y-4},p3); FillRectDecal({(float)pos.x+3,(float)pos.y+3},{(float)size.x-5,(float)size.y-5},p); Draw({pos.x,pos.y},olc::BLACK); Draw({pos.x+size.x,pos.y+size.y},olc::BLACK); Draw({pos.x+size.x,pos.y},olc::BLACK); Draw({pos.x,pos.y+size.y},olc::BLACK); } void fadeOut() { fade=true; } void fadeIn() { fade=false; } void DisplayMessageBox(int dialogNumber) { messageBoxVisible=true; messageBoxCursor=0; std::string split1=STORY_DIALOG[dialogNumber].substr(0,STORY_DIALOG[dialogNumber].find('\n')); //Unused for now. std::string split2=STORY_DIALOG[dialogNumber].substr(STORY_DIALOG[dialogNumber].find('\n')+1,STORY_DIALOG[dialogNumber].find('\n',STORY_DIALOG[dialogNumber].find('\n')+1)-(STORY_DIALOG[dialogNumber].find('\n')+1)); std::string split3=STORY_DIALOG[dialogNumber].substr(STORY_DIALOG[dialogNumber].find('\n',STORY_DIALOG[dialogNumber].find('\n')+1)+1,STORY_DIALOG[dialogNumber].length()-(STORY_DIALOG[dialogNumber].find('\n',STORY_DIALOG[dialogNumber].find('\n')+1)+1)); messageBoxSpeaker=split2; messageBoxText=""; messageBoxRefText=split3; } }; int main() { SeasonsOfLoneliness demo; if (demo.Construct(256, 224, 4, 4)) demo.Start(); return 0; }