Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,749 @@
|
||||
#include "projectiles.h"
|
||||
#include "line3d.h"
|
||||
#include "main/references.h"
|
||||
#include "processing.h"
|
||||
#include "physics/physics_world.h"
|
||||
#include "obj/object.h"
|
||||
#include "empire.h"
|
||||
#include "design/effector.h"
|
||||
#include "threads.h"
|
||||
#include "scene/node.h"
|
||||
#include "scene/particle_system.h"
|
||||
#include "memory/AllocOnlyPool.h"
|
||||
#include "threads.h"
|
||||
#include "obj/lock.h"
|
||||
#include "scene/animation/anim_projectile.h"
|
||||
#include "util/save_file.h"
|
||||
#include "design/design.h"
|
||||
#include "obj/blueprint.h"
|
||||
#include "network/network_manager.h"
|
||||
#include "ISound.h"
|
||||
#include "ISoundDevice.h"
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
|
||||
memory::AllocOnlyPool<Projectile,threads::Mutex> projPool(8192);
|
||||
|
||||
void initNewThread();
|
||||
void cleanupThread();
|
||||
|
||||
struct ProjEffect : public ObjectMessage {
|
||||
Object* source;
|
||||
const Effector* effector;
|
||||
vec3d relImpact;
|
||||
float effectiveness;
|
||||
float partiality;
|
||||
float delay;
|
||||
|
||||
ProjEffect(const Effector* fctr, Object* target, Object* src, vec3d relImpactPt, float eff, float part, float del) : ObjectMessage(target), source(src), effector(fctr), relImpact(relImpactPt), effectiveness(eff), partiality(part), delay(del) {
|
||||
source->grab();
|
||||
effector->grab();
|
||||
}
|
||||
|
||||
~ProjEffect() {
|
||||
object->drop();
|
||||
source->drop();
|
||||
effector->drop();
|
||||
}
|
||||
|
||||
void process() override {
|
||||
effector->triggerEffect(source, object, relImpact, effectiveness, partiality, delay);
|
||||
}
|
||||
};
|
||||
|
||||
struct ProjImpactEffect : public scene::NodeEvent {
|
||||
const scene::ParticleSystemDesc* system;
|
||||
vec3d from, vel;
|
||||
float scale;
|
||||
float delay;
|
||||
|
||||
ProjImpactEffect(scene::Node* parentNode, const scene::ParticleSystemDesc* sys, const vec3d& From, const vec3d& Vel, float Scale, float Delay) : NodeEvent(parentNode), system(sys), from(From), vel(Vel), scale(Scale), delay(Delay) {
|
||||
}
|
||||
|
||||
void process() override {
|
||||
vec3d pos;
|
||||
quaterniond rot;
|
||||
if(node) {
|
||||
vec3d d = (from - node->abs_position).normalized();
|
||||
pos = (from - node->abs_position);
|
||||
rot = quaterniond::fromImpliedTransform(vec3d::front(), d);
|
||||
}
|
||||
else {
|
||||
pos = from;
|
||||
}
|
||||
auto* pSys = scene::playParticleSystem(system, node, pos, rot, vel, scale, delay);
|
||||
if(pSys)
|
||||
pSys->drop();
|
||||
}
|
||||
|
||||
~ProjImpactEffect() {
|
||||
}
|
||||
};
|
||||
|
||||
double clamp(double v, double low, double high) {
|
||||
if(v <= low)
|
||||
return low;
|
||||
else if(v >= high)
|
||||
return high;
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline double sqr(double x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
//Look for a new objec hostile to the owner
|
||||
Object* findTarget(Empire* owner, const vec3d& position, double radius, const void* ofType = nullptr) {
|
||||
if(!owner)
|
||||
return nullptr;
|
||||
|
||||
Object* obj = nullptr;
|
||||
|
||||
devices.physics->findInBox(AABBoxd::fromCircle(position, radius), [&obj,owner,&position,radius,ofType](const PhysicsItem& item) {
|
||||
if(obj)
|
||||
return;
|
||||
if(item.type == PIT_Object) {
|
||||
Object* target = item.object;
|
||||
if(!target->isVisibleTo(owner) || !target->isValid())
|
||||
return;
|
||||
if(ofType && target->type != ofType)
|
||||
return;
|
||||
if(position.distanceToSQ(target->position) > (radius + target->radius) * (radius + target->radius))
|
||||
return;
|
||||
|
||||
obj = target;
|
||||
obj->grab();
|
||||
}
|
||||
}, owner->hostileMask);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Projectile::tick(double time) {
|
||||
//Rotate to track the target
|
||||
if(type == PT_Missile) {
|
||||
if(target && target->isValid()) {
|
||||
if(recover <= 0.f) {
|
||||
vec3d dir = (target->position - position).normalized();
|
||||
vec3d projImpact = target->position + dir;
|
||||
if(target->type->blueprintOffset != 0) {
|
||||
auto* blueprint = (Blueprint*)(((size_t)target) + target->type->blueprintOffset);
|
||||
const Design* dsg = blueprint->design;
|
||||
|
||||
if(dsg != nullptr) {
|
||||
vec3d impactOffset = target->rotation.inverted() * dir;
|
||||
projImpact = target->rotation * dsg->hull->getImpact(impactOffset, target->radius, true) + target->position;
|
||||
}
|
||||
}
|
||||
|
||||
dir = (projImpact - position).normalized();
|
||||
double speed = velocity.getLength();
|
||||
vec3d vdir = velocity / speed;
|
||||
|
||||
double angle = acos(clamp(vdir.dot(dir),-1,1)), rot = tracking * time;
|
||||
if(angle < rot) {
|
||||
velocity = dir * speed;
|
||||
}
|
||||
else if(angle >= pi * 0.9) {
|
||||
quaterniond dirq = quaterniond::fromImpliedTransform(vec3d::front(), dir);
|
||||
quaterniond vdirq = quaterniond::fromImpliedTransform(vec3d::front(), vdir);
|
||||
quaterniond result = vdirq.slerp(dirq, rot / angle);
|
||||
velocity = (result * vec3d::front()).normalized(speed);
|
||||
}
|
||||
else {//if(angle < 0.05) {
|
||||
//Lerp is a good enough approximation
|
||||
// -- It really is not :( stupid missiles can't even turn 180 degrees
|
||||
velocity = vdir.interpolate(dir, rot / angle).normalized(speed);
|
||||
}
|
||||
|
||||
if(graphics) {
|
||||
auto* anim = (scene::ProjectileAnim*)graphics->animator.ptr;
|
||||
anim->velocity = velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(source && source->isValid()) {
|
||||
if(target) {
|
||||
auto* prevTarget = target;
|
||||
target = findTarget(source->owner, target->position, 50.0, target->type);
|
||||
prevTarget->drop();
|
||||
}
|
||||
else {
|
||||
target = findTarget(source->owner, position, 50.0);
|
||||
}
|
||||
|
||||
//If we didn't quickly find a target, we die twice as fast
|
||||
if(target == 0 && !effector->type.pierces)
|
||||
lifetime *= 0.5f;
|
||||
}
|
||||
else {
|
||||
//Become a dummy when the target is already dead
|
||||
//Also live only half the remaining time
|
||||
lifetime *= 0.5f;
|
||||
if(target) {
|
||||
target->drop();
|
||||
target = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == PT_Beam) {
|
||||
//Beams can't fire when the source is dead
|
||||
if(!source->isValid())
|
||||
return true;
|
||||
|
||||
if(target) {
|
||||
if(target->isValid()) {
|
||||
if(tracking > 0) {
|
||||
vec3d targDir = (target->position - (source->position + position)).normalize();
|
||||
double range = velocity.getLength();
|
||||
|
||||
vec3d curDir = velocity / range;
|
||||
|
||||
double dot = std::max(std::min(targDir.dot(curDir), 1.0), -1.0);
|
||||
double angDiff = acos(dot);
|
||||
double track = tracking * time;
|
||||
|
||||
if(angDiff <= track)
|
||||
velocity = targDir * range;
|
||||
else
|
||||
velocity = curDir.slerp(targDir,track/angDiff) * range;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Our current target is dead, find a new one
|
||||
auto* prevTarget = target;
|
||||
if(source)
|
||||
target = findTarget(source->owner, target->position, 50.0, target->type);
|
||||
else
|
||||
target = 0;
|
||||
prevTarget->drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line3dd line(position, vec3d());
|
||||
|
||||
if(type != PT_Beam) {
|
||||
line.end = position + velocity * time;
|
||||
}
|
||||
else {
|
||||
line.start += source->position;
|
||||
line.end = line.start + velocity;
|
||||
}
|
||||
|
||||
Object* other = 0;
|
||||
vec3d impactPt;
|
||||
|
||||
double prevDistSQ = 0;
|
||||
double projScale = scale;
|
||||
|
||||
//If our line still collides with our last impacted object, we can check for only intervening objects
|
||||
if(lastImpact && lastImpact->isValid()) {
|
||||
vec3d closePt = line.getClosestPoint(lastImpact->position,false);
|
||||
double distSQ = closePt.distanceToSQ(lastImpact->position);
|
||||
double width = lastImpact->radius + scale;
|
||||
if(distSQ <= width * width) {
|
||||
other = lastImpact;
|
||||
other->grab();
|
||||
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
|
||||
if(!effector || !effector->type.pierces)
|
||||
line.end = impactPt;
|
||||
prevDistSQ = closePt.distanceToSQ(line.start);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode != PM_OnlyHitsTarget) {
|
||||
if(mode == PM_PassthroughInvalid) {
|
||||
const Effector* eff = effector;
|
||||
Object* src = source;
|
||||
devices.physics->findInBox(AABBoxd(line, scale), [&other,&line,&prevDistSQ,&impactPt,projScale,eff,src](const PhysicsItem& item) {
|
||||
//if(item.type != PIT_Object)
|
||||
// return;
|
||||
Object* obj = item.object;
|
||||
vec3d closePt = line.getClosestPoint(obj->position,false);
|
||||
double distSQ = closePt.distanceToSQ(obj->position);
|
||||
double width = obj->radius + projScale;
|
||||
if(distSQ <= width * width && (!eff || eff->canTarget(src, obj))) {
|
||||
double objDistSQ = closePt.distanceToSQ(line.start);
|
||||
if(!other || objDistSQ < prevDistSQ) {
|
||||
if(other)
|
||||
other->drop();
|
||||
other = obj;
|
||||
other->grab();
|
||||
prevDistSQ = objDistSQ;
|
||||
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
|
||||
}
|
||||
}
|
||||
}, source->owner ? (source->owner->hostileMask | 0x1) : ~0x0);
|
||||
}
|
||||
else {
|
||||
devices.physics->findInBox(AABBoxd(line, scale), [&other,&line,&prevDistSQ,&impactPt,projScale](const PhysicsItem& item) {
|
||||
//if(item.type != PIT_Object)
|
||||
// return;
|
||||
Object* obj = item.object;
|
||||
vec3d closePt = line.getClosestPoint(obj->position,false);
|
||||
double distSQ = closePt.distanceToSQ(obj->position);
|
||||
double width = obj->radius + projScale;
|
||||
if(distSQ <= width * width) {
|
||||
double objDistSQ = closePt.distanceToSQ(line.start);
|
||||
if(!other || objDistSQ < prevDistSQ) {
|
||||
if(other)
|
||||
other->drop();
|
||||
other = obj;
|
||||
other->grab();
|
||||
prevDistSQ = objDistSQ;
|
||||
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
|
||||
}
|
||||
}
|
||||
}, source->owner ? (source->owner->hostileMask | 0x1) : ~0x0);
|
||||
}
|
||||
}
|
||||
else if(target && target->isValid()){
|
||||
vec3d closePt = line.getClosestPoint(target->position,false);
|
||||
double distSQ = closePt.distanceToSQ(target->position);
|
||||
double width = target->radius + projScale;
|
||||
if(distSQ <= width * width) {
|
||||
if(other)
|
||||
other->drop();
|
||||
other = target;
|
||||
other->grab();
|
||||
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
|
||||
}
|
||||
}
|
||||
|
||||
if(other != nullptr && other->type->blueprintOffset != 0) {
|
||||
auto* blueprint = (Blueprint*)(((size_t)other) + other->type->blueprintOffset);
|
||||
const Design* dsg = blueprint->design;
|
||||
|
||||
if(dsg != nullptr) {
|
||||
vec3d impactOffset = impactPt - other->position;
|
||||
impactOffset = other->rotation.inverted() * impactOffset;
|
||||
impactPt = other->rotation * dsg->hull->getImpact(impactOffset, other->radius, type == PT_Beam) + other->position;
|
||||
}
|
||||
}
|
||||
|
||||
if(recover > 0.f) {
|
||||
if(other && other == lastImpact) {
|
||||
other->drop();
|
||||
other = nullptr;
|
||||
}
|
||||
recover -= time;
|
||||
if(recover <= 0.f)
|
||||
recover = 0.f;
|
||||
}
|
||||
|
||||
if(other) {
|
||||
if(impact)
|
||||
*impact = impactPt;
|
||||
if(type == PT_Beam || effector->type.pierces) {
|
||||
other->grab();
|
||||
if(lastImpact)
|
||||
lastImpact->drop();
|
||||
lastImpact = other;
|
||||
}
|
||||
|
||||
auto* player = Empire::getPlayerEmpire();
|
||||
|
||||
if(effector) {
|
||||
if(player && other->isVisibleTo(player)) {
|
||||
if(auto* sfx = effector->type.skins[effector->skinIndex].impact_sound) {
|
||||
if(sfx->loaded && !audio::disableSFX) {
|
||||
auto* sound = devices.sound->play3D(sfx->source, snd_vec(other->position), false, true);
|
||||
if(sound) {
|
||||
if(source)
|
||||
sound->setVolume((float)((scale + 1.0) / (scale + 7.0)));
|
||||
sound->setPitch((float)randomd(0.95,1.05));
|
||||
float dist = other->position.distanceTo(devices.render->cam_pos);
|
||||
float lo = dist / (dist + scale * 500.0);
|
||||
if(lo > 0.05)
|
||||
sound->setLowPass(lo);
|
||||
sound->resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LockGroup* lockGroup = other->lockGroup;
|
||||
float delay = 0.f;
|
||||
if(effector && lockGroup) {
|
||||
if(type == PT_Missile && missileData) {
|
||||
double speed = velocity.getLength();
|
||||
delay = impactPt.distanceTo(position) / speed * 1.5f;
|
||||
}
|
||||
|
||||
if(effector->type.skins[effector->skinIndex].impact && (!player || other->isVisibleTo(player)))
|
||||
scene::queueNodeEvent(new ProjImpactEffect(other->node, effector->type.skins[effector->skinIndex].impact, impactPt, other->velocity, (float)sqrt(effector->relativeSize * (source ? source->radius : 1.0)), delay));
|
||||
if(!devices.network->isClient)
|
||||
lockGroup->addMessage(new ProjEffect(effector, other, source, impactPt - other->position, efficiency, type == PT_Beam ? (float)time : 1.f, delay));
|
||||
else
|
||||
other->drop();
|
||||
}
|
||||
else {
|
||||
other->drop();
|
||||
}
|
||||
|
||||
if(type != PT_Beam) {
|
||||
if(effector && effector->type.pierces) {
|
||||
recover = effector->type.recoverTime;
|
||||
if(target && other == target) {
|
||||
target->drop();
|
||||
target = nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(type == PT_Missile && missileData) {
|
||||
auto& data = **missileData;
|
||||
double gameTime = devices.driver->getGameTime();
|
||||
double speed = velocity.getLength();
|
||||
|
||||
data.lastUpdate = gameTime;
|
||||
data.aliveUntil = gameTime + delay;
|
||||
data.pos = impactPt;
|
||||
data.vel = vec3f((impactPt - position).normalized(speed));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(impact) {
|
||||
*impact = line.end;
|
||||
}
|
||||
|
||||
if(type != PT_Beam) {
|
||||
position = line.end;
|
||||
if(type == PT_Missile && missileData) {
|
||||
auto& data = **missileData;
|
||||
data.lastUpdate = devices.driver->getGameTime();
|
||||
data.pos = position;
|
||||
data.vel = vec3f(velocity);
|
||||
}
|
||||
}
|
||||
lifetime -= (float)time;
|
||||
return lifetime <= 0;
|
||||
}
|
||||
|
||||
void* Projectile::operator new(size_t size) {
|
||||
return projPool.alloc();
|
||||
}
|
||||
|
||||
void Projectile::operator delete(void* p) {
|
||||
return projPool.dealloc((Projectile*)p);
|
||||
}
|
||||
|
||||
Projectile::Projectile(ProjType Type) : lastTick(devices.driver->getGameTime()), source(0), target(0), lastImpact(0), graphics(0), type(Type), impact(0), efficiency(1.f), endNotice(0), mode(PM_Normal), recover(0.f) {
|
||||
}
|
||||
|
||||
Projectile::~Projectile() {
|
||||
if(graphics) {
|
||||
graphics->markForDeletion();
|
||||
graphics->drop();
|
||||
}
|
||||
|
||||
if(type != PT_Missile) {
|
||||
if(endNotice) {
|
||||
**endNotice = true;
|
||||
endNotice->drop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(missileData) {
|
||||
if((*missileData)->aliveUntil < 0)
|
||||
(*missileData)->aliveUntil = 0;
|
||||
missileData->drop();
|
||||
}
|
||||
}
|
||||
|
||||
if(source)
|
||||
source->drop();
|
||||
if(target)
|
||||
target->drop();
|
||||
if(lastImpact)
|
||||
lastImpact->drop();
|
||||
if(effector)
|
||||
effector->drop();
|
||||
}
|
||||
|
||||
extern double frameTime_s;
|
||||
Projectile* Projectile::load(SaveFile& file) {
|
||||
const Effector* efftr = nullptr;
|
||||
|
||||
unsigned char empID;
|
||||
int dsgId;
|
||||
unsigned subsysIndex, effectorIndex, effId;
|
||||
if((bool)file) {
|
||||
empID = file;
|
||||
if(empID == INVALID_EMPIRE)
|
||||
return 0;
|
||||
|
||||
dsgId = file;
|
||||
subsysIndex = file;
|
||||
effectorIndex = file;
|
||||
|
||||
Empire* dsgOwner = Empire::getEmpireByID(empID);
|
||||
const Design* dsg = dsgOwner->getDesign(dsgId);
|
||||
if(dsg == nullptr || subsysIndex >= dsg->subsystems.size() || effectorIndex >= dsg->subsystems[subsysIndex].type->effectors.size()) {
|
||||
//throw "Invalid projectile effector.";
|
||||
// Just cancel the projectile later so we can continue the load.
|
||||
}
|
||||
else {
|
||||
efftr = &dsg->subsystems[subsysIndex].effectors[effectorIndex]; //No refcounting on design effectors
|
||||
}
|
||||
}
|
||||
else {
|
||||
effId = file;
|
||||
efftr = getEffector(effId); //Reference is transfered to projectile
|
||||
}
|
||||
|
||||
//Find effector
|
||||
Projectile* proj = new Projectile((ProjType)file.read<unsigned char>());
|
||||
proj->effector = efftr;
|
||||
|
||||
//Load data
|
||||
file >> proj->lastTick;
|
||||
file >> proj->lifetime;
|
||||
proj->source = file.readExistingObject();
|
||||
proj->target = file.readExistingObject();
|
||||
file >> proj->position;
|
||||
file >> proj->velocity;
|
||||
file >> proj->scale;
|
||||
file >> proj->tracking;
|
||||
file >> proj->efficiency;
|
||||
|
||||
//Actually screw this, these things cause nothing but problems
|
||||
delete proj;
|
||||
return 0;
|
||||
|
||||
if(!proj->source || !proj->effector) {
|
||||
//This can happen if the object that fired the projectile
|
||||
//was already destroyed when the save occured. We have no way
|
||||
//of instantiating these or their effects at the moment, because
|
||||
//we know absolutely nothing about the source.
|
||||
|
||||
//TODO: Preserve these in some way, they can still impact and matter
|
||||
//quite a bit.
|
||||
delete proj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: The effects are non-trivial, figure out how to restore them
|
||||
//Create graphics
|
||||
/*proj->graphics = proj->effector->type.createGraphics(proj->effector, proj->scale);
|
||||
if(!proj->graphics) {
|
||||
delete proj;
|
||||
throw "Invalid projectile effector graphics.";
|
||||
}
|
||||
|
||||
proj->graphics->position = proj->position;
|
||||
proj->graphics->rebuildTransformation();
|
||||
proj->graphics->animator = new scene::ProjectileAnim(proj->velocity, 1.f);
|
||||
|
||||
registerProjectile(proj);
|
||||
proj->graphics->queueReparent(devices.scene);
|
||||
return proj;*/
|
||||
registerProjectile(proj);
|
||||
return proj;
|
||||
}
|
||||
|
||||
void Projectile::save(SaveFile& file) {
|
||||
if(effector && effector->effectorId != 0) {
|
||||
file << false;
|
||||
file << effector->effectorId;
|
||||
}
|
||||
else {
|
||||
file << true;
|
||||
if(!effector || !effector->inDesign || !effector->inDesign->owner) {
|
||||
file << INVALID_EMPIRE;
|
||||
return;
|
||||
}
|
||||
|
||||
file << effector->inDesign->owner->id;
|
||||
file << effector->inDesign->id;
|
||||
file << effector->subsysIndex;
|
||||
file << effector->effectorIndex;
|
||||
}
|
||||
|
||||
file << (unsigned char)type;
|
||||
file << lastTick;
|
||||
file << lifetime;
|
||||
file << source;
|
||||
file << target;
|
||||
file << position;
|
||||
file << velocity;
|
||||
file << scale;
|
||||
file << tracking;
|
||||
file << efficiency;
|
||||
}
|
||||
|
||||
threads::Mutex projAddLock, projfillLock;
|
||||
threads::Signal activeProjThreads;
|
||||
threads::atomic_int pull_index, push_index, processed_count;
|
||||
std::vector<double> projThreadTimes;
|
||||
|
||||
std::vector<Projectile*>* source = new std::vector<Projectile*>, *dest = new std::vector<Projectile*>;
|
||||
std::vector<Projectile*> queuedProjs;
|
||||
|
||||
static void fillProjectiles() {
|
||||
dest->resize(push_index);
|
||||
|
||||
if(!queuedProjs.empty()) {
|
||||
projAddLock.lock();
|
||||
dest->insert(dest->end(), queuedProjs.begin(), queuedProjs.end());
|
||||
queuedProjs.clear();
|
||||
projAddLock.release();
|
||||
}
|
||||
|
||||
std::swap(source, dest);
|
||||
dest->resize(source->size());
|
||||
|
||||
processed_count = 0;
|
||||
push_index = 0;
|
||||
pull_index = 0;
|
||||
}
|
||||
|
||||
volatile bool ProjectilesActive = false;
|
||||
volatile bool EndProjectiles = false;
|
||||
volatile bool PauseProjectiles = false;
|
||||
volatile bool ProjectilesPaused = false;
|
||||
class ProcessProjectiles : public processing::Action {
|
||||
bool host, active;
|
||||
public:
|
||||
ProcessProjectiles(bool Host) : host(Host), active(true) {
|
||||
}
|
||||
|
||||
~ProcessProjectiles() {
|
||||
//This happens when the game is ending. We may not complete an entire cycle at the right time.
|
||||
if(host)
|
||||
ProjectilesActive = false;
|
||||
else if(active)
|
||||
activeProjThreads.signalDown();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
if(host && activeProjThreads.check(0)) {
|
||||
if(EndProjectiles)
|
||||
return true;
|
||||
ProjectilesActive = true;
|
||||
|
||||
if(PauseProjectiles) {
|
||||
ProjectilesActive = false;
|
||||
ProjectilesPaused = true;
|
||||
return false;
|
||||
}
|
||||
else if(ProjectilesPaused) {
|
||||
ProjectilesPaused = false;
|
||||
}
|
||||
else {
|
||||
fillProjectiles();
|
||||
}
|
||||
|
||||
if(!source->empty()) {
|
||||
activeProjThreads.signal(4);
|
||||
for(unsigned i = 0; i < 4; ++i)
|
||||
processing::queueAction(new ProcessProjectiles(false));
|
||||
}
|
||||
else {
|
||||
ProjectilesActive = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double curTime = devices.driver->getGameTime();
|
||||
unsigned tickMax = 1000;
|
||||
while(tickMax--) {
|
||||
int index = pull_index++;
|
||||
if(index >= (int)source->size()) {
|
||||
if(host) {
|
||||
ProjectilesActive = false;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
active = false;
|
||||
activeProjThreads.signalDown();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Take the next projectile, tick it, and put it on the output stack (unless it needs deleted)
|
||||
Projectile* proj = source->at(index);
|
||||
double t = curTime - proj->lastTick;
|
||||
|
||||
if(t < 0.125) {
|
||||
int outIndex = push_index++;
|
||||
dest->at(outIndex) = proj;
|
||||
}
|
||||
else {
|
||||
if(!proj->tick(t)) {
|
||||
proj->lastTick = curTime;
|
||||
int outIndex = push_index++;
|
||||
dest->at(outIndex) = proj;
|
||||
}
|
||||
else {
|
||||
delete proj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void registerProjectile(Projectile* proj) {
|
||||
projAddLock.lock();
|
||||
queuedProjs.push_back(proj);
|
||||
projAddLock.release();
|
||||
}
|
||||
|
||||
void initProjectiles() {
|
||||
EndProjectiles = false;
|
||||
PauseProjectiles = false;
|
||||
processing::queueAction(new ProcessProjectiles(true));
|
||||
}
|
||||
|
||||
void stopProjectiles() {
|
||||
EndProjectiles = true;
|
||||
while(ProjectilesActive)
|
||||
threads::sleep(1);
|
||||
activeProjThreads.wait(0);
|
||||
source->clear(); source->shrink_to_fit();
|
||||
dest->clear(); dest->shrink_to_fit();
|
||||
for(auto i = queuedProjs.begin(), end = queuedProjs.end(); i != end; ++i)
|
||||
delete *i;
|
||||
queuedProjs.clear();
|
||||
pull_index = 0;
|
||||
push_index = 0;
|
||||
processed_count = 0;
|
||||
}
|
||||
|
||||
void saveProjectiles(SaveFile& file) {
|
||||
unsigned cnt = (unsigned)(queuedProjs.size() + push_index);
|
||||
file << cnt;
|
||||
|
||||
cnt = (unsigned)queuedProjs.size();
|
||||
for(unsigned i = 0; i < cnt; ++i)
|
||||
queuedProjs[i]->save(file);
|
||||
|
||||
cnt = (unsigned)push_index;
|
||||
for(unsigned i = 0; i < cnt; ++i)
|
||||
(*dest)[i]->save(file);
|
||||
}
|
||||
|
||||
void loadProjectiles(SaveFile& file) {
|
||||
unsigned cnt = file;
|
||||
for(unsigned i = 0; i < cnt; ++i)
|
||||
Projectile::load(file);
|
||||
}
|
||||
|
||||
void pauseProjectiles() {
|
||||
PauseProjectiles = true;
|
||||
if(!ProjectilesActive)
|
||||
return;
|
||||
activeProjThreads.wait(0);
|
||||
while(!ProjectilesPaused && ProjectilesActive)
|
||||
threads::sleep(1);
|
||||
}
|
||||
|
||||
void resumeProjectiles() {
|
||||
PauseProjectiles = false;
|
||||
}
|
||||
Reference in New Issue
Block a user