/* Copyright (C) 2015-2019 Parallel Realities 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "effects.h" static void setRandomFlameHue(Effect *e); static void setRandomShieldHue(Effect *e); static void resizeDrawList(void); static int pointOnScreen(float x, float y); static float calculateEffectsValue(unsigned int); static AtlasImage *explosionTexture; static AtlasImage *shieldHitTexture; static AtlasImage *haloTexture; static Effect **effectsToDraw; static int drawCapacity; void initEffects(void) { explosionTexture = getAtlasImage("gfx/effects/explosion.png"); shieldHitTexture = getAtlasImage("gfx/effects/shieldHit.png"); haloTexture = getAtlasImage("gfx/effects/halo.png"); drawCapacity = INITIAL_EFFECT_DRAW_CAPACITY; effectsToDraw = malloc(sizeof(Effect*) * drawCapacity); memset(effectsToDraw, 0, sizeof(Effect*) * drawCapacity); } void doEffects(void) { int i, onScreen; Effect *e; Effect *prev = &battle.effectHead; i = 0; memset(effectsToDraw, 0, sizeof(Effect*) * drawCapacity); for (e = battle.effectHead.next ; e != NULL ; e = e->next) { e->x += e->dx; e->y += e->dy; e->a -= (e->type != EFFECT_ECM) ? 1 : 3; e->a = MAX(0, e->a); e->health--; e->size += e->scaleAmount; if (e->health <= 0) { if (e == battle.effectTail) { battle.effectTail = prev; } prev->next = e->next; free(e); e = prev; } else { onScreen = 0; switch (e->type) { case EFFECT_LINE: onScreen = pointOnScreen(e->x - battle.camera.x, e->y - battle.camera.y) || pointOnScreen(e->x + (e->dx * 3) - battle.camera.x, e->y + (e->dy * 3) - battle.camera.y); break; case EFFECT_POINT: onScreen = pointOnScreen(e->x - battle.camera.x, e->y - battle.camera.y); break; default: onScreen = isOnBattleScreen(e->x, e->y, e->size, e->size); break; } if (onScreen) { effectsToDraw[i++] = e; if (i == drawCapacity) { resizeDrawList(); } } } prev = e; } } static int pointOnScreen(float x, float y) { return collision(x, y, 1, 1, 0, 0, app.winWidth, app.winHeight); } static void resizeDrawList(void) { int n; n = drawCapacity + INITIAL_EFFECT_DRAW_CAPACITY; SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Resizing effect draw capacity: %d -> %d", drawCapacity, n); effectsToDraw = resize(effectsToDraw, sizeof(Effect*) * drawCapacity, sizeof(Effect*) * n); drawCapacity = n; } void drawEffects(void) { int i; Effect *e; SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_BLEND); for (i = 0, e = effectsToDraw[i] ; e != NULL ; e = effectsToDraw[++i]) { SDL_SetRenderDrawColor(app.renderer, e->r, e->g, e->b, e->a); if (e->texture != NULL) { SDL_SetTextureBlendMode(e->texture->texture, SDL_BLENDMODE_ADD); } switch (e->type) { case EFFECT_POINT: SDL_RenderDrawPoint(app.renderer, e->x - battle.camera.x, e->y - battle.camera.y); break; case EFFECT_LINE: SDL_RenderDrawLine(app.renderer, e->x - battle.camera.x, e->y - battle.camera.y, e->x + (e->dx * 3) - battle.camera.x, e->y + (e->dy * 3) - battle.camera.y); break; case EFFECT_TEXTURE: setAtlasColor(e->r, e->g, e->b, e->a); blitScaled(e->texture, e->x - battle.camera.x, e->y - battle.camera.y, e->size, e->size, 0); break; case EFFECT_HALO: setAtlasColor(e->r, e->g, e->b, e->a); blitScaled(e->texture, e->x - battle.camera.x - (e->size / 2), e->y - battle.camera.y - (e->size / 2), e->size, e->size, 0); break; case EFFECT_ECM: setAtlasColor(e->r, e->g, e->b, e->a); blitScaled(e->texture, app.winWidth / 2 - (e->size / 2), app.winHeight / 2 - (e->size / 2), e->size, e->size, 0); break; } if (e->texture != NULL) { SDL_SetTextureBlendMode(e->texture->texture, SDL_BLENDMODE_BLEND); } } SDL_SetRenderDrawBlendMode(app.renderer, SDL_BLENDMODE_NONE); } void drawShieldHitEffect(Entity *e) { int size = MAX(e->w, e->h) + 32; SDL_SetTextureBlendMode(shieldHitTexture->texture, SDL_BLENDMODE_BLEND); SDL_SetTextureAlphaMod(shieldHitTexture->texture, e->shieldHit); blitScaled(shieldHitTexture, e->x - battle.camera.x, e->y - battle.camera.y, size, size, 1); } void addBulletHitEffect(int x, int y, int r, int g, int b) { Effect *e; int i; for (i = 0 ; i < 4 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->texture = explosionTexture; e->size = calculateEffectsValue(16); e->x = x; e->y = y; e->dx = (rand() % 25) - (rand() % 25); e->dx *= 0.01; e->dy = (rand() % 25) - (rand() % 25); e->dy *= 0.01; e->r = r; e->g = g; e->b = b; e->a = 64; e->health = calculateEffectsValue(e->a); } } void addSmallFighterExplosion(void) { Effect *e; e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = self->x + (rand() % 16 - rand() % 16); e->y = self->y + (rand() % 16 - rand() % 16); e->texture = explosionTexture; e->size = calculateEffectsValue(32); setRandomFlameHue(e); e->a = 128 + (rand() % 128); e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } void addDebrisFire(int x, int y) { Effect *e; e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = x + (rand() % 8 - rand() % 8); e->y = y + (rand() % 8 - rand() % 8); e->texture = explosionTexture; e->size = calculateEffectsValue(4 + rand() % 12); setRandomFlameHue(e); e->a = rand() % 256; e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } void addSmallExplosion(void) { int i; Effect *e; for (i = 0 ; i < 32 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = self->x; e->y = self->y; e->dx = (rand() % 25) - (rand() % 25); e->dx *= 0.025; e->dy = (rand() % 25) - (rand() % 25); e->dy *= 0.025; e->texture = explosionTexture; e->size = calculateEffectsValue(32 + (rand() % 64)); e->r = 255; setRandomFlameHue(e); e->a = 128 + (rand() % 128); e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } for (i = 0 ; i < 96 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_LINE; e->x = self->x; e->y = self->y; e->dx = rand() % 64 - rand() % 64; e->dx *= 0.1; e->dy = rand() % 64 - rand() % 64; e->dy *= 0.1; e->a = 128; e->health = calculateEffectsValue(e->a); setRandomFlameHue(e); } } void addMineExplosion(void) { int i; Effect *e; for (i = 0 ; i < 64 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = self->x + rand() % 64 - rand() % 64; e->y = self->y + rand() % 64 - rand() % 64; e->texture = explosionTexture; e->size = calculateEffectsValue(64 + (rand() % 64)); e->r = 255; setRandomFlameHue(e); e->a = 32 + (rand() % 192); e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_HALO; e->x = self->x; e->y = self->y; e->size = calculateEffectsValue(64); e->scaleAmount = 6; e->texture = haloTexture; e->r = 255; e->g = 255; e->b = 255; e->a = 128; e->health = calculateEffectsValue(128); } void addLargeExplosion(void) { int i; Effect *e; for (i = 0 ; i < 64 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = self->x + rand() % 255 - rand() % 255; e->y = self->y + rand() % 255 - rand() % 255; e->dx = (rand() % 25) - (rand() % 25); e->dx *= 0.01; e->dy = (rand() % 25) - (rand() % 25); e->dy *= 0.01; e->texture = explosionTexture; e->size = calculateEffectsValue(128 + (rand() % 512)); e->r = 255; setRandomFlameHue(e); e->a = 128 + (rand() % 128); e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_HALO; e->x = self->x; e->y = self->y; e->size = calculateEffectsValue(256); e->scaleAmount = 4; e->texture = haloTexture; e->r = 255; e->g = 255; e->b = 255; e->a = 255; e->health = calculateEffectsValue(255); } void addMissileExplosion(Bullet *b) { int i; Effect *e; for (i = 0 ; i < 8 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = b->x; e->y = b->y; e->dx = (rand() % 25) - (rand() % 25); e->dx *= 0.025; e->dy = (rand() % 25) - (rand() % 25); e->dy *= 0.025; e->texture = explosionTexture; e->size = calculateEffectsValue(32 + (rand() % 64)); e->r = 255; setRandomFlameHue(e); e->a = 128 + (rand() % 128); e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } for (i = 0 ; i < 24 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_LINE; e->x = b->x; e->y = b->y; e->dx = rand() % 64 - rand() % 64; e->dx *= 0.1; e->dy = rand() % 64 - rand() % 64; e->dy *= 0.1; e->a = 128; e->health = calculateEffectsValue(e->a); setRandomFlameHue(e); } } void addEngineEffect(void) { Effect *e; float c, s; int h; e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; s = sin(TO_RAIDANS(self->angle)); c = cos(TO_RAIDANS(self->angle)); h = self->h / 2; e->x = -(h * s) + self->x; e->y = (h * c) + self->y; e->x += rand() % 4 - rand() % 4; e->texture = explosionTexture; e->size = calculateEffectsValue(16); e->r = 128; e->g = 128; e->b = 255; e->a = 64; e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } void addLargeEngineEffect(void) { Effect *e; e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = self->x; e->y = self->y; e->x -= sin(TO_RAIDANS(self->angle)) * 16; e->y -= -cos(TO_RAIDANS(self->angle)) * 16; e->x += rand() % 4; e->x -= rand() % 4; e->texture = explosionTexture; e->size = calculateEffectsValue(64); e->r = 128; e->g = 128; e->b = 255; e->a = 64; e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } void addMissileEngineEffect(Bullet *b) { Effect *e; e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_TEXTURE; e->x = b->x; e->y = b->y; e->x -= sin(TO_RAIDANS(b->angle)) * 10; e->y -= -cos(TO_RAIDANS(b->angle)) * 10; e->x += rand() % 4; e->x -= rand() % 4; e->texture = explosionTexture; e->size = calculateEffectsValue(12); setRandomFlameHue(e); e->a = 128; e->health = calculateEffectsValue(e->a); e->x -= e->size / 2; e->y -= e->size / 2; } void addShieldSplinterEffect(Entity *ent) { int i; Effect *e; for (i = 0 ; i < 48 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_LINE; e->x = ent->x; e->y = ent->y; e->dx = rand() % 64 - rand() % 64; e->dx *= 0.1; e->dy = rand() % 64 - rand() % 64; e->dy *= 0.1; e->a = 255; e->health = e->a; setRandomShieldHue(e); } } void addECMEffect(Entity *ent) { int i; Effect *e; for (i = 0 ; i < 3 ; i++) { e = malloc(sizeof(Effect)); memset(e, 0, sizeof(Effect)); battle.effectTail->next = e; battle.effectTail = e; e->type = EFFECT_ECM; e->x = ent->x; e->y = ent->y; e->size = i * 4; e->scaleAmount = 5; e->texture = haloTexture; e->r = 128; e->g = 128 + (i * 64); e->b = 255; e->a = 255; e->health = 255; } } static void setRandomFlameHue(Effect *e) { e->r = 255; switch (rand() % 4) { case 0: break; case 1: e->g = 128; break; case 2: e->g = 255; break; case 3: e->g = e->b = 255; break; } } static void setRandomShieldHue(Effect *e) { e->b = 255; switch (rand() % 4) { case 0: e->g = 128; break; case 1: e->g = 196; break; case 2: e->g = 255; break; case 3: e->r = e->g = 255; break; } } void destroyEffects(void) { if (effectsToDraw) { free(effectsToDraw); } effectsToDraw = NULL; } static float calculateEffectsValue(unsigned int val) { #if defined(__amigaos4__) if (app.effects) { return val>>(app.effects<<1); } #endif return (float)val; }