#include "../entity.h" #include "../item.h" #include "../layers.h" #include #define OLC_PGE_APPLICATION #include "../pixelGameEngine.h" #define OLC_PGEX_SPLASHSCREEN #include "../splash.h" #define OLC_SOUNDWAVE #include "../defines.h" #include "../soundwaveEngine.h" #include "../tiles.h" #include "../references.h" #include "../states.h" #include "../flags.h" #include #include "../cutscene.h" #include "../encounters.h" #include "../particle.h" #include "../effect.h" #include "../battleproperty.h" #include "test.h" #include "../SeasonI.h" #include "../map.h" #define TestWhileMax(testname,condition,maxIterations) _TestWait(testname);while(_TestResolve(condition,maxIterations)) #define TestWhile(testname,condition) TestWhileMax(testname,condition,1000) extern int frameCount; extern double CUTSCENE_FADE_VALUE; extern Cutscene*CurrentCutscene; extern ActionType CurrentAction; extern vd2d cameraPos; extern bool messageBoxVisible; extern std::string messageBoxText; extern std::array PARTY_MEMBER_STATS; extern Entity::pstats_t partyMemberDefaultStats; extern std::map SPRITES; extern std::map ANIMATIONS; extern std::array PARTY_MEMBER_OBJ; extern std::map OBJ_INFO; extern std::mapMOVELIST; extern std::vectorENCOUNTER_LIST; extern std::map BATTLE_PROPERTIES; extern std::map MAPS; extern std::vectorCUTSCENE_QUEUE; int testCount=0; int MAX_ITERATIONS=1000; int iterations=0; enum KeyIdentifier{ UP, RIGHT, DOWN, LEFT, BACK, }; struct TestKey{ Key k; std::string keyName; KeyIdentifier id; bool operator==(TestKey&other){ return id==other.id; } }; namespace key{ TestKey UP{Key::UP,"UP",KeyIdentifier::UP}; TestKey DOWN{Key::DOWN,"DOWN",KeyIdentifier::DOWN}; TestKey LEFT{Key::LEFT,"LEFT",KeyIdentifier::LEFT}; TestKey RIGHT{Key::RIGHT,"RIGHT",KeyIdentifier::RIGHT}; TestKey BACK{Key::BACK,"BACKSPACE",KeyIdentifier::BACK}; TestKey ESCAPE{Key::ESCAPE,"ESCAPE",KeyIdentifier::BACK}; TestKey X{Key::X,"X",KeyIdentifier::BACK}; TestKey CTRL{Key::CTRL,"CTRL",KeyIdentifier::BACK}; TestKey DEL{Key::DEL,"DEL",KeyIdentifier::BACK}; TestKey END{Key::END,"END",KeyIdentifier::BACK}; TestKey NP_DECIMAL{Key::NP_DECIMAL,"Numpad Decimal",KeyIdentifier::BACK}; TestKey NP_SUB{Key::NP_SUB,"Numpad Subtract",KeyIdentifier::BACK}; TestKey MINUS{Key::MINUS,"MINUS",KeyIdentifier::BACK}; TestKey PAUSE{Key::PAUSE,"PAUSE",KeyIdentifier::BACK}; TestKey TAB{Key::TAB,"TAB",KeyIdentifier::BACK}; TestKey CAPS_LOCK{Key::CAPS_LOCK,"CAPS LOCK",KeyIdentifier::BACK}; TestKey W{Key::W,"W",KeyIdentifier::UP}; TestKey A{Key::A,"A",KeyIdentifier::LEFT}; TestKey S{Key::S,"S",KeyIdentifier::DOWN}; TestKey D{Key::D,"D",KeyIdentifier::RIGHT}; TestKey NP8{Key::NP8,"NP8",KeyIdentifier::UP}; TestKey NP4{Key::NP4,"NP4",KeyIdentifier::LEFT}; TestKey NP5{Key::NP5,"NP5",KeyIdentifier::DOWN}; TestKey NP6{Key::NP6,"NP6",KeyIdentifier::RIGHT}; TestKey NP2{Key::NP2,"NP2",KeyIdentifier::DOWN}; } bool GetKeyPressed(SeasonI&instance,TestKey&k) { if (k==key::UP) { return instance.UpPressed(); } if (k==key::DOWN) { return instance.DownPressed(); } if (k==key::RIGHT) { return instance.RightPressed(); } if (k==key::LEFT) { return instance.LeftPressed(); } if (k==key::BACK) { return instance.BackPressed(); } std::cerr<<"Something went terribly wrong! THIS SHOULD NOT BE HAPPENING!\n"; return false; } bool GetKeyHeld(SeasonI&instance,TestKey&k) { if (k==key::UP) { return instance.UpHeld(); } if (k==key::DOWN) { return instance.DownHeld(); } if (k==key::RIGHT) { return instance.RightHeld(); } if (k==key::LEFT) { return instance.LeftHeld(); } if (k==key::BACK) { return instance.BackHeld(); } std::cerr<<"Something went terribly wrong! THIS SHOULD NOT BE HAPPENING!\n"; return false; } bool GetKeyReleased(SeasonI&instance,TestKey&k) { if (k==key::UP) { return instance.UpReleased(); } if (k==key::DOWN) { return instance.DownReleased(); } if (k==key::RIGHT) { return instance.RightReleased(); } if (k==key::LEFT) { return instance.LeftReleased(); } if (k==key::BACK) { return instance.BackReleased(); } std::cerr<<"Something went terribly wrong! THIS SHOULD NOT BE HAPPENING!\n"; return false; } void Test(std::string name,bool success) { testCount++; std::cout<<"Test ("<MAX_ITERATIONS) { std::cout<<" FAILED! (Iterations>"< sprList) { for (int i=0;i sprList) { for (int i=0;isprite->width!=0&&SPRITES[sprList[i]]->sprite->height!=0); } } bool SeasonI::OnUserCreate(){ for (int i=0;i0); for (int i=0;istats==partyMemberDefaultStats); } TestSpriteUndefined({"terrainmap.png","pixel.png","bufficons.png","rollingcounter.png","additionalFont.png"}); SetupAnimations(); TestSpriteInitialized({"terrainmap.png","pixel.png","bufficons.png","rollingcounter.png","additionalFont.png"}); Test("Player animation is undefined", ANIMATIONS.count("player.png")==0); Test("Party Member Object is not setup", PARTY_MEMBER_OBJ[0]==nullptr); vd2d testPos={0,0},testScale={2,2}; CreateObjectInfo(new Standard_Obj(PLAYER,"player",testPos,nullptr,testScale,MAGENTA,32),"player.png",{24,32}); Test("Player animation is defined", ANIMATIONS["player.png"]!=nullptr); Decal*playerAnimPointer=ANIMATIONS["player.png"]->spr; Test("Player animation is setup correctly", ANIMATIONS["player.png"]->frames>0&&ANIMATIONS["player.png"]->spr!=nullptr); Test("Object Info database now has 1 entry", OBJ_INFO.size()==1); Test("Object Info is correctly assigned", OBJ_INFO[0]->name=="player"&&OBJ_INFO[0]->id==PLAYER&&OBJ_INFO[0]->GetPos()==testPos&&OBJ_INFO[0]->GetScale()==testScale&&OBJ_INFO[0]->color==MAGENTA&&OBJ_INFO[0]->spr->sprSize.x==24&&OBJ_INFO[0]->spr->sprSize.y==32); delete OBJ_INFO[PLAYER]; OBJ_INFO.erase(PLAYER); Test("Object Info is cleared", OBJ_INFO.size()==0); SetupObjectInfo(); Test("Using the same sprite definition does not cause memory leak", playerAnimPointer==OBJ_INFO[0]->spr->spr); Test("No encounters are loaded", ENCOUNTER_LIST.size()==0); SetupEncounters(); Test("SetupEncounters() loads up new encounters", ENCOUNTER_LIST.size()>0); Encounter*testEncounter=new Encounter(encounter::ENCOUNTER_1,{0,0},std::array{vd2d {grid(1,2)},{grid(2,2)},{grid(3,2)},{grid(4,2)}}, std::vector{ new Entity(new Standard_Obj( NPC1_4,"Test Obj",3,2,ANIMATIONS["player.png"]), {HP:70,maxHP:70,PP:10,maxPP:10,baseAtk:14,speed:5,resistances:{0,0,0,0}},std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], MOVELIST[BattleMoveName::TESTMOVE2], MOVELIST[BattleMoveName::TESTMOVE3], }, {ITEMLIST[ItemName::COOKIE]}, 돈 20), new Entity(new Standard_Obj( NPC1_4,"Test Obj 2",1,3,ANIMATIONS["player.png"]), {HP:70,maxHP:70,PP:10,maxPP:10,baseAtk:14,speed:5,resistances:{0,0,0,0}},std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], MOVELIST[BattleMoveName::TESTMOVE2], }, {ITEMLIST[ItemName::PIZZA]}), new Entity(new Standard_Obj( NPC1_4,"Test Obj 3",2,2,ANIMATIONS["player.png"]), {HP:70,maxHP:70,PP:10,maxPP:10,baseAtk:14,speed:5,resistances:{0,0,0,0}},std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], }), }); Test("Test encounter has three entities", testEncounter->objs.size()==3); Test("Test encounter uses ID ENCOUNTER_1", testEncounter->id==encounter::ENCOUNTER_1); testPos={0,0}; Test("Test encounter uses position ("+std::to_string(testPos.x)+","+std::to_string(testPos.y)+")", testEncounter->pos==testPos); Test("Test encounter player positions are correct", testEncounter->playerPos==std::array{vd2d{grid(1,2)},{grid(2,2)},{grid(3,2)},{grid(4,2)}}); Test("Test encounter grid positions are correct", testEncounter->playerPos==std::array{vd2d{grid(1,2)},{grid(2,2)},{grid(3,2)},{grid(4,2)}}); Test("Enemy 1 has 3 battle moves", testEncounter->objs[0]->moveSet==std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], MOVELIST[BattleMoveName::TESTMOVE2], MOVELIST[BattleMoveName::TESTMOVE3], }); Test("Enemy 1 has a cookie as a drop", testEncounter->objs[0]->inventory==std::vector{ITEMLIST[ItemName::COOKIE]}); Test("Enemy 1 gives 20 money.", testEncounter->objs[0]->money==20); Test("Enemy 2 has 2 battle moves.", testEncounter->objs[1]->moveSet==std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], MOVELIST[BattleMoveName::TESTMOVE2], }); Test("Enemy 2 has a pizza as a drop", testEncounter->objs[1]->inventory==std::vector{ITEMLIST[ItemName::PIZZA]}); Test("Enemy 2 gives 0 money.", testEncounter->objs[1]->money==0); Test("Enemy 3 has 1 battle move.", testEncounter->objs[2]->moveSet==std::vector{ MOVELIST[BattleMoveName::TESTMOVE1], }); Test("Enemy 3 has no items to drop", testEncounter->objs[2]->inventory==std::vector{}); Test("Enemy 3 gives 0 money.", testEncounter->objs[2]->money==0); Test("There are no battle properties", BATTLE_PROPERTIES.size()==0); SetupBattleProperties(); Test("Battle Properties populated", BATTLE_PROPERTIES.size()>0); Test("Test Move 1 exists in the map.", MOVELIST.count(BattleMoveName::TESTMOVE1)); Test("Test Move 1 is undefined", MOVELIST[BattleMoveName::TESTMOVE1]==nullptr); MOVELIST[BattleMoveName::TESTMOVE1]=SetupMove(TESTMOVE1)"Test Move 1","An attack",baseDmg:30,randomDmg:5,range:6,channelTime:27,friendly:false}; Test("Verify Test Move 1's now set properly", MOVELIST[BattleMoveName::TESTMOVE1]!=nullptr); Test("Verify Test Move 1's Power Name shows up correctly", MOVELIST[BattleMoveName::TESTMOVE1]->GetPowerName()=="Test Move 1"); MOVELIST[BattleMoveName::TESTMOVE1]->grade=GAMMA; Test("Verify Test Move 1's Power Name shows up correctly with a grade", MOVELIST[BattleMoveName::TESTMOVE1]->GetPowerName()=="Test Move 1 "+std::string(1,(char)GAMMA)); Test("Cookie is undefined", ITEMLIST[ItemName::COOKIE]==nullptr); SetupItemList(); Test("Cookie is defined", ITEMLIST[ItemName::COOKIE]!=nullptr); delete ITEMLIST[ItemName::COOKIE]; ITEMLIST.erase(ItemName::COOKIE); ITEMLIST[ItemName::COOKIE]=SetupItem(COOKIE)"Cookie","A delightful little treat. Restores 40 HP.",2,{hpRecovery:40,consumable:Consumable::FRIENDLY,sellPrice:2}); Test("Cookie name is set properly", ITEMLIST[ItemName::COOKIE]->name=="Cookie"); Test("Cookie description is set properly", ITEMLIST[ItemName::COOKIE]->description=="A delightful little treat. Restores 40 HP."); Test("Cookie drop chance is set properly", ITEMLIST[ItemName::COOKIE]->dropChance==2); delete ITEMLIST[ItemName::FREEZE_PACKET]; ITEMLIST.erase(ItemName::FREEZE_PACKET); ITEMLIST[ItemName::FREEZE_PACKET]=SetupItem(FREEZE_PACKET)"Freeze Packet","Lets out some blistering cold weather.",256,{consumable:Consumable::ENEMY,sellPrice:36},MOVELIST[BattleMoveName::FREEZE_PACKET]); Test("Freeze Packet name is set properly", ITEMLIST[ItemName::FREEZE_PACKET]->name=="Freeze Packet"); Test("Freeze Packet description is set properly", ITEMLIST[ItemName::FREEZE_PACKET]->description=="Lets out some blistering cold weather."); Test("Freeze Packet drop chance is set properly", ITEMLIST[ItemName::FREEZE_PACKET]->dropChance==256); Test("No Maps are loaded", MAPS.size()==0); SetupMapList(); Test("Maps are loaded", MAPS.size()!=0); Test("No Cutscenes are loaded", CUTSCENES.size()==0); SetupCutscenes(); Test("Calling SetupCutscenes() loads up different cutscenes", CUTSCENES.size()!=0); Test("No active cutscene yet", CurrentCutscene==nullptr); StartCutscene(CUTSCENES[CutsceneName::TEST_CUTSCENE]); Test("Starting a cutscene sets the current cutscene", CurrentCutscene!=nullptr); Test("Current Action should not be updated yet", CurrentAction==ActionType::NONE); updateGame(); Test("HandleCutscenes should get called in updateGame(), updating the current action", CurrentAction!=ActionType::NONE); int startFrame=frameCount; TestWhile("Cutscene should complete entirely",CurrentAction!=ActionType::NONE) { updateGame(); } std::cout<<"Frame Count: "<<(frameCount-startFrame)<previous_fade_value); Test("There should be no objects in the game", OBJECTS.size()==0); TestWhile("Fade out should finish",CurrentAction==ActionType::FADE) { updateGame(); } Test("When faded out, CUTSCENE_FADE_VALUE should be 255", CUTSCENE_FADE_VALUE==255); Test("Next cutscene action should be CreateObjects", CurrentAction==ActionType::CREATE_OBJECTS); Test("There should be 3 objects in the game", OBJECTS.size()==3); Test("Cutscene objects should have the temp flag set", OBJECTS[0]->temp&&OBJECTS[1]->temp&&OBJECTS[2]->temp); updateGame(); Test("Next cutscene action should be Fade back in", CurrentAction==ActionType::FADE); previous_fade_value=CUTSCENE_FADE_VALUE; updateGame(); Test("Fading in causes CUTSCENE_FADE_VALUE to decrease", CUTSCENE_FADE_VALUEGetEndingCutsceneFlag()==Flag::TEST_FLAG3&&CurrentCutscene->GetEndingCutsceneVal()); updateGame(); Test("Next cutscene action should be setting a flag on the spot", CurrentAction==ActionType::SET_FLAG); Test("Test Flag 2 should now be set", GetGameFlag(Flag::TEST_FLAG2)); Test("Test Flag 2 should still not be set", !GetGameFlag(Flag::TEST_FLAG3)); cameraPos={0,0}; Test("Camera position should be {0,0}", cameraPos.x==0&&cameraPos.y==0); updateGame(); Test("Next cutscene action should be panning the camera", CurrentAction==ActionType::PAN_CAMERA); Test("Camera position should be moving towards {128,128}", cameraPos.x>0&&cameraPos.y>0); Test("Temporary cutscene object 1 is at position {136,136}", CurrentCutscene->GetCutsceneObjects()[1]->GetPos().x==136&&CurrentCutscene->GetCutsceneObjects()[1]->GetPos().y==136); TestWhile("Panning Camera should finish",CurrentAction==ActionType::PAN_CAMERA) { updateGame(); } Test("Camera position should be {128,128}", cameraPos.x==128&&cameraPos.y==128); Test("Next cutscene action is the async cutscene object movement", CurrentAction==ActionType::MOVE_CUTSCENE_OBJ_ASYNC); vd2d prevCutsceneObjPosition=CurrentCutscene->GetCutsceneObjects()[1]->GetPos(); updateGame(); Test("Cutscene object is moving towards {80,64}", CurrentCutscene->GetCutsceneObjects()[1]->GetPos().xGetCutsceneObjects()[1]->GetPos().ycameraPos.x&&prevCameraPos.y>cameraPos.y); Test("Due to async actions, next cutscene action should be Dialog Box Async", CurrentAction==ActionType::DIALOG_ASYNC); Test("A message box should be on-screen", messageBoxVisible); GetAnyKeyPress(H); GetAnyKeyPress(H); Test("Pressing any key should close the message box", !messageBoxVisible); updateGame(); Test("Next cutscene action should be Modify Object 0", CurrentAction==ActionType::MODIFY_OBJECT); Test("Cutscene Object 0 is now size {5,5}", CurrentCutscene->GetCutsceneObjects()[0]->GetScale().x==5&&CurrentCutscene->GetCutsceneObjects()[0]->GetScale().y==5); updateGame(); Test("Next cutscene action should be Move Cutscene Object 1", CurrentAction==ActionType::MOVE_CUTSCENE_OBJ); prevCutsceneObjPosition=CurrentCutscene->GetCutsceneObjects()[1]->GetPos(); updateGame(); Test("Cutscene Object 1 is moving towards position {320,64}", CurrentCutscene->GetCutsceneObjects()[1]->GetPos().x>prevCutsceneObjPosition.x&&CurrentCutscene->GetCutsceneObjects()[1]->GetPos().y==prevCutsceneObjPosition.y); TestWhile("Move Cutscene Object should finish",CurrentAction==ActionType::MOVE_CUTSCENE_OBJ) { updateGame(); } Test("Cutscene Object 1 should be at position {320,64}", CurrentCutscene->GetCutsceneObjects()[1]->GetPos().x==320&&CurrentCutscene->GetCutsceneObjects()[1]->GetPos().y==64); Test("Next cutscene action is Dialog Box", CurrentAction==ActionType::DIALOG); Test("Dialog box should be visible", messageBoxVisible); GetAnyKeyPress(S); Test("Pressing a key should show the entire message", messageBoxText=="Hello!"); GetAnyKeyPress(S); Test("Pressing a key should close the message box", !messageBoxVisible); int prevDestructionCount=OBJECT_DESTRUCTION_COUNT; updateGame(); Test("Next cutscene action should be loading a map", CurrentAction==ActionType::LOAD_MAP); Test("Current Map should now be TWOSON", CURRENT_MAP==MAPS[MapName::TWOSON]); Test("All temporary objects should now be destroyed", OBJECT_DESTRUCTION_COUNT-3==prevDestructionCount); updateGame(); Test("Next action should be moving player objects", CurrentAction==ActionType::MOVE_PLAYER_OBJS); Test("All player objects should now be at position {16,16}", PARTY_MEMBER_OBJ[0]->GetPos().x==16&&PARTY_MEMBER_OBJ[0]->GetPos().y==16); Test("Next cutscene action should be cleanup", CurrentCutscene->CurrentAction()==ActionType::CLEANUP); updateGame(); Test("All cutscene and action variables should be cleaned up", CurrentCutscene==nullptr&& CurrentAction==ActionType::NONE); TestKey testkeys[] = {key::UP,key::DOWN,key::LEFT,key::RIGHT,key::W,key::A,key::S,key::D,key::NP8,key::NP4,key::NP5,key::NP6,key::NP2,key::BACK,key::ESCAPE,key::X,key::CTRL,key::DEL,key::END,key::NP_DECIMAL,key::NP_SUB,key::MINUS,key::PAUSE,key::TAB,key::CAPS_LOCK}; for (TestKey k : testkeys) { PressTestKey(k.k); Test(k.keyName+" key is pressed", GetKeyPressed(*this,k)); updateGame(); Test(k.keyName+" key is no longer pressed", !GetKeyPressed(*this,k)); Test(k.keyName+" key is held down", GetKeyHeld(*this,k)); updateGame(); Test(k.keyName+" key is still held down", GetKeyHeld(*this,k)); for (int i=0;i<50;i++) { updateGame(); } Test(k.keyName+" key is still held down after multiple updates", GetKeyHeld(*this,k)); ReleaseTestKey(k.k); Test(k.keyName+" key is no longer held down", !GetKeyHeld(*this,k)); Test(k.keyName+" key is released", GetKeyReleased(*this,k)); updateGame(); Test(k.keyName+" key is no longer released", !GetKeyReleased(*this,k)); } return true; } bool SeasonI::OnUserUpdate(float fElapsedTime) { return true; } int main() { SeasonI demo; if (demo.Construct(WIDTH, HEIGHT, 4, 4)) demo.Start(); return 0; }