#ifndef OBJECT_H #define OBJECT_H #include "pixelGameEngine.h" #include "flags.h" #include "animation.h" #include "defines.h" #include "layers.h" #include "item.h" #include "states.h" #include "entity.h" #include "SeasonI.h" using namespace olc; extern std::vector OBJECTS; struct Interaction{ std::vector messages={}; Item*item=nullptr; Flag flag=Flag::NONE; }; struct ObjectExtraData{ int moveTime,moveFreq; double moveSpd; }; class Object{ private: vd2d scale={1,1}; vd2d pos; vd2d startPos; //Where the object was first located. The object will be put back here if we enter the editor. public: int id; Animation*spr; int animationSubIndex=0; //Which animation within the animation we are on. int frameIndex=0; int frameCount=0; int animationSpd=12; //How many frames to wait between each frame. Setting to 0 pauses the animation. std::string name; bool animated=true; Pixel color=WHITE; vd2d originPoint={0,0}; bool drawn=false; Flag disableFlag=Flag::NONE; Flag enableFlag=Flag::NONE; ObjectExtraData extraData; int objArrElement; //Which element in the object array this object is located in. For sorting purposes. bool temp=false; //If set to true, it's marked for deletion after cutscene handling. bool enc=false; //If set to true, it's not included in the main list of entities for map saving because it's from an encounter. bool dead=false; //If set to true, this object was properly part of an Entity and got declared as dead. bool hasCollision=true; //Set to false to prevent resolving of collisions. int blinkFrames=0; //Frame count of how much time is left for the image to be blinking. Used when enemies take damage. //animationSpd is how long to wait before switching frames. bool highlighted=false; //Whether or not this object has been declared as highlighted by a target range selector. bool Collision(vd2d pos) { GAME->SetDrawTarget(layer::COLLISION); Pixel collisionData = GAME->GetDrawTarget()->GetPixel((int)pos.x-cameraPos.x,(int)pos.y-cameraPos.y); if (collisionData!=MAGENTA) { return true; } else { for (int i=0;ihasCollision) { if (std::abs(OBJECTS[i]->GetPosWithOrigin().x-pos.x)+std::abs(OBJECTS[i]->GetPosWithOrigin().y-pos.y)<4) { return true; } } } } return false; } //A grid version of the constructor. used ONLY for battle setups. Object(int id,std::string name,int gridx,int gridy,Animation*spr,vd2d scale={1,1},Pixel color=WHITE,int animationSpd=1,bool temp=false,ObjectExtraData data={moveTime:0,moveFreq:0,moveSpd:0}) :Object(id,name,{gridx*32-(spr->sprSize.x*0.5)*(scale.x-1),gridy*32-(spr->sprSize.y-4)*(scale.y-1)},spr,scale,color,animationSpd,temp,data) {} Object(int id,std::string name,vd2d pos,Animation*spr,vd2d scale={1,1},Pixel color=WHITE,int animationSpd=1,bool temp=false,ObjectExtraData data={moveTime:0,moveFreq:0,moveSpd:0}) :spr(spr),pos(pos),id(id),name(name),color(color),animationSpd(animationSpd),temp(temp),extraData(data),startPos(pos) { SetScale(scale); } virtual Object* CreateType(int id,std::string name,vd2d pos,Animation*spr,vd2d scale={1,1},Pixel color=WHITE,int animationSpd=1,bool temp=false,ObjectExtraData data={moveTime:0,moveFreq:0,moveSpd:0})=0; //When the player tries to interact with this object. virtual Interaction Interact()=0; virtual void ChoiceMade(int choice)=0; virtual void DialogClosed()=0; virtual void ShoppingCompleted()=0; virtual void Update()=0; void SetScale(vd2d scale) { this->scale=scale; if (spr!=nullptr) { this->originPoint={(double)spr->sprSize.x/2*scale.x,(spr->sprSize.y-4)*scale.y}; } } vd2d GetScale() { return scale; } vd2d GetPos() { return pos; } //Get where the object was first located. The object will be put back here if we enter the editor. vd2d GetStartPos() { return startPos; } void Move(vd2d move); void SetPos(vd2d pos) { Move(pos-this->pos); } vd2d GetPosWithOrigin() { return GetPos()+originPoint; } //THIS SHOULD ONLY BE SET FOR PLAYER OBJECTS! void _SetStartPos(vd2d pos) { startPos=pos; } bool SmoothMove(vd2d move) { const int wiggleRoom=5; vd2d originPos = {pos.x+originPoint.x,pos.y-1+originPoint.y}; bool moved=false; if (move.x!=0&&!Collision({originPos.x+move.x,originPos.y})) { Move({move.x,0}); originPos.x+=move.x; move.x=0; moved=true; if (move.y==0) { return true; } } if (move.y!=0&&!Collision({originPos.x,originPos.y+move.y})) { Move({0,move.y}); originPos.y+=move.y; move.y=0; return true; } if (moved) { return true; } if (move.x!=0) { for (int i=0;i0) { if (remainingMoveTime>0) { vd2d moveVector; animationSubIndex=(int)moveDir; switch (moveDir) { case Direction::SOUTH:{ moveVector={0,1}; }break; case Direction::EAST:{ moveVector={1,0}; }break; case Direction::WEST:{ moveVector={-1,0}; }break; case Direction::NORTH:{ moveVector={0,-1}; }break; } animated=SmoothMove(moveVector*extraData.moveSpd); remainingMoveTime--; } else { if (rand()%extraData.moveFreq==0) { remainingMoveTime=extraData.moveTime; moveDir=(Direction)(rand()%4); } animated=false; } } } }; class TrashCan_Obj : public Object{ DynamicObject(TrashCan_Obj) Interaction Interact()override{ frameIndex=1; return {{"You dig around the trash can.","Nope! Just looks like plain ol' trash."}};} void DialogClosed()override{}; void ChoiceMade(int choice)override{}; void ShoppingCompleted()override{}; void Update()override{}; }; extern int MESSAGE_BOX_DIALOG_ANSWER; extern std::array GAME_FLAGS; extern int GAME_STATE; extern void SetupShop(Object*shopkeeper,std::vector> shopItems); extern std::mapITEMLIST; extern void DisplayMessageBox(std::string targetT); extern int MESSAGE_BOX_DIALOG_ANSWER; extern std::vector PARTY_INVENTORY; extern int ITEM_SELECTION_CURSOR; extern int MONEY; extern std::array PARTY_MEMBER_STATS; extern std::array PARTY_MEMBER_ID; extern std::string A_An(std::string str); extern int GAME_STATE; extern SeasonI*GAME; extern Object*SHOPKEEPER_INTERACTING_WITH; class Shopkeeper_Obj : public Object{ DynamicObject(Shopkeeper_Obj) std::vector> itemList; std::string welcomeMessage="Welcome! Please choose an option: [BUY,SELL,FDSAJIVDSAJ]\ >0:Please take a look through our wares.\ >1:What would you like to sell?\ >2:Are you okay?<"; void Update()override{}; Interaction Interact()override{ if (GAME->GetGameFlag(Flag::SHOPKEER_BRANCH1)) { GAME_FLAGS[(int)Flag::SHOPKEER_BRANCH1]=false; switch (MESSAGE_BOX_DIALOG_ANSWER) { case 2:{ return {{"No! Stay away! [Okay,Ignore]\ >0:Yeah I thought so.\ >1:...<"}}; }break; } } else { return {{welcomeMessage},flag:Flag::SHOPKEER_BRANCH1}; } } void ChoiceMade(int choice)override{ if (GAME->GetGameFlag(Flag::SHOPKEER_BRANCH1)) { if (choice!=2) { GAME->SetGameFlag(Flag::SHOPKEER_BRANCH1,false); } } } void DialogClosed()override{ switch (MESSAGE_BOX_DIALOG_ANSWER) { case 0:{ GAME_STATE = GameState::SHOPKEEPER_MENU; SetupShop( this, { {ITEMLIST[ItemName::EGG],8}, {ITEMLIST[ItemName::COOKIE],4}, {ITEMLIST[ItemName::PIZZA],36}, {ITEMLIST[ItemName::SOME_STUPIDLY_LONG_FEATHER],46}, } ); }break; case 1:{ GAME_STATE = GameState::SHOPKEEPER_SELL_MENU; SHOPKEEPER_INTERACTING_WITH=this; }break; default:{} } } void ShoppingCompleted()override{ DisplayMessageBox("Thanks for shopping with us! Have a great day."); }; }; #endif