1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-02-04 02:24:00 +00:00
Aquaria/Aquaria/SchoolFish.cpp
2015-08-03 20:33:22 +02:00

644 lines
14 KiB
C++

/*
Copyright (C) 2007, 2010 - Bit-Blot
This file is part of Aquaria.
Aquaria 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 "SchoolFish.h"
#include "Game.h"
#include "Avatar.h"
#include "../BBGE/AfterEffect.h"
const float strengthSeparation = 1;
const float strengthAlignment = 0.8f;
const float strengthAvoidance = 1;
const float strengthCohesion = 0.5f;
const float avoidanceDistance = 128;
const float separationDistance = 128;
const float minUrgency = 5;//0.05;
const float maxUrgency = 10;//0.1;
const float maxSpeed = 400; // 1
const float maxChange = maxSpeed*maxUrgency;
const float velocityScale = 1;
SchoolFish::SchoolFish(const std::string &texname) : FlockEntity()
{
burstDelay = 0;
naijaReaction = "smile";
rippleTimer = 0;
oldFlockID = -1;
respawnTimer = 0;
dodgeAbility = (rand()%900)/1000.0f + 0.1f;
float randScale = float(rand()%200)/1000.0f;
scale = Vector(0.6f-randScale, 0.6f-randScale);
/*
float randColor = float(rand()%250)/1000.0f;
color = Vector(1-randColor, 1-randColor, 1-randColor);
*/
//color.interpolateTo(Vector(0.5, 0.5, 0.5), 2, -1, 1);
color.ensureData();
color.data->path.addPathNode(Vector(1,1,1), 0);
color.data->path.addPathNode(Vector(1,1,1), 0.5);
color.data->path.addPathNode(Vector(0.8, 0.8, 0.8), 0.7);
color.data->path.addPathNode(Vector(1,1,1), 1.0);
color.startPath(2);
color.data->loopType = -1;
color.update((rand()%1000)/1000.0f);
flipDelay = 0;
swimSound = "SchoolFishSwim";
soundDelay = (rand()%300)/100.0f;
range = 0;
setEntityType(ET_ENEMY);
canBeTargetedByAvatar = true;
health = maxHealth = 1;
//scale = Vector(0.5, 0.5);
avoidTime=0;
vel = Vector(-minUrgency, 0);
setTexture(texname);
flockType = FLOCK_FISH;
//updateCull = -1;
updateCull = 4000;
collideRadius = 20;
//2 32 0.1 0.1 -0.03 0 4 0
setSegs(8, 2, 0.1, 0.9, 0, -0.03, 8, 0);
/*
setDamageTarget(DT_AVATAR_SPORECHILD, false);
setDamageTarget(DT_AVATAR_ENERGYBLAST, false);
*/
//setAllDamageTargets(true);
setDamageTarget(DT_AVATAR_LIZAP, false);
/*
setDamageTarget(DT_AVATAR_ENERGYBLAST, false);
setDamageTarget(DT_AVATAR_SHOCK, false);
*/
//setDamageTarget(DT_AVATAR_BITE, true);
// updateCull = 10248;
targetPriority = -1;
setEatType(EAT_FILE, "SchoolFish");
this->deathSound = "";
this->targetPriority = -1;
setDamageTarget(DT_AVATAR_PET, false);
}
void SchoolFish::onEnterState(int action)
{
Entity::onEnterState(action);
if (action == STATE_DEAD)
{
//rotation.interpolateTo(Vector(0,0,180), 2);
vel.setLength2D(vel.getLength2D()*-1);
oldFlockID = flock ? flock->flockID : -1;
removeFromFlock();
doDeathEffects(0, false);
/*
alpha = 0;
alphaMod = 0;
*/
respawnTimer = 20 + rand()%20;
alphaMod = 0;
/*
this->setLife(2);
this->setDecayRate(1);
this->fadeAlphaWithLife = true;
*/
/*
dsq->game->spawnIngredient("FishOil", position, 10);
dsq->game->spawnIngredient("FishMeat", position, 10);
dsq->game->spawnIngredient("SmallEgg", position, 10);
dsq->game->spawnIngredient("PlantLeaf", position, 60);
*/
if (!isGoingToBeEaten())
{
if (dsq->game->firstSchoolFish)
{
if (chance(50))
dsq->game->spawnIngredient("FishOil", position, 1);
else
dsq->game->spawnIngredient("FishMeat", position, 1);
dsq->game->firstSchoolFish = false;
}
else
{
if (chance(8))
dsq->game->spawnIngredient("FishOil", position, 1);
if (chance(5))
dsq->game->spawnIngredient("FishMeat", position, 1);
}
}
}
else if (action == STATE_IDLE)
{
revive(3000);
alpha.interpolateTo(1, 1);
alphaMod = 1;
if (oldFlockID != -1)
addToFlock(oldFlockID);
}
}
void SchoolFish::updateVelocity(Vector &accumulator)
{
// Ok, now limit speeds
accumulator.capLength2D(maxChange);
// Save old speed
lastSpeed = vel.getLength2D();
// Calculate new velocity and constrain it
vel += accumulator;
vel.capLength2D(getMaxSpeed() * maxSpeedLerp.x);
vel.z = 0;
if (fabsf(vel.y) > fabsf(vel.x))
{
/*
float sign = vel.y / fabsf(vel.y);
vel.y = fabsf(vel.x) * sign;
*/
//std::swap(vel.x, vel.y);
// going up
/*
float len = vel.getLength2D();
if (vel.y < 0)
{
if (vel.x < 0)
vel.y = vel.x;
else
vel.y = -vel.x;
}
else
{
if (vel.x < 0)
vel.y = -vel.x;
else
vel.y = vel.x;
}
vel.setLength2D(len);
*/
}
}
inline
void SchoolFish::avoid(Vector &accumulator, Vector pos, bool inv)
{
//accumulator = Vector(0,0,0);
Vector change;
if (inv)
change = pos - this->position;
else
change = this->position - pos;
//change = position;
//change -= this->position;
change.setLength2D(maxUrgency);
//change |= maxUrgency;
//change = Vector(100,0);
//avoidTime = 2;
/*
std::ostringstream os;
os << "change(" << change.x << ", " << change.y << ")";
debugLog (os.str());
*/
accumulator += change;
}
void SchoolFish::applyLayer(int layer)
{
switch (layer)
{
case -3:
setv(EV_LOOKAT, 0);
break;
}
}
inline
void SchoolFish::applyAvoidance(Vector &accumulator)
{
// only avoid the player if not in the background
if (this->layer < LR_ELEMENTS10)
{
if ((dsq->game->avatar->position - this->position).isLength2DIn(128))
{
avoid(accumulator, dsq->game->avatar->position);
}
}
//return;
if (avoidTime>0) return;
const int range = 10;
const int step = 1;
int radius = range*TILE_SIZE;
int obsSumX = 0, obsSumY = 0; // Not a Vector (avoid using floats)
int obsCount = 0;
const TileVector t0(position);
TileVector t;
for (t.x = t0.x-range; t.x <= t0.x+range; t.x += step)
{
for (t.y = t0.y-range; t.y <= t0.y+range; t.y += step)
{
if (dsq->game->isObstructed(t))
{
obsSumX += t0.x - t.x;
obsSumY += t0.y - t.y;
obsCount++;
}
}
}
if (obsCount > 0)
{
const float tileMult = (float)TILE_SIZE / (float)obsCount;
Vector change(obsSumX*tileMult, obsSumY*tileMult);
change += position - t0.worldVector();
//change |= 200;
float dist = change.getLength2D();
float ratio = dist / radius;
if (ratio < minUrgency) ratio = minUrgency;
else if (ratio > maxUrgency) ratio = maxUrgency;
change *= (ratio + lastSpeed*0.1f) / dist;
accumulator += change;
}
if (this->range!=0)
{
if (!((position - startPos).isLength2DIn(this->range)))
{
Vector diff = startPos - position;
diff.setLength2D(lastSpeed);
accumulator += diff;
}
}
}
inline
void SchoolFish::applyAlignment(Vector &accumulator, const Vector &flockHeading)
{
if (strengthAlignment <= 0) return;
Vector change;
// Flocking Rule: Alignment
// Try to align boid's heading with its flockmates
// Copy the heading of the flock
change = getFlockHeading();
// normalize change to look more natural
//change |= (minUrgency * strengthAlignment );
change.setLength2D(minUrgency*strengthAlignment);
// Add Change to Accumulator
accumulator += change;
}
inline
void SchoolFish::applyCohesion(Vector &accumulator)
{
if (strengthCohesion<=0) return;
Vector change;
// Flocking Rule: Cohesion
// Try to go toward where all the flockmates are (the flock's center point)
// Copy the position of center of flock
change = getFlockCenter();
// Average with our position
change -= position;
// normalize change to look more natural
if (!change.isZero())
change.setLength2D(minUrgency*strengthCohesion);
accumulator += change;
if (dsq->continuity.form == FORM_FISH)
{
if ((dsq->game->avatar->position - this->position).isLength2DIn(256))
{
change = dsq->game->avatar->position - position;
change.setLength2D(maxUrgency*strengthCohesion);
accumulator += change;
//avoid(accumulator, dsq->game->avatar->position, true);
}
}
}
inline
void SchoolFish::applySeparation(Vector &accumulator)
{
FlockEntity *e = getNearestFlockEntity();
if (e)
{
const float dist = getNearestFlockEntityDist();
if (dist < separationDistance)
{
float ratio = dist / separationDistance;
if (ratio < minUrgency) ratio = minUrgency;
else if (ratio > maxUrgency) ratio = maxUrgency;
ratio *= strengthSeparation;
Vector change(e->position - this->position);
if (!change.isZero())
{
change.setLength2D(-ratio);
accumulator += change;
}
}
// Are we too far from nearest flockmate? Then Move Closer
/*
else if (dist > separationDistance)
change |= ratio;
*/
}
}
void SchoolFish::onUpdate(float dt)
{
BBGE_PROF(SchoolFish_onUpdate);
/*
Quad::onUpdate(dt);
return;
*/
/*
if (dsq->continuity.form == FORM_BEAST)
this->activationType = ACT_CLICK;
else
this->activationType = ACT_NONE;
*/
/*
if (burstDelay == 0)
{
maxSpeedLerp = 2;
Vector v = getNormal();
vel = 0;
v *= -5000;
vel += v;
//float t = (100 + rand()%100)/100.0f;
float t = 2;
maxSpeedLerp.interpolateTo(1, t);
burstDelay = 10;// + (rand()%100)/100.0f;
//rotateToVec(v, 0, 90);
//rotation.interpolateTo(0, 1);
if (v.x > 0 && !isfh())
{
flipHorizontal();
flipDelay = 0.5;
}
if (v.x < 0 && isfh())
{
flipHorizontal();
flipDelay = 0.5;
}
}
else
*/
{
burstDelay -= dt;
if (burstDelay < 0)
{
burstDelay = 0;
}
}
if (stickToNaijasHead && alpha.x < 0.1f)
stickToNaijasHead = false;
if (this->layer < LR_ENTITIES)
{
//debugLog("background fish!");
/*
setDamageTarget(DT_AVATAR_SHOCK, false);
setDamageTarget(DT_AVATAR_BITE, false);
setDamageTarget(DT_AVATAR_VOMIT, false);
setDamageTarget(DT_AVATAR_ENERGYBLAST, false);
*/
setEntityType(ET_NEUTRAL);
collideRadius = 0;
}
if (getState() == STATE_DEAD)
{
FlockEntity::onUpdate(dt);
respawnTimer -= dt;
if (!(dsq->game->avatar->position - this->position).isLength2DIn(2000))
{
if (respawnTimer < 0)
{
respawnTimer = 0;
perform(STATE_IDLE);
}
}
}
else
{
/*
if (layer == LR_ENTITIES || layer == LR_ENTITIES2)
{
rippleTimer -= dt;
if (rippleTimer < 0)
{
if (core->afterEffectManager)
core->afterEffectManager->addEffect(new ShockEffect(Vector(core->width/2, core->height/2),position,0.04,0.06,15,0.2f));
rippleTimer = 0.5;
}
}
*/
FlockEntity::onUpdate(dt);
if (dsq->game->isValidTarget(this, 0))
dsq->game->handleShotCollisions(this);
/*
soundDelay -= dt;
if (soundDelay <= 0)
{
//sound(swimSound, 1000 + rand()%100);
soundDelay = 4+(rand()%50)/100.0f;
}
*/
/*
1. if distance_to(closest_boid) <= too_close then set direction away from closest_boid
2. speed_of_neighbors := average(speed(x), for all x where distance_to(x) <= neighborhood_size)
direction_of_neighbors := avg(direction(x), for all x where distance_to(x) <= neighborhood_size)
if speed < speed_of_neighbors then increase speed
if speed > speed_of_neighbors then decrease speed
turn towards direction_of_neighbors
3. position_of_neighbors := avg(position(x), for all x where distance_to(x) <= neighborhood_size)
turn towards position_of_neighbors
*/
/*
FlockPiece flock;
getFlockInRange(160, &flock);
*/
// if flock in 160 ?
if (true)
{
VectorSet newDirection;
if (avoidTime>0)
{
avoidTime -= dt;
if (avoidTime<0)
avoidTime=0;
}
Vector dir = getFlockHeading();
Vector accumulator;
applyCohesion(accumulator);
applyAlignment(accumulator, dir);
// alignment
applySeparation(accumulator);
applyAvoidance(accumulator);
updateVelocity(accumulator);
/*
if (dsq->game->isValidTarget(this, 0))
doSpellAvoidance(dt, 96, dodgeAbility);
*/
Vector lastPosition = position;
position += (vel*velocityScale + vel2) * dt;
if (dsq->game->isObstructed(position))
{
position = lastPosition;
/*
Vector newPosition = position;
position = Vector(newPosition.x, lastPosition.y);
if (dsq->game->isObstructed(position))
{
position = Vector(lastPosition.x, newPosition.y);
if (dsq->game->isObstructed(position))
{
position = lastPosition;
}
}
*/
}
//updateCurrents(dt);
updateVel2(dt);
/*
if (flipDelay > 0)
{
flipDelay -= dt;
if (flipDelay < 0)
{
flipDelay = 0;
}
}
*/
flipDelay = 0;
//dir.normalize2D();
if (flipDelay <= 0)
{
const float amt = 0;
/*
if (fabsf(dir.x) > fabsf(dir.y))
{
if (dir.x > amt && !isfh())
{
flipHorizontal();
flipDelay = 0.5;
}
if (dir.x < -amt && isfh())
{
flipHorizontal();
flipDelay = 0.5;
}
}
*/
if (vel.x > amt && !isfh())
{
flipHorizontal();
}
if (vel.x < -amt && isfh())
{
flipHorizontal();
}
}
//rotateToVec(accumulator, 5, 90);
float angle = atan2f(dir.x<0 ? -dir.y : dir.y, fabsf(dir.x));
angle = ((angle*180)/PI);
if (angle > 45)
angle = 45;
if (angle < -45)
angle = -45;
rotation = Vector(0,0,angle);
//rotation.interpolateTo(Vector(0, 0, angle), 0);
}
}
}
void SchoolFish::onRender()
{
FlockEntity::onRender();
/*
glDisable(GL_BLEND);
glPointSize(12);
glDisable(GL_LIGHTING);
glColor3f(1,1,1);
glBegin(GL_POINTS);
glVertex3f(0,0,0);
glEnd();
glBegin(GL_LINES);
glVertex3f(0,0,0);
glVertex3f(vel.x*50, vel.y*50, 0);
glEnd();
*/
}