Files
sdl2-snakegame/SnakeGame.cpp
2019-07-05 18:24:10 +02:00

561 lines
16 KiB
C++

/*
* snakeGame.cpp
*
* Created on: Apr 11, 2019
* Author: dennisgunia
*/
#include "SnakeGame.hpp"
static void soundPlayloop(){
Mix_PlayMusic(AssetLoader::AL_LoadMusic(assetPath,"8bit Stage1 Loop.wav"),-1);
}
SnakeGame::SnakeGame(size Size, SDL_Renderer *render, SDL_Window *window) {
this->Size = Size;
this->render = render;
this->player = {Size.X/2,Size.Y/2,true};
this->direction = dir_stop;
this->taillen = 0;
this->foodcount = 0;
this->lastWasHit = false;
this->speed = 750;
this->coins = 0;
this->score = 0;
this->highscore = 0;
this->activePowerup = foodtype::powerup_none;
this->tailPhase = -1;
this->countdownPowerup = -1;
this->tmpOffset = {20,20};
this->stars = AssetLoader::AL_LoadTexture(assetPath,"stars.png",this->render);
this->powerup[foodtype::powerup_potion] =AssetLoader::AL_LoadTexture(assetPath,"potion_A_green_full.png",this->render);
//this->font=TTF_OpenFont("/home/dennisgunia/eclipse-workspace-cpp/Desktop/Seven Oh Ess 8x8 Monospaced.ttf", 29);
this->font=AssetLoader::AL_LoadFont(assetPath,"Seven Oh Ess 8x8 Monospaced.ttf", 29);
//this->snd_click = Mix_LoadWAV("/home/dennisgunia/eclipse-workspace-cpp/Desktop/Misc01.wav");
//this->snd_death = Mix_LoadWAV("/home/dennisgunia/eclipse-workspace-cpp/Desktop/Death02.wav");
//this->snd_powerup = Mix_LoadWAV("/home/dennisgunia/eclipse-workspace-cpp/Desktop/Misc05.wav");
this->snd_click = AssetLoader::AL_LoadChunk(assetPath,"Misc01.wav");
this->snd_death = AssetLoader::AL_LoadChunk(assetPath,"Death02.wav");
this->snd_powerup = AssetLoader::AL_LoadChunk(assetPath,"Misc05.wav");
SDL_SetWindowSize(window,this->Size.scale * this->Size.X + offset_left + offset_right,this->Size.scale * this->Size.Y + offset_top + offset_bottom);
Mix_VolumeChunk(snd_click, 35);
//this->tx_food[foodtype::type_coin_std].setupAnimatedTexture("/home/dennisgunia/eclipse-workspace-cpp/Desktop/coin", 14,this->render );
//this->tx_food[foodtype::type_coin_death].setupAnimatedTexture("/home/dennisgunia/eclipse-workspace-cpp/Desktop/coinDeath", 14, this->render );
//this->tx_food[foodtype::type_powerup].setupAnimatedTexture("/home/dennisgunia/eclipse-workspace-cpp/Desktop/sparkle", 34, this->render );
this->tx_food[foodtype::type_coin_std].setupAnimatedTexture(AssetLoader::AL_AssetURL(assetPath,"coin"), 14,this->render );
this->tx_food[foodtype::type_coin_death].setupAnimatedTexture(AssetLoader::AL_AssetURL(assetPath,"coinDeath"), 14, this->render );
this->tx_food[foodtype::type_powerup].setupAnimatedTexture(AssetLoader::AL_AssetURL(assetPath,"sparkle"), 34, this->render );
this->Music_normal = AssetLoader::AL_LoadMusic(assetPath,"8bit Stage1 Loop.wav");
this->Music_powerup1 = AssetLoader::AL_LoadMusic(assetPath,"8bit Stage4 Loop.wav");
text_animations = SnakeTextAnimationHelper();
text_animations.setRenderer(this->render);
}
void SnakeGame::doAnimation(){
this->text_animations.doAnimations();
this->tx_food[0].doAnimationStep();
this->tx_food[1].doAnimationStep();
this->tx_food[2].doAnimationStep();
if (this->tailPhase >= 0){
this->tailPhase += 20;
if (this->tailPhase >= 180){
this->tailPhase = 0;
}
}
if (this->countdownPowerup > 0){
this->countdownPowerup --;
}else if (this->countdownPowerup == 0){
this->countdownPowerup --;
this->activePowerup = foodtype::powerup_none;
this->tailPhase = -1;
this->countdownPowerup = -1;
Mix_PlayMusic(this->Music_normal, -1);
}
}
SnakeGame::~SnakeGame() {
// TODO Auto-generated destructor stub
}
void SnakeGame::shiftTailArray () {
if (this->taillen > 0){
// aktuelle länge bestimmen
int len =this->taillen;
// array verschieben
if (this->lastWasHit ){
//len++;
this->lastWasHit = false;
}
for ( int index = len ; index > 0; index--){
this->tail[index] = this->tail[index - 1];
}
// aktuelle position hinzufügen
this->tail[0] = this->player;
this->tail[0].enabled = true;
}
}
void SnakeGame::addTailArray(){
this->taillen++;
this->lastWasHit = true;
this->speed = this->speed - (this->speed * 0.10);
if (this->speed < 50){
this->speed = 50;
}
Mix_PlayChannel(-1, snd_click, 0);
}
void SnakeGame::setDirection(int direction){
this->direction = direction;
if (direction == dir_stop){
Mix_PlayChannel(-1, snd_death, 0);
}
}
void SnakeGame::convertToCoins(){
for ( int index3 = 0 ; index3 < 500 ; index3++){
this->food[index3].X = 0;
this->food[index3].Y = 0;
this->food[index3].enabled = false;
this->food[index3].type = foodtype::type_coin_std;
this->food[index3].offset = 0;
}
int arrayLen = this->taillen;
for ( int index = 0 ; index < arrayLen ; index++){
if (this->tail[index].enabled){
this->foodcount++;
for ( int index2 = 0 ; index2 < 50 ; index2++){
if (this->food[index2].enabled == false){
this->food[index2].X = this->tail[index].X;
this->food[index2].Y = this->tail[index].Y;
this->food[index2].enabled = true;
this->food[index2].type = foodtype::type_coin_death;
//this->food[index2].offset = this->tx_food[foodtype::type_coin_death].getOffset();
this->food[index2].powerupType = foodtype::powerup_none;
this->tail[index].enabled = false;
break;
}
}
}
else
{
break;
}
}
}
bool SnakeGame::doSnakeStep(){
switch(this->direction){
case dir_up:
this->shiftTailArray();
this->player.Y -= 1;
break;
case dir_down:
this->shiftTailArray();
this->player.Y += 1;
break;
case dir_left:
this->shiftTailArray();
this->player.X -= 1;
break;
case dir_right:
this->shiftTailArray();
this->player.X += 1;
break;
default:
return true;
}
for ( int index = 0 ; index < 500 ; index++){
if (this->food[index].enabled && (this->food[index].X == this->player.X)&& (this->food[index].Y == this->player.Y)) {
std::stringstream popup;
int tx = this->food[index].X * this->Size.scale + 2 + offset_left;
int ty = (this->food[index].Y - 1) * this->Size.scale + 2 + offset_top;
if (this->food[index].powerupType == foodtype::powerup_none){
this->addTailArray();
if (this->food[index].type == foodtype::type_coin_std){
this->score += 20;
popup << "+20" ;
}else{
this->score += 5;
popup << "+5" ;
}
if (this->highscore < this->score){
this->highscore = this->score;
}
this->coins++;
this->food[index].enabled = false;
this->text_animations.startAnimation(tx + this->tmpOffset.X,ty+ this->tmpOffset.Y,popup.str(),{64,255,64,0},this->font);
}else{
if (this->activePowerup == foodtype::powerup_none){
this->score += 150;
popup << "+150" ;
this->text_animations.startAnimation(tx + this->tmpOffset.X,ty+ this->tmpOffset.Y,popup.str(),{64,255,64,0},this->font);
this->food[index].enabled = false;
this->activePowerup = this->food[index].powerupType;
Mix_PlayChannel(-1, snd_powerup, 0);
this->tailPhase = 0;
this->countdownPowerup = 500;
Mix_PlayMusic(this->Music_powerup1, -1);
}
}
}
}
if (this->player.X < 0){
this->player.X = this->Size.X - 1;
if (this->tailPhase < 0){return false;};
}
if (this->player.Y < 0){
this->player.Y = this->Size.Y - 1;
if (this->tailPhase < 0){return false;};
}
if (this->player.X > this->Size.X - 1){
this->player.X = 0;
if (this->tailPhase < 0){return false;};
}
if (this->player.Y > this->Size.Y - 1){
this->player.Y = 0;
if (this->tailPhase < 0){return false;};
}
for ( int index = 0 ; index < this->taillen + 1 ; index++){
if (this->tail[index].enabled && (this->tail[index].X == this->player.X)&& (this->tail[index].Y == this->player.Y)){
if (this->tailPhase < 0){return false;};
}
}
return true;
}
void SnakeGame::generateFood () {
int foodtype = rand()%40;
if (foodtype < 30)
{
this->foodcount++;
for ( int index = 0 ; index < 500 ; index++){
if (this->food[index].enabled == false){
this->food[index].X = rand()%this->Size.X;
this->food[index].Y = rand()%this->Size.Y;
this->food[index].enabled = true;
this->food[index].type = foodtype::type_coin_std;
this->food[index].offset = this->tx_food[foodtype::type_coin_std].getOffset();
this->food[index].powerupType = foodtype::powerup_none;
break;
}
}
}
else if (foodtype < 40)
{
this->foodcount++;
for ( int index = 0 ; index < 500 ; index++){
if (this->food[index].enabled == false){
this->food[index].X = rand()%this->Size.X;
this->food[index].Y = rand()%this->Size.Y;
this->food[index].enabled = true;
this->food[index].type = foodtype::type_powerup;
this->food[index].offset = this->tx_food[foodtype::type_powerup].getOffset();
this->food[index].powerupType = foodtype::powerup_potion;
break;
}
}
}
}
int SnakeGame::getTimeOut(){
return this->speed;
}
void SnakeGame::drawHud(){
}
void SnakeGame::drawGame(){
coordinate playerInRender = {20,20};
SDL_Rect cell;
SDL_GetRendererOutputSize(this->render,&cell.w,&cell.h);
playerInRender.X = this->player.X * this->Size.scale + 2 + offset_left + this->tmpOffset.X;
playerInRender.Y = this->player.Y * this->Size.scale + 2 + offset_top + this->tmpOffset.Y;
int steps = 4;
if(playerInRender.X < cell.w * 0.2){
this->tmpOffset.X += this->Size.scale /steps;
}
if(playerInRender.X > cell.w * 0.8){
this->tmpOffset.X -= this->Size.scale /steps;
}
if(playerInRender.Y < cell.h * 0.2){
this->tmpOffset.Y += this->Size.scale /steps;
}
if(playerInRender.Y > cell.h * 0.8){
this->tmpOffset.Y -= this->Size.scale/steps;
}
SDL_SetRenderDrawColor(this->render, SNAKE_BACKGROUND);
SDL_RenderClear(this->render);
this->drawStarfield(this->tmpOffset.X / 8,this->tmpOffset.Y / 8);
for (int posY = 0 ; posY < this->Size.Y + 1;posY++){
SDL_Rect cell;
cell.x = offset_left - 32 + offset_left - 32 + this->tmpOffset.X;
cell.y = posY * this->Size.scale+ offset_top - 32 + this->tmpOffset.Y;
cell.h = this->Size.scale;
cell.w = this->Size.scale;
SDL_SetRenderDrawColor(this->render, SNAKE_BORDER);
SDL_RenderDrawRects(this->render,&cell,1);
cell.x = (this->Size.X + 1) * this->Size.scale + offset_left - 32 + this->tmpOffset.X;
cell.y = posY * this->Size.scale+ offset_top - 32 + this->tmpOffset.Y;
SDL_RenderDrawRects(this->render,&cell,1);
}
for (int posX = 0 ; posX < this->Size.X + 1;posX++){
SDL_Rect cell;
cell.x = posX * this->Size.scale + offset_left - 32 + this->tmpOffset.X;
cell.y = offset_top - 32 + this->tmpOffset.Y;
cell.h = this->Size.scale;
cell.w = this->Size.scale;
SDL_SetRenderDrawColor(this->render, SNAKE_BORDER);
SDL_RenderDrawRects(this->render,&cell,1);
cell.x = posX * this->Size.scale + offset_left - 32 + this->tmpOffset.X;
cell.y = (this->Size.Y +1) * this->Size.scale + offset_top - 32 + this->tmpOffset.Y;
SDL_RenderDrawRects(this->render,&cell,1);
}
SDL_SetRenderDrawColor(this->render, SNAKE_PLAYER);
cell.x = this->player.X * this->Size.scale + 2 + offset_left + this->tmpOffset.X;
cell.y = this->player.Y * this->Size.scale + 2 + offset_top + this->tmpOffset.Y;
cell.h = this->Size.scale - 4;
cell.w = this->Size.scale - 4;
if (this->tailPhase >= 0){
int phase = (this->tailPhase)%180;
SDL_SetRenderDrawColor(this->render, 255,128,000,(sin(phase*PI/180)*255));
}
SDL_RenderFillRects(this->render,&cell,1);
int arrayLen = taillen;
SDL_SetRenderDrawColor(this->render, SNAKE_TAIL);
SDL_Rect cell1;
cell1.h = this->Size.scale - 4;
cell1.w = this->Size.scale - 4;
SDL_SetRenderDrawColor(this->render, SNAKE_TAIL);
for ( int index = 0 ; index < arrayLen ; index++){
if (this->tail[index].enabled){
cell1.x = this->tail[index].X * this->Size.scale + 2 + offset_left + this->tmpOffset.X;
cell1.y = this->tail[index].Y * this->Size.scale + 2 + offset_top + this->tmpOffset.Y;
if (this->tailPhase >= 0){
int offset = (index + 1) * 20;
int phase = (offset + this->tailPhase)%180;
SDL_SetRenderDrawColor(this->render, 255,255,255,(sin(phase*PI/180)*255));
}
SDL_RenderFillRects(this->render,&cell1,1);
}
else
{
break;
}
}
//SDL_SetRenderDrawColor(this->render, SNAKE_FOOD);
for ( int index = 0 ; index < 500 ; index++){
if (this->food[index].enabled){
//SDL_Rect cell1;
cell1.x = this->food[index].X * this->Size.scale + 2 + offset_left + this->tmpOffset.X;
cell1.y = this->food[index].Y * this->Size.scale + 2 + offset_top + this->tmpOffset.Y;
cell1.h = this->Size.scale - 4;
cell1.w = this->Size.scale - 4;
//SDL_RenderFillRects(this->render,&cell1,1);
if(this->food[index].type == foodtype::type_powerup){
switch(this->food[index].powerupType) {
case foodtype::powerup_potion:
SDL_RenderCopy(this->render, this->powerup[this->food[index].powerupType], NULL, &cell1);
break;
default:
break;
}
}
//SDL_RenderCopy(this->render, this->coin[(this->coinStep + this->food[index].offset)%14][this->food[index].type -1], NULL, &cell1);
//printf("[debug] getting texture for %i with animIndex %i - pointer to %i\n",index,this->food[index].offset,this->tx_food[this->food[index].type].getTexture(this->food[index].offset));
SDL_RenderCopy(this->render, this->tx_food[this->food[index].type].getTexture(this->food[index].offset), NULL, &cell1);
}
}
SDL_Color color={255,255,255,255}, bgcolor={0,0,0,0};
std::stringstream ss;
ss << "score:" << this->score;
std::string s = ss.str();
char * tab2 = new char [s.length()+1];
strcpy (tab2, s.c_str());
// text score
SDL_Surface *temp_surface = TTF_RenderText_Blended(this->font,tab2,color);
SDL_Texture *tx_score = SDL_CreateTextureFromSurface(this->render, temp_surface);
SDL_Rect txt_score_dest;
SDL_QueryTexture(tx_score, NULL, NULL, &txt_score_dest.w, &txt_score_dest.h);
txt_score_dest.x = 10;
txt_score_dest.y = 1;
SDL_RenderCopy(this->render, tx_score, NULL, &txt_score_dest);
SDL_DestroyTexture(tx_score);
SDL_FreeSurface(temp_surface);
// text highscore
std::stringstream ss2;
ss2 << "highscore:" << this->highscore;
std::string s2 = ss2.str();
char * tab3 = new char [s2.length()+1];
strcpy (tab3, s2.c_str());
temp_surface = TTF_RenderText_Blended(this->font,tab3,color);
SDL_Texture *tx_highscore = SDL_CreateTextureFromSurface(this->render, temp_surface);
txt_score_dest.x = txt_score_dest.w + 50;
SDL_QueryTexture(tx_highscore, NULL, NULL, &txt_score_dest.w, &txt_score_dest.h);
SDL_RenderCopy(this->render, tx_highscore, NULL, &txt_score_dest);
SDL_DestroyTexture(tx_highscore);
SDL_FreeSurface(temp_surface);
// text coins
std::stringstream ss3;
ss3 << "coins:" << this->coins;
std::string s3 = ss3.str();
char * tab4 = new char [s3.length()+1];
strcpy (tab4, s3.c_str());
temp_surface = TTF_RenderText_Blended(this->font,tab4,color);
SDL_Texture *tx_coins = SDL_CreateTextureFromSurface(this->render, temp_surface);
txt_score_dest.x =txt_score_dest.x + txt_score_dest.w + 50;
SDL_QueryTexture(tx_coins, NULL, NULL, &txt_score_dest.w, &txt_score_dest.h);
SDL_RenderCopy(this->render, tx_coins, NULL, &txt_score_dest);
SDL_DestroyTexture(tx_coins);
SDL_FreeSurface(temp_surface);
this->text_animations.renderAnimations();
SDL_RenderPresent(this->render);
}
int SnakeGame::getScore(){
return this->score;
}
void SnakeGame::startGame(){
Mix_PlayMusic(AssetLoader::AL_LoadMusic(assetPath,"8bit Stage1 Intro.wav"), 1);
Mix_HookMusicFinished(soundPlayloop);
this->taillen = 0;
this->speed = 300;// 750;
this->player = {this->Size.X/2,this->Size.Y/2,true};
this->score = 0;
this->coins = 0;
this->activePowerup = foodtype::powerup_none;
this->tailPhase = -1;
SDL_Rect cell;
SDL_GetRendererOutputSize(this->render,&cell.w,&cell.h);
this->tmpOffset.X = (cell.w/2) - (((this->player.X)+2) * this->Size.scale);
this->tmpOffset.Y = (cell.h/2) - (((this->player.Y)+2) * this->Size.scale);
}
void SnakeGame::drawStarfield(int x, int y){
SDL_Rect cell;
SDL_GetRendererOutputSize(this->render,&cell.w,&cell.h);
x = x%cell.w;
y = y%cell.h;
cell.x = x;
cell.y = y;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x;
cell.y = y - cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x;
cell.y = y + cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x + cell.w;
cell.y = y - cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x+ cell.w;
cell.y = y + cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x - cell.w;
cell.y = y - cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x - cell.w;
cell.y = y + cell.h;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x + cell.w;
cell.y = y;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
cell.x = x - cell.w;
cell.y = y;
SDL_RenderCopy(this->render, this->stars, NULL, &cell);
}