/* ************************************************************************* * SDL-Ball - DX-Ball/Breakout remake with openGL and SDL for Linux Copyright (C) 2008 Jimmy Christensen ( dusted at dusted dot dk ) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . * ************************************************************************* */ struct effect_vars { int type; //Hvilken slags effekt er det? bool coldet; //er der collision detection på denne? bool active; //Er denne effekt aktiv GLfloat col[3]; //Hvilken farve har effekten? GLfloat speed; //Hvor hurtigt bevæger den sig GLfloat spread; //Hvor stor er spredningen (i grader) GLfloat size; //Skalering af elementerne struct pos rect; //rectangular size (for particleField) GLfloat gravity; //Hvor stor er tyndgekraften int num; //Hvor mange elementer er der i den int life; //hvor mange ms lever den? textureClass tex; //Texture int effectId; //unique id for this effect }; class transit_effect_class { private: GLfloat opacity; public: int age; effect_vars vars; void init() { age=0; opacity = 0.0f; var.transition_half_done=0; } void draw() { age += globalTicksSinceLastDraw; if(age < vars.life/2.0) //første halvdel { //Gør solid opacity += vars.life/500.0*globalMilliTicksSinceLastDraw; } else { var.transition_half_done=1; //Gør trans opacity -= vars.life/500.0*globalMilliTicksSinceLastDraw; } glLoadIdentity(); glDisable( GL_TEXTURE_2D ); glTranslatef(0.0, 0.0, -3.0); glColor4f( vars.col[0], vars.col[1], vars.col[2], opacity ); glBegin( GL_QUADS ); glVertex3f(-1.66, 1.25, 0.0); glVertex3f( 1.66, 1.25, 0.0); glVertex3f( 1.66,-1.25, 0.0); glVertex3f(-1.66,-1.25, 0.0); glEnd( ); glEnable( GL_TEXTURE_2D ); } }; class sparkle { public: bool active; GLfloat size; GLfloat ang; int life; int lifeleft; pos p,v; //position og vel effect_vars vars; GLfloat bounce,f; //bounce og friktion sparkle() { bounce=0; f=0; active=1; } void draw() { if(lifeleft > 0) { //sanitize lifeleft -= globalTicksSinceLastDraw; //er vi indenfor skærmen? if(p.x > 1.67 || p.y < -1.7 || p.x < -1.67) { active=0; } v.y -= vars.gravity*globalMilliTicksSinceLastDraw; v.y -= bounce*globalMilliTicksSinceLastDraw; if(v.x < 0) { v.x += f*globalMilliTicksSinceLastDraw; } else { v.x -= f*globalMilliTicksSinceLastDraw; } p.x += v.x * globalMilliTicksSinceLastDraw; p.y += v.y * globalMilliTicksSinceLastDraw; glColor4f(vars.col[0], vars.col[1],vars.col[2], (1.0/(float)life)*(float)lifeleft); GLfloat curSize = size/(float)life * (float)lifeleft; glBindTexture( GL_TEXTURE_2D, vars.tex.prop.texture ); glBegin( GL_QUADS ); glTexCoord2f(0,0); glVertex3f(p.x-curSize,p.y+curSize, 0.0); glTexCoord2f(0,1); glVertex3f(p.x+curSize,p.y+curSize, 0.0); glTexCoord2f(1,1); glVertex3f(p.x+curSize,p.y-curSize, 0.0); glTexCoord2f(1,0); glVertex3f(p.x-curSize,p.y-curSize, 0.0); glEnd( ); } else { active=0; } } void coldet(brick & b) { if(p.x > b.posx-b.width && p.x < b.posx+b.width) { if(p.y > b.posy-b.height && p.y < b.posy+b.height) { //Hvor ramte vi fra? if(p.y < b.posy && !b.n(3)) { //bunden v.y *= -1; //p.y = b.posy-b.height-0.01; } else if(p.y > b.posy && !b.n(2)) { //toppen p.y = b.posy+b.height; if(v.y < 0) { v.y *= -1; v.y /= 2.0; f += 0.01; } } else if(p.x > b.posx && !b.n(1)) { //p.x = b.posx+b.width; //højre if(v.x < 0) v.x *= -1; } else if(p.x < b.posx && !b.n(0)) { //p.x = b.posx-b.width; //venstre if(v.x > 0) v.x *= -1; } } } } void pcoldet(paddle_class & b) { if(p.x > b.posx-b.width && p.x < b.posx+b.width) { if(p.y > b.posy-b.height && p.y < b.posy+b.height) { //Hvor ramte vi fra? if(p.y < b.posy) { //bunden p.y = b.posy-b.height; } if(p.y > b.posy) { //toppen p.y = b.posy+b.height; if(v.y < 0) { v.y *= -1; v.y /= 2.0; f += 0.01; } } else if(p.x > b.posx) { p.x = b.posx+b.width; //højre if(v.x < 0) v.x *= -1; } else if(p.x < b.posx) { p.x = b.posx-b.width; //venstre if(v.x > 0) v.x *= -1; } } } } }; class particleFieldClass { private: int spawnTimeout; //This is used to spawn sparkles at a random time sparkle *sparks; void spawnSpark(int sparkNum); public: struct pos p; struct effect_vars vars; void init(struct effect_vars varsP, struct pos p); void draw(); void move(struct pos p); void coldet(brick & b); void pcoldet(paddle_class & b); ~particleFieldClass(); }; void particleFieldClass::init(struct effect_vars varsP, struct pos spawnPos) { vars=varsP; spawnTimeout=0; vars.active=1; sparks = new sparkle[vars.num]; p=spawnPos; for(int i=0; i < vars.num; i++) { sparks[i].active=1; sparks[i].vars = vars; //NOTE:remove effect_vars from sparkle class? spawnSpark(i); } } particleFieldClass::~particleFieldClass() { delete[] sparks; //free the sparks } void particleFieldClass::spawnSpark(int sparkNum) { GLfloat angle=(RAD/vars.num-1)*(rndflt(vars.num,0.0)); //FIXME: Make this elegant: Choose a random angle (in radients) spawnTimeout = rand()%10; sparks[sparkNum].life = rand()%vars.life; sparks[sparkNum].lifeleft = sparks[sparkNum].life; sparks[sparkNum].v.x = (vars.speed*rndflt(vars.speed*2.0,0.0)) * sin(angle); sparks[sparkNum].v.y = (vars.speed*rndflt(vars.speed*2.0,0.0)) * cos(angle); sparks[sparkNum].size = rndflt(vars.size,0); sparks[sparkNum].p=p; sparks[sparkNum].p.x += rndflt(vars.rect.x, (vars.rect.x/2.0)); sparks[sparkNum].p.y += rndflt(vars.rect.y, (vars.rect.y/2.0)); sparks[sparkNum].active=1; } void particleFieldClass::draw() { spawnTimeout -= globalTicksSinceLastDraw; glLoadIdentity(); glTranslatef(0.0,0.0 , -3.0); int t=0; for(int i=0; iinit(vars, p); break; } } void draw() { //find ud af hvor længe der er gået siden sidst int i; bool stay=0; switch(vars.type) { case FX_SPARKS: glLoadIdentity(); glTranslatef(0.0,0.0 , -3.0); for(i=0; i < vars.num; i++) { if(sparks[i].active) { sparks[i].draw(); stay=1; } } break; case FX_TRANSIT: transit.draw(); if(transit.age <= vars.life) stay=1; break; case FX_PARTICLEFIELD: pf->draw(); if(pf->vars.active) stay=1; break; } //Ugley hack, to prevent transit from flickering when spark effects are deleted. if(var.effectnum != -1 && vars.type != FX_TRANSIT) stay=1; if(!stay) { vars.active=0; switch(vars.type) { case FX_SPARKS: delete[] sparks; //Free sparks again ;) break; case FX_PARTICLEFIELD: delete pf; //remove the particlefield break; } } } void coldet(brick & b) { int i; if(vars.type == FX_SPARKS) { for(i=0; i < vars.num; i++) { if(sparks[i].active) { sparks[i].coldet(b); } } } else if(vars.type == FX_PARTICLEFIELD) { pf->coldet(b); } } void pcoldet(paddle_class & b) { int i; if(vars.type == FX_SPARKS) { for(i=0; i < vars.num; i++) { if(sparks[i].active) { sparks[i].pcoldet(b); } } } else if(vars.type == FX_PARTICLEFIELD) { pf->pcoldet(b); } } }; class effectManager { private: struct effect_vars vars; //denne kopieres over i den næste effekt der bliver spawned int effectId; //ever rising number of a spawned effect. public: listeffects; effectManager() { effects.clear(); effectId=0; } void set(int var, GLfloat val) { switch(var) { case FX_VAR_SPEED: vars.speed = val; break; case FX_VAR_SPREAD: vars.spread = val; break; case FX_VAR_SIZE: vars.size = val; break; case FX_VAR_GRAVITY: vars.gravity = val; } } void set(int var, int val) { switch(var) { case FX_VAR_NUM: vars.num=val; break; case FX_VAR_LIFE: vars.life=val; break; case FX_VAR_TYPE: vars.type=val; break; case FX_VAR_COLDET: vars.coldet=val; break; } } void set(int var, GLfloat r, GLfloat g, GLfloat b) { switch(var) { case FX_VAR_COLOR: vars.col[0] = r; vars.col[1] = g; vars.col[2] = b; break; } } void set(int var, textureClass tex) { switch(var) { case FX_VAR_TEXTURE: vars.tex = tex; break; } } void set(int var, struct pos p) { switch(var) { case FX_VAR_RECTANGLE: vars.rect = p; break; } } //retunerer effektid så der kan checkes om det er aktivt int spawn(pos p) { effectId++; effect_class tempEffect; vars.effectId = effectId; tempEffect.vars = vars; //kopier det over som er sat op tempEffect.init(p); effects.push_back(tempEffect); return(effectId); } void draw() { for(list ::iterator it = effects.begin(); it != effects.end(); ++it) { it->draw(); if(!it->vars.active) { it = effects.erase(it); } } } void coldet(brick & b) { if(b.collide && b.active) { for(list ::iterator it = effects.begin(); it != effects.end(); ++it) { if(it->vars.coldet) it->coldet(b); } } } void pcoldet(paddle_class & b) { for(list::iterator it = effects.begin(); it != effects.end(); ++it) { if(it->vars.coldet) it->pcoldet(b); } } int isActive(int id) { for(list::iterator it = effects.begin(); it != effects.end(); ++it) { if(it->vars.effectId == id && it->vars.active) { return(1); } } return(0); } void kill(int id) { for(list::iterator it = effects.begin(); it != effects.end(); ++it) { if(it->vars.effectId) { it->vars.active=0; } } } }; class glAnnounceMessageClass { private: int age; GLfloat zoom,fade; bool fadedir; public: bool active; int life; string text; int font; glAnnounceMessageClass() { active=1; age=0; fade=0; fadedir=0; zoom=0; } void draw() { GLfloat s; zoom += 4000.0/life * globalMilliTicksSinceLastDraw; if(fadedir) { fade -= 4000.0/life * globalMilliTicksSinceLastDraw; } else { fade += 4000.0/life * globalMilliTicksSinceLastDraw; } glLoadIdentity(); glTranslatef(0.0,0.0,-3.0); glColor4f(1.0,0.0,0.0,fade); s=zoom*0.85; glText->write(text, font, 1, s, 0.0, 0.0); glColor4f(0.0,1.0,0.0,fade); s=zoom*0.90; glText->write(text, font, 1, s, 0.0, 0.0); glColor4f(0.0,0.0,1.0,fade); s=zoom*0.95; glText->write(text, font, 1, s, 0.0, 0.0); glColor4f(1.0,1.0,1.0,fade); s=zoom; glText->write(text, font, 1, s, 0.0, 0.0); age += globalTicksSinceLastDraw; if(age > life*0.50) { fadedir=1; } if(age > life) active=0; } }; #define MAXMSG 10 class glAnnounceTextClass { private: int len; //hvor mange mangler vi at vise list msg; list::iterator it; public: glAnnounceTextClass() { len=0; } void write(const char *text, int ttl, int font) { len++; msg.resize(len); it=msg.end(); --it; it->life=ttl; it->text=text; it->font=font; } void draw() { if(len>0) { it=msg.begin(); if(it->active) { it->draw(); } else { msg.erase(it); len--; } } } };