|
|
|
#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<Object*> OBJECTS;
|
|
|
|
|
|
|
|
struct Interaction{
|
|
|
|
std::vector<std::string> messages={};
|
|
|
|
Item*item=nullptr;
|
|
|
|
Flag flag=Flag::NONE;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ObjectExtraData{
|
|
|
|
int moveTime,moveFreq;
|
|
|
|
double moveSpd;
|
|
|
|
};
|
|
|
|
|
|
|
|
extern int OBJECT_DESTRUCTION_COUNT;
|
|
|
|
|
|
|
|
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:
|
|
|
|
~Object(){
|
|
|
|
OBJECT_DESTRUCTION_COUNT++;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
Pixel collisionData2 = GAME->GetDrawTarget()->GetPixel((int)pos.x-cameraPos.x-1,(int)pos.y-cameraPos.y);
|
|
|
|
if (collisionData!=MAGENTA) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
for (int i=0;i<OBJECTS.size();i++) {
|
|
|
|
if (OBJECTS[i]!=this&&OBJECTS[i]->hasCollision) {
|
|
|
|
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;i<wiggleRoom;i++) { //Search Up.
|
|
|
|
if (!Collision({originPos.x+move.x,originPos.y-i})&&!Collision({originPos.x,originPos.y-1})) {
|
|
|
|
//There is potentially to move up-right/up-left here, so we will do so.
|
|
|
|
Move({0,-1});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i=0;i<wiggleRoom;i++) { //Search Down.
|
|
|
|
if (!Collision({originPos.x+move.x,originPos.y+i})&&!Collision({originPos.x,originPos.y+1})) {
|
|
|
|
//There is potentially to move down-right/down-left here, so we will do so.
|
|
|
|
Move({0,1});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (move.y!=0) {
|
|
|
|
for (int i=0;i<wiggleRoom;i++) { //Search Left.
|
|
|
|
if (!Collision({originPos.x-i,originPos.y+move.y})&&!Collision({originPos.x-1,originPos.y})) {
|
|
|
|
//There is potentially to move up-left/up-right here, so we will do so.
|
|
|
|
Move({-1,0});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i=0;i<wiggleRoom;i++) { //Search Right.
|
|
|
|
if (!Collision({originPos.x+i,originPos.y+move.y})&&!Collision({originPos.x+1,originPos.y})) {
|
|
|
|
//There is potentially to move down-right/down-left here, so we will do so.
|
|
|
|
Move({1,0});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DynamicObject(objName) public:\
|
|
|
|
objName(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,gridy,spr,scale,color,animationSpd,temp,data){};\
|
|
|
|
objName(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})\
|
|
|
|
:Object(id,name,pos,spr,scale,color,animationSpd,temp,data){};\
|
|
|
|
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})override{\
|
|
|
|
return new objName(id,name,pos,spr,scale,color,animationSpd,temp,data);\
|
|
|
|
}
|
|
|
|
|
|
|
|
class Standard_Obj : public Object{
|
|
|
|
DynamicObject(Standard_Obj)
|
|
|
|
Interaction Interact()override{return {};}
|
|
|
|
void DialogClosed()override{};
|
|
|
|
void ChoiceMade(int choice)override{};
|
|
|
|
void ShoppingCompleted()override{};
|
|
|
|
void Update()override{};
|
|
|
|
};
|
|
|
|
|
|
|
|
class NPC_Obj : public Standard_Obj{
|
|
|
|
float remainingMoveTime=0;
|
|
|
|
Direction moveDir=Direction::SOUTH;
|
|
|
|
public:
|
|
|
|
NPC_Obj(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})
|
|
|
|
:Standard_Obj(id,name,pos,spr,scale,color,animationSpd,temp,data){
|
|
|
|
animated=false;
|
|
|
|
frameIndex=0;
|
|
|
|
}
|
|
|
|
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})override{
|
|
|
|
return new NPC_Obj(id,name,pos,spr,scale,color,animationSpd,temp,data);
|
|
|
|
}
|
|
|
|
void Update()override{
|
|
|
|
if (extraData.moveFreq>0) {
|
|
|
|
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<bool,512> GAME_FLAGS;
|
|
|
|
extern int GAME_STATE;
|
|
|
|
extern void SetupShop(Object*shopkeeper,std::vector<std::pair<Item*,int>> shopItems);
|
|
|
|
extern std::map<ItemName,Item*>ITEMLIST;
|
|
|
|
extern void DisplayMessageBox(std::string targetT);
|
|
|
|
extern int MESSAGE_BOX_DIALOG_ANSWER;
|
|
|
|
extern std::vector<Item*> PARTY_INVENTORY;
|
|
|
|
extern int ITEM_SELECTION_CURSOR;
|
|
|
|
extern int MONEY;
|
|
|
|
extern std::array<Entity*,7> PARTY_MEMBER_STATS;
|
|
|
|
extern std::array<int,4> 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<std::pair<Item*,int>> 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
|