|
|
|
@ -1,5 +1,7 @@ |
|
|
|
|
#define OLC_PGE_APPLICATION |
|
|
|
|
#include "olcPixelGameEngine.h" |
|
|
|
|
#define OLC_PGEX_QUICKGUI |
|
|
|
|
#include "olcPGEX_QuickGUI.h" |
|
|
|
|
|
|
|
|
|
using namespace olc; |
|
|
|
|
|
|
|
|
@ -33,14 +35,40 @@ class CardGameRef : public olc::PixelGameEngine |
|
|
|
|
DRAWFOUR, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
enum GameState{ |
|
|
|
|
AIPICK, |
|
|
|
|
DRAW, |
|
|
|
|
PICK, |
|
|
|
|
RESULT, |
|
|
|
|
VICTORY |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct Rect{ |
|
|
|
|
vf2d pos; |
|
|
|
|
vf2d size; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct Card{ |
|
|
|
|
Color col; |
|
|
|
|
CardValue val; |
|
|
|
|
bool faceDown=false; |
|
|
|
|
Card(Color col,CardValue val,bool faceDown=false) |
|
|
|
|
:col(col),val(val),faceDown(faceDown){} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
typedef std::vector<Card> Hand; |
|
|
|
|
typedef std::vector<Card> CardPile; |
|
|
|
|
|
|
|
|
|
std::vector<Decal*>IMAGES; |
|
|
|
|
Rect cardBorder,cardBack,skip,reverse,wild,drawTwo,drawFour; |
|
|
|
|
Decal*cardTilesheet; |
|
|
|
|
Hand playerHand; |
|
|
|
|
std::vector<Hand>opponentHands; |
|
|
|
|
CardPile startingPile,drawPile,discardPile; |
|
|
|
|
QuickGUI::Manager playerPickMenu; |
|
|
|
|
QuickGUI::Button *TwoPlayerButton,*ThreePlayerButton,*FourPlayerButton; |
|
|
|
|
|
|
|
|
|
GameState state=AIPICK; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
CardGameRef() |
|
|
|
@ -49,23 +77,25 @@ public: |
|
|
|
|
sAppName = "Uno!"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
bool OnUserCreate() override |
|
|
|
|
{ |
|
|
|
|
// Called once at the start, so create things here
|
|
|
|
|
|
|
|
|
|
cardBorder={{0,0},{48,64}}; |
|
|
|
|
cardBack={{48,0},{48,64}}; |
|
|
|
|
skip={{0,64},{32,32}}; |
|
|
|
|
reverse={{32,64},{32,32}}; |
|
|
|
|
wild={{64,64},{32,32}}; |
|
|
|
|
drawTwo={{96,32},{32,32}}; |
|
|
|
|
drawFour={{96,64},{32,32}}; |
|
|
|
|
|
|
|
|
|
cardTilesheet = new Decal(new Sprite("CardGame.png")); |
|
|
|
|
void ReshuffleDrawPile(){ |
|
|
|
|
while(discardPile.size()>1){ |
|
|
|
|
int randomCardIndex=rand()%(discardPile.size()-1); |
|
|
|
|
drawPile.push_back(discardPile[randomCardIndex]); |
|
|
|
|
discardPile.erase(discardPile.begin()+randomCardIndex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
SetPixelMode(Pixel::MASK); |
|
|
|
|
void DrawCard(Hand&hand){ |
|
|
|
|
if(drawPile.size()==0){ |
|
|
|
|
ReshuffleDrawPile(); |
|
|
|
|
} |
|
|
|
|
if(drawPile.size()>0){//It's possible all the cards in the game may have been drawn,
|
|
|
|
|
hand.push_back(drawPile.back()); |
|
|
|
|
drawPile.erase(drawPile.end()-1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void GenerateCards(){ |
|
|
|
|
for(int color=0;color<4;color++){ |
|
|
|
|
for(int numb=0;numb<13;numb++){ |
|
|
|
|
Decal*card=new Decal(new Sprite(48,64)); |
|
|
|
@ -73,43 +103,43 @@ public: |
|
|
|
|
DrawPartialSprite({0,0},cardTilesheet->sprite,cardBack.pos,cardBack.size); |
|
|
|
|
Pixel targetCol; |
|
|
|
|
switch(Color(color)){ |
|
|
|
|
case Color::RED:{ |
|
|
|
|
targetCol={192,0,0}; |
|
|
|
|
}break; |
|
|
|
|
case Color::GREEN:{ |
|
|
|
|
targetCol={0,192,0}; |
|
|
|
|
}break; |
|
|
|
|
case Color::BLUE:{ |
|
|
|
|
targetCol={0,0,192}; |
|
|
|
|
}break; |
|
|
|
|
case Color::YELLOW:{ |
|
|
|
|
targetCol={192,192,0}; |
|
|
|
|
}break; |
|
|
|
|
case Color::RED:{ |
|
|
|
|
targetCol={192,0,0}; |
|
|
|
|
}break; |
|
|
|
|
case Color::GREEN:{ |
|
|
|
|
targetCol={0,192,0}; |
|
|
|
|
}break; |
|
|
|
|
case Color::BLUE:{ |
|
|
|
|
targetCol={0,0,192}; |
|
|
|
|
}break; |
|
|
|
|
case Color::YELLOW:{ |
|
|
|
|
targetCol={192,192,0}; |
|
|
|
|
}break; |
|
|
|
|
} |
|
|
|
|
for(int y=0;y<64;y++){ |
|
|
|
|
for(int x=0;x<48;x++){ |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{235,235,235}){ |
|
|
|
|
card->sprite->SetPixel({x,y},targetCol); |
|
|
|
|
} else
|
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},targetCol/2); |
|
|
|
|
} |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},targetCol/2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DrawPartialSprite({0,0},cardTilesheet->sprite,cardBorder.pos,cardBorder.size); |
|
|
|
|
switch(numb){ |
|
|
|
|
case SKIP:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-skip.size/2,cardTilesheet->sprite,skip.pos,skip.size); |
|
|
|
|
}break; |
|
|
|
|
case REVERSE:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-reverse.size/2,cardTilesheet->sprite,reverse.pos,reverse.size); |
|
|
|
|
}break; |
|
|
|
|
case DRAWTWO:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-drawTwo.size/2,cardTilesheet->sprite,drawTwo.pos,drawTwo.size); |
|
|
|
|
}break; |
|
|
|
|
default:{ |
|
|
|
|
DrawString(vi2d{24,32}-GetTextSize(std::to_string(numb))/2*3,std::to_string(numb),WHITE,3); |
|
|
|
|
} |
|
|
|
|
case SKIP:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-skip.size/2,cardTilesheet->sprite,skip.pos,skip.size); |
|
|
|
|
}break; |
|
|
|
|
case REVERSE:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-reverse.size/2,cardTilesheet->sprite,reverse.pos,reverse.size); |
|
|
|
|
}break; |
|
|
|
|
case DRAWTWO:{ |
|
|
|
|
DrawPartialSprite(vi2d{24,32}-drawTwo.size/2,cardTilesheet->sprite,drawTwo.pos,drawTwo.size); |
|
|
|
|
}break; |
|
|
|
|
default:{ |
|
|
|
|
DrawString(vi2d{24,32}-GetTextSize(std::to_string(numb))/2*3,std::to_string(numb),WHITE,3); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
card->Update(); |
|
|
|
|
IMAGES.push_back(card); |
|
|
|
@ -120,45 +150,97 @@ public: |
|
|
|
|
SetDrawTarget(card->sprite); |
|
|
|
|
DrawPartialSprite({0,0},cardTilesheet->sprite,cardBack.pos,cardBack.size); |
|
|
|
|
switch(i){ |
|
|
|
|
case 0:{//Wild Card
|
|
|
|
|
DrawPartialSprite(vi2d{24,32}-wild.size/2,cardTilesheet->sprite,wild.pos,wild.size); |
|
|
|
|
}break; |
|
|
|
|
case 1:{//Draw Four Card
|
|
|
|
|
DrawPartialSprite(vi2d{24,32}-drawFour.size/2,cardTilesheet->sprite,drawFour.pos,drawFour.size); |
|
|
|
|
}break; |
|
|
|
|
case 0:{//Wild Card
|
|
|
|
|
DrawPartialSprite(vi2d{24,32}-wild.size/2,cardTilesheet->sprite,wild.pos,wild.size); |
|
|
|
|
}break; |
|
|
|
|
case 1:{//Draw Four Card
|
|
|
|
|
DrawPartialSprite(vi2d{24,32}-drawFour.size/2,cardTilesheet->sprite,drawFour.pos,drawFour.size); |
|
|
|
|
}break; |
|
|
|
|
} |
|
|
|
|
switch(i){//Two cards have black backs.
|
|
|
|
|
case 0: //Wild Card
|
|
|
|
|
case 1:{//Draw Four Card
|
|
|
|
|
for(int y=0;y<64;y++){ |
|
|
|
|
for(int x=0;x<48;x++){ |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{235,235,235}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK); |
|
|
|
|
} else
|
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK/2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
case 0: //Wild Card
|
|
|
|
|
case 1:{//Draw Four Card
|
|
|
|
|
for(int y=0;y<64;y++){ |
|
|
|
|
for(int x=0;x<48;x++){ |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{235,235,235}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK); |
|
|
|
|
} else
|
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK/2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}break; |
|
|
|
|
default:{//This is the face down card.
|
|
|
|
|
for(int y=0;y<64;y++){ |
|
|
|
|
for(int x=0;x<48;x++){ |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{235,235,235}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK); |
|
|
|
|
} else
|
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},{102,0,64}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}break; |
|
|
|
|
default:{//This is the face down card.
|
|
|
|
|
for(int y=0;y<64;y++){ |
|
|
|
|
for(int x=0;x<48;x++){ |
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{235,235,235}){ |
|
|
|
|
card->sprite->SetPixel({x,y},BLACK); |
|
|
|
|
} else
|
|
|
|
|
if(card->sprite->GetPixel(x,y)==Pixel{103,103,103}){ |
|
|
|
|
card->sprite->SetPixel({x,y},{102,0,64}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DrawPartialSprite({0,0},cardTilesheet->sprite,cardBorder.pos,cardBorder.size); |
|
|
|
|
card->Update(); |
|
|
|
|
IMAGES.push_back(card); |
|
|
|
|
} |
|
|
|
|
SetDrawTarget(nullptr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
bool OnUserCreate() override |
|
|
|
|
{ |
|
|
|
|
// Called once at the start, so create things here
|
|
|
|
|
|
|
|
|
|
cardBorder={{0,0},{48,64}}; |
|
|
|
|
cardBack={{48,0},{48,64}}; |
|
|
|
|
skip={{0,64},{32,32}}; |
|
|
|
|
reverse={{32,64},{32,32}}; |
|
|
|
|
wild={{64,64},{32,32}}; |
|
|
|
|
drawTwo={{96,32},{32,32}}; |
|
|
|
|
drawFour={{96,64},{32,32}}; |
|
|
|
|
|
|
|
|
|
cardTilesheet = new Decal(new Sprite("CardGame.png")); |
|
|
|
|
|
|
|
|
|
SetPixelMode(Pixel::MASK); |
|
|
|
|
|
|
|
|
|
GenerateCards(); |
|
|
|
|
|
|
|
|
|
//2 of each basic card. (80)
|
|
|
|
|
//2 of each special card. (24)
|
|
|
|
|
//8 Wild Cards (8)
|
|
|
|
|
//4 Draw Four Cards (4)
|
|
|
|
|
for(int color=0;color<4;color++){ |
|
|
|
|
for(int value=0;value<13;value++){ |
|
|
|
|
startingPile.emplace_back(Color(color),CardValue(value),true); |
|
|
|
|
startingPile.emplace_back(Color(color),CardValue(value),true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for(int i=0;i<8;i++){ |
|
|
|
|
startingPile.emplace_back(Color::RED,WILD,true); |
|
|
|
|
} |
|
|
|
|
for(int i=0;i<4;i++){ |
|
|
|
|
startingPile.emplace_back(Color::RED,DRAWFOUR,true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
while(startingPile.size()>0){ |
|
|
|
|
int randomCardIndex=rand()%startingPile.size(); |
|
|
|
|
drawPile.push_back(startingPile[randomCardIndex]); |
|
|
|
|
startingPile.erase(startingPile.begin()+randomCardIndex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TwoPlayerButton=new QuickGUI::Button(playerPickMenu,"1 Opponent",{128-42,20},{84,40}); |
|
|
|
|
ThreePlayerButton=new QuickGUI::Button(playerPickMenu,"2 Opponents",{128-42,94},{84,40}); |
|
|
|
|
FourPlayerButton=new QuickGUI::Button(playerPickMenu,"3 Opponents",{128-42,168},{84,40}); |
|
|
|
|
|
|
|
|
|
for(int i=0;i<7;i++){ |
|
|
|
|
DrawCard(playerHand); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -175,15 +257,36 @@ public: |
|
|
|
|
|
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override |
|
|
|
|
{ |
|
|
|
|
// Called once per frame, draws random coloured pixels
|
|
|
|
|
float scale=256.f/(13*48); |
|
|
|
|
for(int color=0;color<4;color++){ |
|
|
|
|
for(int value=0;value<13;value++){ |
|
|
|
|
DrawRotatedDecal(vf2d{value*scale*48,color*scale*64}+scale*vf2d{24,32}/2,GetCardImage(Color(color),CardValue(value)),0,scale*vf2d{24,32},{scale,scale});
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (int value=0;value<3;value++){ |
|
|
|
|
DrawRotatedDecal(vf2d{value*scale*48,5*scale*64}+scale*vf2d{24,32}/2,GetCardImage(Color::RED,CardValue(value+13),value==2?true:false),0,scale*vf2d{24,32},{scale,scale});
|
|
|
|
|
switch(state){ |
|
|
|
|
case AIPICK:{ //Player Pick Menu
|
|
|
|
|
playerPickMenu.Update(this); |
|
|
|
|
|
|
|
|
|
bool buttonClicked=false; |
|
|
|
|
int AICount=0; |
|
|
|
|
if(TwoPlayerButton->bPressed){ |
|
|
|
|
buttonClicked=true; |
|
|
|
|
AICount=1; |
|
|
|
|
} |
|
|
|
|
if(ThreePlayerButton->bPressed){ |
|
|
|
|
buttonClicked=true; |
|
|
|
|
AICount=2; |
|
|
|
|
} |
|
|
|
|
if(FourPlayerButton->bPressed){ |
|
|
|
|
buttonClicked=true; |
|
|
|
|
AICount=3; |
|
|
|
|
} |
|
|
|
|
if(buttonClicked){ |
|
|
|
|
for(int ai=0;ai<AICount;ai++){ |
|
|
|
|
Hand opponent; |
|
|
|
|
for (int i=0;i<7;i++){ |
|
|
|
|
DrawCard(opponent); |
|
|
|
|
} |
|
|
|
|
opponentHands.push_back(opponent); |
|
|
|
|
} |
|
|
|
|
state=GameState::DRAW; |
|
|
|
|
} |
|
|
|
|
playerPickMenu.DrawDecal(this); |
|
|
|
|
}break; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|