Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
110 KB
Referenced Files
None
Subscribers
None
diff --git a/source/game/obj/blueprint.cpp b/source/game/obj/blueprint.cpp
index 9af05d8..45440b5 100644
--- a/source/game/obj/blueprint.cpp
+++ b/source/game/obj/blueprint.cpp
@@ -1,1855 +1,1855 @@
#include "obj/blueprint.h"
#include "constants.h"
#include "util/random.h"
#include "main/references.h"
#include "main/logging.h"
#include "network/message.h"
#include <algorithm>
#include "empire.h"
#include "util/save_file.h"
#include "scriptany.h"
#include <assert.h>
#include "scene/node.h"
void shader_quadrant_damage(float* values, unsigned short amt, void*) {
if(auto* node = scene::renderingNode) {
Object* obj = node->obj;
if(obj != nullptr) {
size_t offset = obj->type->blueprintOffset;
if(offset != 0) {
Blueprint* bp = (Blueprint*)(((size_t)obj) + offset);
const Design* dsg = bp->design;
if(dsg != nullptr) {
for(unsigned i = 0; i < 4; ++i) {
float curHP = bp->quadrantHP[i];
float maxHP = dsg->quadrantTotalHP[i];
if(maxHP <= 0.f)
values[i] = 1.f;
else
values[i] = 1.f - (curHP / maxHP);
}
}
}
}
return;
}
for(unsigned i = 0; i < 4; ++i)
values[i] = 0.f;
}
Blueprint::Blueprint()
: design(nullptr), statusID(0), designChanged(false), hpDelta(false), repairingHex(-1,-1), holdFire(false),
hpFactor(1.f), removedHP(0.f) {
}
void Blueprint::init(Object* obj) {
}
Blueprint::HexStatus* Blueprint::getHexStatus(unsigned x, unsigned y) {
if(!design->hexStatusIndex.valid(vec2u(x, y)))
return 0;
int index = design->hexStatusIndex.get(x, y);
if(index < 0)
return 0;
return &hexes[index];
}
Blueprint::HexStatus* Blueprint::getHexStatus(unsigned index) {
if(design == nullptr || index >= design->usedHexCount)
return 0;
return &hexes[index];
}
Blueprint::SysStatus* Blueprint::getSysStatus(unsigned index) {
return &subsystems[index];
}
Blueprint::SysStatus* Blueprint::getSysStatus(unsigned x, unsigned y) {
if(!design->grid.valid(vec2u(x, y)))
return 0;
int index = design->grid.get(x, y);
if(index < 0)
return 0;
return &subsystems[index];
}
CScriptAny* Blueprint::getHookData(unsigned index) {
if(index >= design->dataCount)
return nullptr;
return data[index];
}
void Blueprint::create(Object* obj, const Design* design) {
this->design = design;
if(!design)
return;
designChanged = true;
hpDelta = true;
++statusID;
++design->built;
++design->active;
//Create hex status grid
hexes = new HexStatus[design->usedHexCount];
for(unsigned i = 0; i < design->usedHexCount; ++i) {
HexStatus& hex = hexes[i];
hex.hp = 255;
hex.flags = HF_Active;
int hexIndex = design->hexIndex[design->hexes[i]];
int sysIndex = design->grid[design->hexes[i]];
if(sysIndex != -1) {
const float* hp = design->subsystems[sysIndex].hexVariable(HV_HP, hexIndex);
if(hp == nullptr || *hp == 0.f) {
hex.hp = 0;
hex.flags |= HF_NoHP;
}
}
}
//Initialize subsystems
EffectEvent event;
event.obj = obj;
unsigned cnt = (unsigned)design->subsystems.size();
subsystems = new SysStatus[cnt];
states = new BasicType[design->stateCount];
effectorStates = new double[design->effectorStateCount];
effectorTargets = new EffectorTarget[design->effectorCount];
destroyedHexes = 0;
currentHP = design->totalHP;
for(unsigned i = 0; i < 4; ++i)
quadrantHP[i] = design->quadrantTotalHP[i];
shipEffectiveness = 1.0;
removedHP = 0.f;
data = new CScriptAny*[design->dataCount];
for(unsigned i = 0, cnt = design->dataCount; i < cnt; ++i)
data[i] = new CScriptAny(devices.scripts.server->engine);
memset(effectorStates, 0, design->effectorStateCount * sizeof(double));
- memset(effectorTargets, 0, design->effectorCount * sizeof(EffectorTarget));
+ memset(reinterpret_cast<void *>(effectorTargets), 0, design->effectorCount * sizeof(EffectorTarget));
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
d.workingHexes = (unsigned short)sys.hexes.size();
d.status = ES_Active;
//Initialize states
for(size_t j = 0, jcnt = sys.type->states.size(); j < jcnt; ++j)
states[sys.stateOffset + j] = sys.defaults[j];
//Initialize turret tracking
for(size_t j = 0, jcnt = sys.type->effectors.size(); j < jcnt; ++j)
effectorTargets[sys.effectorOffset+j].tracking = sys.effectors[j].turretAngle;
}
}
void Blueprint::start(Object* obj, bool fromRetrofit) {
//Initialize subsystems
EffectEvent event;
event.obj = obj;
unsigned cnt = (unsigned)design->subsystems.size();
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
d.status = ES_Active;
//Start the subsystem
event.source = i;
if(fromRetrofit) {
sys.call(EH_Retrofit_Post, event);
sys.call(EH_Continue, event);
}
else {
sys.call(EH_Start, event);
}
//Enable all the modules
for(size_t j = 0, jcnt = sys.modules.size(); j < jcnt; ++j)
sys.modules[j]->onEnable(event, sys.hexes[j]);
}
}
bool Blueprint::hasTagActive(int index) {
if(!design)
return false;
for(size_t i = 0, cnt = design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
if(d.status == ES_Active)
if(sys.type->hasTag(index))
return true;
}
return false;
}
double Blueprint::getTagEfficiency(int index, bool ignoreInactive) {
unsigned totalHexes = 0;
unsigned activeHexes = 0;
for(size_t i = 0, cnt = design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
totalHexes += (unsigned)sys.hexes.size();
if(!ignoreInactive || d.status == ES_Active)
activeHexes += (unsigned)d.workingHexes;
}
if(totalHexes == 0)
return 0.0;
return (double)activeHexes / (double)totalHexes;
}
double Blueprint::getEfficiencySum(int variable, int tag, bool ignoreInactive) {
if(!design)
return 0.0;
double total = 0.0;
for(size_t i = 0, cnt = design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
if(ignoreInactive && d.status != ES_Active)
continue;
if(tag != -1 && !sys.type->hasTag(tag))
continue;
const float* val = sys.variable(variable);
if(val) {
double eff = (double)d.workingHexes / (double)sys.hexes.size();
total += eff * (double)*val;
}
}
return total;
}
double Blueprint::getEfficiencyFactor(int variable, int tag, bool ignoreInactive) {
if(!design)
return 0.0;
double total = 0.0;
double active = 0.0;
for(size_t i = 0, cnt = design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
if(tag != -1 && !sys.type->hasTag(tag))
continue;
const float* val = sys.variable(variable);
if(!val)
continue;
total += (double)*val;
if(!ignoreInactive || d.status == ES_Active) {
double eff = (double)d.workingHexes / (double)sys.hexes.size();
active += eff * (double)*val;
}
}
if(total == 0)
return 0.0;
return active / total;
}
Object* Blueprint::getCombatTarget() {
if(!design)
return nullptr;
unsigned start = 0;
unsigned end = design->effectorCount;
for(; start < end; ++start) {
auto& targ = effectorTargets[start];
if(targ.target) {
targ.target->grab();
return targ.target;
}
}
return nullptr;
}
//Cached facing angles that are going to be looked at
const vec3d FACING_POSITIONS[20] = {
vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.1*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.2*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.3*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.4*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.5*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.6*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.7*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.8*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 0.9*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.0*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.1*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.2*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.3*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.4*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.5*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.6*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.7*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.8*pi) * vec3d::front(),
quaterniond::fromAxisAngle(vec3d::up(), 1.9*pi) * vec3d::front(),
};
vec3d Blueprint::getOptimalFacing(int sysVariable, int tag, bool ignoreInactive) {
if(!design)
return vec3d::front();
double values[20] = {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
size_t sysCount = design->subsystems.size();
for(size_t i = 0; i < sysCount; ++i) {
auto& sys = design->subsystems[i];
auto& status = subsystems[i];
//Ignore subsystems with no effectors to determine facing
size_t effCnt = sys.type->effectors.size();
if(effCnt == 0)
continue;
if(ignoreInactive && status.status != ES_Active)
continue;
//Do tag filtering
if(tag != -1 && !sys.type->hasTag(tag))
continue;
//Determine value of subsystem by passed variable
const float* val = sys.variable(sysVariable);
if(!val)
continue;
float curValue = *val;
curValue *= (float)status.workingHexes / (float)sys.hexes.size();
//Make sure we have effectors with firing arcs
bool foundOne = false;
for(size_t n = 0; n < effCnt; ++n) {
Effector* eff = &sys.effectors[n];
if(eff->fireArc >= twopi-0.01 || !eff->enabled)
continue;
foundOne = true;
break;
}
if(!foundOne)
continue;
//Mark everything that can be fired at by all effectors
for(unsigned j = 0; j < 20; ++j) {
const vec3d& facing = FACING_POSITIONS[j];
bool usable = true;
double dist = 0;
for(size_t n = 0; n < effCnt; ++n) {
Effector* eff = &sys.effectors[n];
//Ignore omnidirectional stuff
if(eff->fireArc >= twopi-0.01 || !eff->enabled)
continue;
//Check if we can fire in this direction
double d = facing.angleDistance(eff->turretAngle);
dist += d;
if(d > eff->fireArc) {
usable = false;
break;
}
}
if(usable)
values[j] += curValue - (dist * 0.001);
}
}
//Find the best facing
double best = 0.0;
vec3d bestFacing = vec3d::front();
for(unsigned j = 0; j < 20; ++j) {
if(values[j] > best) {
best = values[j];
bestFacing = FACING_POSITIONS[j];
}
}
return bestFacing;
}
void Blueprint::destroy(Object* obj) {
--design->active;
//Stop all the subsystem effects
EffectEvent event;
event.obj = obj;
unsigned cnt = (unsigned)design->subsystems.size();
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
event.source = i;
//Disable all the modules
if(d.status == ES_Active) {
for(size_t j = 0, jcnt = sys.modules.size(); j < jcnt; ++j)
sys.modules[j]->onDisable(event, sys.hexes[j]);
sys.call(EH_End, event);
}
sys.call(EH_Destroy, event);
}
}
void Blueprint::ownerChange(Object* obj, Empire* prevEmpire, Empire* newEmpire) {
//Stop all the subsystem effects
EffectEvent event;
event.obj = obj;
unsigned cnt = (unsigned)design->subsystems.size();
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = design->subsystems[i];
event.source = i;
sys.ownerChange(event, prevEmpire, newEmpire);
}
}
void Blueprint::preClear() {
for(unsigned i = 0; i < design->effectorCount; ++i) {
if(effectorTargets[i].target) {
effectorTargets[i].target->drop();
effectorTargets[i].target = nullptr;
}
}
}
Blueprint::~Blueprint() {
delete[] hexes;
delete[] subsystems;
delete[] effectorStates;
for(unsigned i = 0; i < design->effectorCount; ++i)
if(effectorTargets[i].target)
effectorTargets[i].target->drop();
delete[] effectorTargets;
for(unsigned i = 0, cnt = design->dataCount; i < cnt; ++i) {
if(data[i])
data[i]->Release();
}
delete[] data;
}
void Blueprint::retrofit(Object* obj, const Design* toDesign) {
if(this->design == nullptr)
return;
if(toDesign->base() == this->design->base()
&& toDesign->usedHexCount == this->design->usedHexCount
&& toDesign->subsystems.size() == this->design->subsystems.size()
&& toDesign->dataCount == this->design->dataCount
&& toDesign->effectorCount == this->design->effectorCount
&& toDesign->effectorStateCount == this->design->effectorStateCount
&& toDesign->stateCount == this->design->stateCount
//These extra checks *should* be implied from sharing a base, but let's just make sure
) {
//Stop all subsystems
EffectEvent event;
event.obj = obj;
for(int i = 0, cnt = (int)design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
//Suspend subsystem
event.source = i;
sys.call(EH_Retrofit_Pre, event);
if(d.status == ES_Active) {
sys.call(EH_Suspend, event);
//Disable all the modules
for(size_t j = 0, jcnt = sys.modules.size(); j < jcnt; ++j)
sys.modules[j]->onDisable(event, sys.hexes[j]);
}
}
this->design = toDesign;
++statusID;
hpDelta = true;
designChanged = true;
//Resume all subsystems
double newHP = 0.0;
for(unsigned i = 0; i < 4; ++i)
quadrantHP[i] = 0;
for(unsigned i = 0; i < design->usedHexCount; ++i) {
vec2u pos = design->hexes[i];
int sysIndex = design->grid[pos];
if(sysIndex == -1)
continue;
auto& hs = hexes[i];
auto& sys = design->subsystems[sysIndex];
int hexIndex = design->hexIndex[pos];
const float* hp = sys.hexVariable(HV_HP, hexIndex);
if(hp) {
double hexHP = *hp * ((double)hs.hp / 255.0);
newHP += hexHP;
quadrantHP[design->getQuadrant(pos)] += hexHP;
}
}
currentHP = newHP;
removedHP = 0.f;
//Start new subsystems
start(obj, true);
}
else {
//Stop all subsystems
EffectEvent event;
event.obj = obj;
for(int i = 0, cnt = (int)design->subsystems.size(); i < cnt; ++i) {
auto& sys = design->subsystems[i];
auto& d = subsystems[i];
//Suspend subsystem
event.source = i;
sys.call(EH_Retrofit_Pre, event);
if(d.status == ES_Active) {
sys.call(EH_Suspend, event);
//Disable all the modules
for(size_t j = 0, jcnt = sys.modules.size(); j < jcnt; ++j)
sys.modules[j]->onDisable(event, sys.hexes[j]);
}
}
//Delete previous data
delete[] hexes;
delete[] subsystems;
delete[] effectorStates;
for(unsigned i = 0, cnt = design->dataCount; i < cnt; ++i) {
if(data[i])
data[i]->Release();
}
delete[] data;
for(unsigned i = 0; i < design->effectorCount; ++i)
if(effectorTargets[i].target)
effectorTargets[i].target->drop();
delete[] effectorTargets;
//Create new data
create(obj, toDesign);
start(obj, true);
}
}
float Blueprint::think(Object* obj, double time) {
if(!design)
return 5.f;
EffectEvent event;
event.obj = obj;
event.time = time;
unsigned cnt = (unsigned)design->subsystems.size();
unsigned efftr = 0;
bool engaged = false, inCombat = obj->getFlag(objCombat);
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = design->subsystems[i];
EffectStatus status = subsystems[i].status;
event.partiality = (float)subsystems[i].workingHexes / (float)sys.hexes.size();
if(status != ES_Active)
event.partiality = 0.f;
event.efficiency = event.partiality * (float)shipEffectiveness;
event.source = i;
event.status = status;
sys.tick(event);
//Handle suspend and continue
if(status == ES_Suspended) {
if(event.status != ES_Suspended) {
sys.call(EH_Continue, event);
++statusID;
}
}
else {
if(event.status == ES_Suspended) {
sys.call(EH_Suspend, event);
++statusID;
}
}
//Handle effectors
if(event.status == ES_Active) {
for(unsigned i = 0, cnt = (unsigned)sys.type->effectors.size(); i < cnt; ++i) {
auto& effector = sys.effectors[i];
if(!effector.enabled)
continue;
EffectorTarget& target = effectorTargets[efftr];
double* states = effectorStates + effector.stateOffset;
effector.update(obj, time, states, target, event.efficiency, holdFire);
++efftr;
if(target.flags & TF_Firing)
engaged = true;
}
}
}
if(engaged)
obj->setFlag(objEngaged, true);
return inCombat ? 0.1f : 0.25f;
}
bool Blueprint::canTarget(Object* obj, Object* target) {
foreach(it, design->subsystems) {
auto& sys = *it;
for(unsigned i = 0, cnt = (unsigned)sys.type->effectors.size(); i < cnt; ++i) {
Effector* eff = &sys.effectors[i];
if(eff->enabled && eff->canTarget(obj, target))
return true;
}
}
return false;
}
bool Blueprint::doesAutoTarget(Object* obj, Object* target) {
foreach(it, design->subsystems) {
auto& sys = *it;
for(unsigned i = 0, cnt = (unsigned)sys.type->effectors.size(); i < cnt; ++i) {
Effector* eff = &sys.effectors[i];
if(eff->autoTarget(obj, target))
return true;
}
}
return false;
}
void Blueprint::target(Object* obj, Object* target, TargetFlags flags) {
unsigned start = 0;
unsigned end = design->effectorCount;
for(; start < end; ++start) {
auto& targ = effectorTargets[start];
if(targ.target == target)
continue;
if(!targ.target || targ.flags & TF_Preference || !(flags & TF_Preference)) {
if(targ.target)
targ.target->drop();
targ.target = target;
targ.flags = flags;
if(target)
target->grab();
}
}
}
void Blueprint::clearTracking(Object* obj) {
unsigned start = 0;
unsigned end = design->effectorCount;
for(; start < end; ++start) {
auto& targ = effectorTargets[start];
targ.flags |= TF_ClearTracking;
}
}
void Blueprint::target(Object* obj, unsigned efftrIndex, Object* target, TargetFlags flags) {
if(efftrIndex >= design->effectorCount)
return;
auto& targ = effectorTargets[efftrIndex];
if(targ.target == target)
return;
if(!targ.target || targ.flags & TF_Preference || !(flags & TF_Preference)) {
if(targ.target)
targ.target->drop();
targ.target = target;
targ.flags = flags;
if(target)
target->grab();
}
}
void Blueprint::target(Object* obj, const Subsystem* sys, Object* target, TargetFlags flags) {
unsigned start = sys->effectorOffset;
unsigned end = start + (unsigned)sys->type->effectors.size();
for(; start < end; ++start) {
auto& targ = effectorTargets[start];
if(targ.target == target)
continue;
if(!targ.target || targ.flags & TF_Preference || !(flags & TF_Preference)) {
if(targ.target)
targ.target->drop();
targ.target = target;
targ.flags = flags;
if(target)
target->grab();
}
}
}
void Blueprint::damage(Object* obj, DamageEvent& evt, const vec2d& direction) {
if(!design || (direction.x == 0.0 && direction.y == 0.0))
return;
//Find a position for this direction that ensures something gets damaged
unsigned count = (unsigned)design->hexes.size();
unsigned index = randomi(0, count-1);
unsigned w = design->grid.width;
unsigned h = design->grid.height;
vec2d dirline = direction.normalized((double)(w+h) * 2.0);
vec2u goal(-1, -1);
for(unsigned i = 0; i < count; ++i, index = (index+1) % count) {
goal = design->hexes[index];
HexStatus* status = getHexStatus(goal.x, goal.y);
if(status && status->hp != 0)
break;
}
if(!design->grid.valid(goal))
return;
vec2u hex = goal;
//We found a hex that can take damage, now run
//the line through here.
vec2d effPos = design->grid.getEffectivePosition(hex);
vec2d startPos = effPos + dirline;
vec2d endPos = effPos - dirline;
//Advance toward the edge in the direction of the source
while(hex.x > 0 && hex.x < w-1 && hex.y > 0 && hex.y < h-1) {
vec2d diff = design->grid.getEffectivePosition(hex);
diff.y = -diff.y;
diff = startPos - diff;
double dir = diff.radians();
HexGridAdjacency adj = HexGrid<>::AdjacencyFromRadians(dir);
if(!design->grid.advance(hex, adj))
break;
}
//Run global damage events
unsigned sysCnt = (unsigned)design->damageOrder.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = *design->damageOrder[i];
if(subsystems[sys.index].status != ES_Active)
continue;
evt.target = obj;
evt.destination = sys.index;
vec2d dir = direction;
switch(sys.globalDamage(evt, hex, dir)) {
case DE_Continue:
break;
case DE_SkipHex:
case DE_EndDamage:
return;
}
}
bool reachedTarget = false;
//Run forward from the starting position to the end position
unsigned n = 0;
for(; n < 500; ++n) {
damage_internal(obj, evt, hex);
//Stop if no damage is left
if(evt.damage <= 0.0)
break;
if(!reachedTarget && hex == goal)
reachedTarget = true;
//Find next hex
vec2d diff = design->grid.getEffectivePosition(hex);
if(reachedTarget)
diff = endPos - diff;
else
diff = effPos - diff;
diff.y = -diff.y;
double dir = diff.radians();
auto adj = HexGrid<>::AdjacencyFromRadians(dir);
if(!design->grid.advance(hex, adj))
break;
}
if(!evt.spillable && evt.damage > 0) {
double prev;
evt.spillable = true;
do {
prev = evt.damage;
damage(obj, evt, direction);
}
while (evt.damage < prev - 0.001 && evt.damage > 0.001);
}
if(n == 500) {
error("WARNING: Detected a damage event that passed"
"a ridiculous amount of hexes (500).\n Stopping the event. Check if "
"an endpoint within the blueprint is being passed.");
}
}
void Blueprint::damage(Object* obj, DamageEvent& evt, double position, const vec2d& direction) {
if(!design || direction.x == 0.0 || direction.y == 0.0)
return;
//Helper to figure out the starting hex from a percentage position
double rad = direction.radians();
vec2u hex;
vec2d endPoint;
//Top
if(rad > 0.25*pi && rad < 0.75*pi) {
if(position == 0.0)
hex = vec2u(0, 0);
else
hex = vec2u((unsigned)ceil(position * design->grid.width) - 1, 0);
vec2d hpos = design->grid.getEffectivePosition(hex);
endPoint.y = design->grid.height;
endPoint.x = hpos.x - direction.x * fabs((double)design->grid.height / direction.y);
}
//Bottom
else if(rad < -0.25*pi && rad > -0.75*pi) {
if(position == 0.0)
hex = vec2u(0, design->grid.height - 1);
else
hex = vec2u((unsigned)ceil(position * design->grid.width) - 1, design->grid.height - 1);
vec2d hpos = design->grid.getEffectivePosition(hex);
endPoint.y = -1.0;
endPoint.x = hpos.x - direction.x * fabs((double)design->grid.height / direction.y);
}
//Right
else if(rad < 0.25*pi && rad > -0.25*pi) {
if(position == 0.0)
hex = vec2u(design->grid.width - 1, 0);
else
hex = vec2u(design->grid.width - 1, (unsigned)ceil(position * design->grid.height) - 1);
vec2d hpos = design->grid.getEffectivePosition(hex);
endPoint.x = -1.0;
endPoint.y = hpos.y - direction.y * fabs((0.75 * design->grid.width) / direction.x);
}
//Left
else {
if(position == 0.0)
hex = vec2u(0, 0);
else
hex = vec2u(0, (unsigned)ceil(position * design->grid.height) - 1);
vec2d hpos = design->grid.getEffectivePosition(hex);
endPoint.x = 0.75 * design->grid.width;
endPoint.y = hpos.y - direction.y * fabs((0.75 * design->grid.width) / direction.x);
}
//Run global damage events
unsigned sysCnt = (unsigned)design->damageOrder.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = *design->damageOrder[i];
if(subsystems[sys.index].status != ES_Active)
continue;
evt.target = obj;
evt.destination = sys.index;
vec2d dir = direction;
switch(sys.globalDamage(evt, hex, dir)) {
case DE_Continue:
break;
case DE_SkipHex:
case DE_EndDamage:
return;
}
}
damage(obj, evt, hex, endPoint);
if(evt.damage > 0)
damage(obj, evt, direction);
}
void Blueprint::damage(Object* obj, DamageEvent& evt, const vec2u& _position, const vec2d& _endPoint) {
if(!design)
return;
vec2u position = _position;
vec2d endPoint = _endPoint;
//Make sure we start at a valid hex
if(!design->grid.valid(position))
return;
//endPoint y coordinate should be flipped due to euclidian space
//and hex grid space being oriented differently in that dimension
endPoint.y = -endPoint.y;
//Keep hitting hexes until we run out of damage or hexes
// (Limit the amount of hexes that can be damaged for if
// some retard passes an endPoint that is within the blueprint)
unsigned i = 0;
for(; i < 500; ++i) {
damage_internal(obj, evt, position);
//Stop if no damage is left
if(evt.damage <= 0.0)
break;
//Find next hex
vec2d diff = design->grid.getEffectivePosition(position);
diff.y = -diff.y;
diff = endPoint - diff;
double dir = diff.radians() + pi;
HexGridAdjacency adj = HexGridAdjacency(dir >= 2*pi ? 5 : (int)floor(dir / (pi / 3.0)));
if(!design->grid.advance(position, adj))
break;
}
if(i == 500) {
error("WARNING: Detected a damage event that passed"
"a ridiculous amount of hexes (500).\n Stopping the event. Check if "
"an endpoint within the blueprint is being passed.");
}
}
void Blueprint::damage(Object* obj, DamageEvent& evt, const vec2u& hex, HexGridAdjacency dir, bool runGlobal) {
if(!design)
return;
vec2u pos = hex;
vec2d direction;
if(!design->grid.valid(pos))
return;
//Run global damage events
if(runGlobal) {
unsigned sysCnt = (unsigned)design->damageOrder.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = *design->damageOrder[i];
if(subsystems[sys.index].status != ES_Active)
continue;
evt.target = obj;
evt.destination = sys.index;
switch(sys.globalDamage(evt, pos, direction)) {
case DE_Continue:
break;
case DE_SkipHex:
case DE_EndDamage:
return;
}
}
}
//Run forward from the starting position to the end position
while(design->grid.valid(pos)) {
damage_internal(obj, evt, pos);
//Stop if no damage is left
if(evt.damage <= 0.0)
break;
//Find next hex
if(!design->grid.advance(pos, dir))
break;
}
}
void Blueprint::damage(Object* obj, DamageEvent& evt, const vec2u& hex, bool runGlobal) {
if(!design)
return;
vec2u pos = hex;
vec2d direction;
if(!design->grid.valid(pos))
return;
//Run global damage events
if(runGlobal) {
unsigned sysCnt = (unsigned)design->damageOrder.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = *design->damageOrder[i];
if(subsystems[sys.index].status != ES_Active)
continue;
evt.target = obj;
evt.destination = sys.index;
switch(sys.globalDamage(evt, pos, direction)) {
case DE_Continue:
break;
case DE_SkipHex:
case DE_EndDamage:
return;
}
}
}
//Damage the specified hex
damage_internal(obj, evt, pos);
}
bool Blueprint::globalDamage(Object* obj, DamageEvent& evt) {
if(!design)
return false;
vec2u dummyHex(0, 0);
vec2d dummyDir(1.0, 0.0);
//Run global damage events
unsigned sysCnt = (unsigned)design->damageOrder.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = *design->damageOrder[i];
if(subsystems[sys.index].status != ES_Active)
continue;
evt.target = obj;
evt.destination = sys.index;
switch(sys.globalDamage(evt, dummyHex, dummyDir)) {
case DE_Continue:
case DE_SkipHex:
return false;
case DE_EndDamage:
return true;
}
}
return false;
}
void Blueprint::damage_internal(Object* obj, DamageEvent& evt, const vec2u& position) {
int index = design->grid[position];
if(index >= 0) {
auto& sys = design->subsystems[index];
auto& status = *getHexStatus(position.x, position.y);
if(status.hp == 0 && !sys.type->alwaysTakeDamage)
return;
float prevPartial = evt.partiality;
//Resistance reduces pierce through
double dealDamage = evt.damage;
int hexIndex = design->hexIndex[position];
if(const float* res = sys.hexVariable(HV_Resistance, hexIndex))
evt.pierce = std::max(evt.pierce - *res, 0.f);
if(evt.pierce > 0) {
if(evt.pierce > 1.f)
return;
dealDamage *= (1.0 - evt.pierce);
float part = (float)(dealDamage / evt.damage);
evt.partiality *= part;
prevPartial *= 1.f - part;
}
double remainingDamage = evt.damage - dealDamage;
//Do damage to hex
evt.target = obj;
evt.destination = index;
evt.damage = dealDamage;
if(status.flags & HF_Active) {
switch(sys.damage(evt, position)) {
case DE_Continue:
damage(obj, evt, position);
break;
case DE_SkipHex:
break;
case DE_EndDamage:
evt.damage = 0;
return;
}
}
else {
damage(obj, evt, position);
}
evt.partiality = prevPartial;
evt.damage += remainingDamage;
}
}
void Blueprint::damage(Object* obj, DamageEvent& evt, const vec2u& position) {
if(!design)
return;
int index = design->grid[position];
if(index < 0)
return;
HexStatus* hexPtr = getHexStatus(position.x, position.y);
if(!hexPtr)
return;
HexStatus& hex = *hexPtr;
auto& sys = design->subsystems[index];
SysStatus& status = subsystems[index];
int hexIndex = design->hexIndex[position];
//If it has HP, we can damage it
if(const float* hp = sys.hexVariable(HV_HP, hexIndex)) {
//Figure out how much damage to deal
double hexHP = *hp * (double)hex.hp / 255.0 * hpFactor;
double deal = std::min(evt.damage, hexHP);
if(deal <= 0.0)
return;
double dmgPts, fracPt = modf(deal * (255.0 / (double)*hp) / hpFactor, &dmgPts);
//Directly deal any full damage
unsigned char relative = (unsigned char)dmgPts;
//If there is a significant fractional damage
//component, use randomness
if(fracPt > 0.001 && relative < 255)
if(randomd() <= fracPt)
relative += 1;
//We absorbed the hit (likely only a fractional hit)
if(relative == 0) {
evt.damage -= deal;
return;
}
//Deal the damage
short prevHP = hex.hp;
hex.hp = (unsigned char)std::max(0, (short)hex.hp - (short)relative);
relative = (prevHP - hex.hp);
hpDelta = true;
//Check if the hex should be marked as destroyed
if(!(hex.flags & HF_Destroyed) && hex.hp == 0) {
hex.flags |= HF_Destroyed;
status.workingHexes -= 1;
//Notify the effects that a hex was destroyed
EffectEvent ef;
ef.obj = obj;
ef.source = index;
ef.partiality = (float)status.workingHexes / (float)sys.hexes.size();
ef.efficiency = ef.partiality * (float)shipEffectiveness;
sys.call(EH_Change, ef);
++statusID;
//Notify the module
auto* mod = sys.modules[hexIndex];
if(mod->scr_onDisable) {
EffectEvent evt;
evt.obj = obj;
evt.source = index;
mod->onDisable(evt, position);
}
//Deactivate the entire subsystem if we have to
if(mod->vital || status.workingHexes == 0) {
status.status = ES_Ended;
sys.call(EH_End, ef);
}
//Check if the entire ship should blow up
++destroyedHexes;
//if(destroyedHexes >= design->usedHexCount / 3) {
// evt.damage = 0.0;
// evt.flags |= 0x40000000;
// obj->flagDestroy();
// return;
//}
}
//Remove dealt damage
evt.damage -= deal;
double change = relative * (double)*hp / 255.0;
currentHP -= change;
quadrantHP[design->getQuadrant(position)] -= change;
}
}
double Blueprint::repair(Object* obj, double amount) {
if(!design || currentHP >= design->totalHP) {
repairingHex = vec2i(-1, -1);
return amount;
}
auto findRepairHex = [](Blueprint* bp) -> vec2i {
//Repair core hexes first
unsigned sysCnt = (unsigned)bp->design->subsystems.size();
for(unsigned i = 0; i < sysCnt; ++i) {
auto& sys = bp->design->subsystems[i];
auto& status = *bp->getSysStatus(i);
if(sys.type->hasCore && status.status == ES_Ended) {
HexStatus* stat = bp->getHexStatus(sys.core.x, sys.core.y);
if(stat && stat->hp < 255 && !(stat->flags & (HF_NoHP | HF_NoRepair)))
return vec2i(sys.core);
}
}
//Search randomly
unsigned hexCnt = bp->design->usedHexCount;
unsigned index = randomi(0, hexCnt-1);
for(unsigned i = 0; i < hexCnt; ++i, index = (index+1) % hexCnt) {
vec2u hex = bp->design->hexes[i];
HexStatus* stat = bp->getHexStatus(hex.x, hex.y);
if(stat && stat->hp < 255 && !(stat->flags & (HF_NoHP | HF_NoRepair)))
return vec2i(hex);
}
return vec2i(-1, -1);
};
vec2u gridSize = design->hull->gridSize;
if((unsigned)repairingHex.x >= gridSize.x || (unsigned)repairingHex.y >= gridSize.y) {
//Find a new hex to be repairing
repairingHex = findRepairHex(this);
}
while(true) {
if((unsigned)repairingHex.x >= gridSize.x || (unsigned)repairingHex.y >= gridSize.y)
return amount;
HexStatus* stat = getHexStatus(repairingHex.x, repairingHex.y);
if(!stat) {
repairingHex = vec2i(-1, -1);
return amount;
}
if(stat->hp < 255 && !(stat->flags & (HF_NoHP | HF_NoRepair)))
amount = repair(obj, vec2u(repairingHex), amount);
if(currentHP >= design->totalHP) {
repairingHex = vec2i(-1, -1);
currentHP = design->totalHP;
for(unsigned i = 0; i < 4; ++i)
quadrantHP[i] = design->quadrantTotalHP[i];
return amount;
}
if(amount > 0.0) {
repairingHex = findRepairHex(this);
continue;
}
else {
return 0.0;
}
}
}
double Blueprint::repair(Object* obj, const vec2u& position, double amount) {
if(!design)
return amount;
int index = design->grid[position];
if(index < 0)
return amount;
HexStatus* hexPtr = getHexStatus(position.x, position.y);
if(!hexPtr)
return amount;
HexStatus& hex = *hexPtr;
auto& sys = design->subsystems[index];
SysStatus& status = subsystems[index];
int hexIndex = design->hexIndex[position];
const float* hp = sys.hexVariable(HV_HP, hexIndex);
if(!hp)
return amount;
if(*hp <= 0.f)
return 0.0;
//Check if it needs any repair at all
if(hex.hp == 255)
return amount;
//Figure out how much damage to deal
double hexDam = *hp * (1.0 - ((double)hex.hp / 255.0)) * hpFactor;
double repair = std::min(amount, hexDam);
if(repair <= 0.0)
return amount;
double repPts, fracPt = modf(repair * (255.0 / (double)*hp) / hpFactor, &repPts);
//Directly deal any full damage
unsigned char relative = (unsigned char)repPts;
//If there is a significant fractional
//component, use randomness
if(fracPt > 0.001 && relative < 255)
if(randomd() <= fracPt)
relative += 1;
//All the repair was randomed out
if(relative == 0)
return 0.0;
//Modify the hex's hp
short prevHP = hex.hp;
hex.hp = (unsigned char)std::min(255, (short)hex.hp + (short)relative);
relative = (hex.hp - prevHP);
hpDelta = true;
amount -= repair;
double change = relative * (double)*hp / 255.0;
currentHP = std::min(currentHP + change, design->totalHP);
unsigned quadrant = design->getQuadrant(position);
quadrantHP[quadrant] = std::min(currentHP + change, design->quadrantTotalHP[quadrant]);
//Inform the subsystem
if(hex.flags & HF_Destroyed && hex.hp > 0) {
hex.flags &= ~HF_Destroyed;
status.workingHexes += 1;
--destroyedHexes;
//Notify the effects that a hex was destroyed
EffectEvent ef;
ef.obj = obj;
ef.source = index;
ef.partiality = (float)status.workingHexes / (float)sys.hexes.size();
ef.efficiency = ef.partiality * (float)shipEffectiveness;
sys.call(EH_Change, ef);
++statusID;
//Notify the module
auto* mod = sys.modules[hexIndex];
if(mod->scr_onEnable) {
EffectEvent evt;
evt.obj = obj;
evt.source = index;
mod->onEnable(evt, position);
}
//Reactivate subsystem if needed
if(mod->vital || status.workingHexes == 1) {
bool hasAllVital = true;
size_t hexCnt = sys.hexes.size();
for(size_t i = 0; i < hexCnt; ++i) {
if(sys.modules[i]->vital) {
HexStatus* otherStatus = getHexStatus(sys.hexes[i].x, sys.hexes[i].y);
if(otherStatus && otherStatus->flags & HF_Destroyed) {
hasAllVital = false;
break;
}
}
}
if(hasAllVital) {
status.status = ES_Active;
sys.call(EH_Start, ef);
}
}
}
if(amount < 0.0001)
return 0.0;
return amount;
}
void Blueprint::sendDetails(Object* obj, net::Message& msg) {
if(!design || !design->owner) {
msg.write0();
return;
}
msg.write1();
msg << design->owner->id;
msg.writeSmall(design->id);
//Sync subsystem status
for(unsigned i = 0; i < design->subsystems.size(); ++i) {
auto& ss = subsystems[i];
if(ss.status == ES_Active) {
msg.write1();
continue;
}
msg.write0();
msg << ss.status;
}
//Sync hex status
for(unsigned i = 0; i < design->usedHexCount; ++i) {
auto& hs = hexes[i];
if(hs.hp == 255 && hs.flags == HF_Active) {
msg.write1();
continue;
}
if(hs.hp == 0 && hs.flags == HF_Destroyed) {
msg.write0();
msg.write1();
continue;
}
msg.write0();
msg.write0();
if(hs.flags == HF_Active) {
msg.write1();
}
else {
msg.write0();
msg << hs.flags;
}
msg << hs.hp;
}
//Sync subsystem states
for(unsigned i = 0; i < design->stateCount; ++i) {
switch(states[i].type) {
case BT_Int:
msg << states[i].integer;
break;
case BT_Double: {
float val = (float)states[i].decimal;
msg << val;
} break;
case BT_Bool:
msg.writeBit(states[i].boolean);
break;
}
}
//Sync effector states
for(unsigned i = 0; i < design->effectorStateCount; ++i) {
float val = (float)effectorStates[i];
msg << val;
}
}
void Blueprint::recvDetails(Object* obj, net::Message& msg) {
unsigned char ownerID;
if(!msg.readBit())
return;
++statusID;
msg >> ownerID;
unsigned designID = msg.readSmall();
if(!design || designID != design->id) {
Empire* owner = Empire::getEmpireByID(ownerID);
if(!owner)
return;
design = owner->getDesign(designID);
if(!design)
return;
create(obj, design);
}
destroyedHexes = 0;
//Sync subsystem status
for(size_t i = 0; i < design->subsystems.size(); ++i) {
auto& ss = subsystems[i];
ss.workingHexes = (unsigned short)design->subsystems[i].hexes.size();
if(msg.readBit()) {
ss.status = ES_Active;
continue;
}
msg >> ss.status;
}
//Sync hex status
for(unsigned i = 0; i < design->usedHexCount; ++i) {
auto& hs = hexes[i];
if(msg.readBit()) {
hs.hp = 255;
hs.flags = HF_Active;
continue;
}
if(msg.readBit()) {
hs.hp = 0;
hs.flags = HF_Destroyed;
}
else {
if(msg.readBit())
hs.flags = HF_Active;
else
msg >> hs.flags;
msg >> hs.hp;
}
if(hs.flags & HF_Destroyed) {
vec2u pos = design->hexes[i];
destroyedHexes += 1;
auto* ss = getSysStatus(pos.x, pos.y);
if(ss)
ss->workingHexes -= 1;
}
}
//Sync subsystem states
for(unsigned i = 0; i < design->stateCount; ++i) {
switch(states[i].type) {
case BT_Int:
msg >> states[i].integer;
break;
case BT_Double: {
float val = 0.f;
msg >> val;
states[i].decimal = val;
} break;
case BT_Bool:
states[i].boolean = msg.readBit();
break;
}
}
//Sync effector states
for(unsigned i = 0; i < design->effectorStateCount; ++i) {
float val = 0.f;
msg >> val;
effectorStates[i] = val;
}
}
bool Blueprint::sendDelta(Object* obj, net::Message& msg) {
if(!design)
return false;
if(!designChanged && !hpDelta)
return false;
hpDelta = false;
msg.write1();
msg.writeBit(designChanged);
if(designChanged) {
designChanged = false;
msg.write(design->owner->id);
msg.writeSmall(design->id);
}
//Sync subsystem status
for(size_t i = 0; i < design->subsystems.size(); ++i) {
auto& ss = subsystems[i];
if(ss.status == ES_Active) {
msg.write1();
continue;
}
msg.write0();
msg << ss.status;
}
//Sync hex status
for(unsigned i = 0; i < design->usedHexCount; ++i) {
auto& hs = hexes[i];
if(hs.hp == 255 && hs.flags == HF_Active) {
msg.write1();
continue;
}
if(hs.hp == 0 && hs.flags == HF_Destroyed) {
msg.write0();
msg.write1();
continue;
}
msg.write0();
msg.write0();
msg << hs.hp;
}
//Sync effectiveness
if(shipEffectiveness != 1.f) {
msg.write1();
msg.writeFixed(shipEffectiveness, 0.0, 50.0, 16);
}
else {
msg.write0();
}
//Sync hpFactor
if(hpFactor != 1.f) {
msg.write1();
msg.writeFixed(hpFactor, 0.0, 50.0, 16);
}
else {
msg.write0();
}
//Sync removedHP
if(removedHP != 0.f) {
msg.write1();
msg.writeFixed(removedHP, 0.0, design->totalHP, 16);
}
else {
msg.write0();
}
//Sync current HP value
//msg.writeFixed(currentHP, 0, design->totalHP, 16);
//Repairing hex
if(repairingHex.x >= 0 && repairingHex.y >= 0) {
msg.write1();
msg.writeSmall(repairingHex.x);
msg.writeSmall(repairingHex.y);
}
else {
msg.write0();
}
return true;
}
void Blueprint::recvDelta(Object* obj, net::Message& msg) {
if(!design)
return;
statusID += 1;
if(msg.readBit()) {
unsigned char empID;
msg >> empID;
unsigned dsgID = msg.readSmall();
Empire* emp = Empire::getEmpireByID(empID);
create(obj, emp->getDesign(dsgID));
assert(design != nullptr);
}
destroyedHexes = 0;
//Sync subsystem status
for(size_t i = 0; i < design->subsystems.size(); ++i) {
auto& ss = subsystems[i];
ss.workingHexes = (unsigned)design->subsystems[i].hexes.size();
if(msg.readBit()) {
ss.status = ES_Active;
continue;
}
msg >> ss.status;
}
//Sync hex status
double newHP = 0;
double newQuadHP[4] = {0.0, 0.0, 0.0, 0.0};
for(unsigned i = 0; i < design->usedHexCount; ++i) {
vec2u pos = design->hexes[i];
auto& hs = hexes[i];
if(msg.readBit()) {
hs.hp = 255;
hs.flags = HF_Active;
}
else {
if(msg.readBit()) {
hs.hp = 0;
hs.flags = HF_Destroyed;
}
else {
msg >> hs.hp;
}
if(hs.flags & HF_Destroyed) {
destroyedHexes += 1;
auto* ss = getSysStatus(pos.x, pos.y);
if(ss)
ss->workingHexes -= 1;
}
}
int sysIndex = design->grid[pos];
if(sysIndex >= 0) {
int hexIndex = design->hexIndex[pos];
const float* ptr = design->subsystems[sysIndex].hexVariable(HV_HP, hexIndex);
if(ptr != nullptr) {
double curHP = double(hs.hp) / 255.0 * (*ptr);
newHP += curHP;
newQuadHP[design->getQuadrant(pos)] += curHP;
}
}
}
currentHP = newHP;
for(unsigned i = 0; i < 4; ++i)
quadrantHP[i] = newQuadHP[i];
//Sync effectiveness
if(msg.readBit())
shipEffectiveness = msg.readFixed(0.0, 50.0, 16);
else
shipEffectiveness = 1.f;
//Sync hpFactor
if(msg.readBit())
hpFactor = msg.readFixed(0.0, 50.0, 16);
else
hpFactor = 1.f;
//Sync removedHP
if(msg.readBit())
removedHP = msg.readFixed(0.0, design->totalHP, 16);
else
removedHP = 0.f;
//Read current hp value
//currentHP = msg.readFixed(0, design->totalHP, 16);
//Repairing hex
if(msg.readBit()) {
repairingHex.x = msg.readSmall();
repairingHex.y = msg.readSmall();
}
else {
repairingHex = vec2i(-1, -1);
}
}
namespace scripts {
extern SaveMessage& loadObject(SaveMessage& msg, Object** obj);
extern SaveMessage& saveObject(SaveMessage& msg, Object* obj);
};
void Blueprint::save(Object* obj, SaveMessage& file) {
file << design->owner->id << design->id;
file << currentHP << shipEffectiveness;
file << repairingHex << holdFire;
file << hpFactor << removedHP;
file.write(hexes,sizeof(HexStatus) * design->usedHexCount);
file.write(subsystems,sizeof(SysStatus) * (unsigned)design->subsystems.size());
file.write(states,sizeof(BasicType) * design->stateCount);
file.write(effectorStates,sizeof(double) * design->effectorStateCount);
for(unsigned i = 0; i < design->effectorCount; ++i) {
scripts::saveObject(file, effectorTargets[i].target);
file << effectorTargets[i].flags;
file << effectorTargets[i].tracking;
file << effectorTargets[i].hits;
}
EffectEvent event;
event.obj = obj;
for(unsigned i = 0; i < design->subsystems.size(); ++i)
design->subsystems[i].save(event, file);
}
void Blueprint::load(Object* obj, SaveMessage& file) {
try {
unsigned char dsgnOwner;
file >> dsgnOwner;
if(Empire* emp = Empire::getEmpireByID(dsgnOwner)) {
int dsgnID;
file >> dsgnID;
design = emp->getDesign(dsgnID);
if(!design)
throw SaveFileError("Invalid design");
}
else {
throw SaveFileError("Invalid design owner");
}
file >> currentHP >> shipEffectiveness;
file >> repairingHex;
if(file >= SFV_0017)
file >> holdFire;
if(file >= SFV_0021)
file >> hpFactor;
if(file >= SFV_0022)
file >> removedHP;
hexes = new HexStatus[design->usedHexCount];
file.read(hexes,sizeof(HexStatus) * design->usedHexCount);
subsystems = new SysStatus[design->subsystems.size()];
file.read(subsystems,sizeof(SysStatus) * (unsigned)design->subsystems.size());
states = new BasicType[design->stateCount];
file.read(states,sizeof(BasicType) * design->stateCount);
effectorStates = new double[design->effectorStateCount];
file.read(effectorStates,sizeof(double) * design->effectorStateCount);
effectorTargets = new EffectorTarget[design->effectorCount];
for(unsigned i = 0; i < design->effectorCount; ++i) {
effectorTargets[i].target = nullptr;
scripts::loadObject(file, &effectorTargets[i].target);
file >> effectorTargets[i].flags;
file >> effectorTargets[i].tracking;
file >> effectorTargets[i].hits;
}
data = new CScriptAny*[design->dataCount];
for(unsigned i = 0, cnt = design->dataCount; i < cnt; ++i)
data[i] = new CScriptAny(devices.scripts.server->engine);
EffectEvent event;
event.obj = obj;
for(unsigned i = 0; i < design->subsystems.size(); ++i)
design->subsystems[i].load(event, file);
for(unsigned i = 0; i < 4; ++i)
quadrantHP[i] = 0.0;
for(unsigned i = 0; i < design->usedHexCount; ++i) {
vec2u pos = design->hexes[i];
auto* hs = getHexStatus(pos.x, pos.y);
if(hs == nullptr)
continue;
int sysIndex = design->grid[pos];
if(sysIndex >= 0) {
int hexIndex = design->hexIndex[pos];
const float* ptr = design->subsystems[sysIndex].hexVariable(HV_HP, hexIndex);
if(ptr != nullptr) {
double curHP = double(hs->hp) / 255.0 * (*ptr);
quadrantHP[design->getQuadrant(pos)] += curHP;
}
}
}
}
catch(net::MessageReadError) {
throw SaveFileError("Unexpected eof");
}
}
diff --git a/source/game/scripts/bind_design.cpp b/source/game/scripts/bind_design.cpp
index 8832a8d..418a645 100644
--- a/source/game/scripts/bind_design.cpp
+++ b/source/game/scripts/bind_design.cpp
@@ -1,1638 +1,1638 @@
#include "scripts/binds.h"
#include "scripts/manager.h"
#include "design/design.h"
#include "design/effect.h"
#include "obj/blueprint.h"
#include "empire.h"
#include "main/references.h"
#include "network/network_manager.h"
#include "util/lockless_type.h"
#include "util/save_file.h"
#include "scriptarray.h"
#include "scriptany.h"
#include <assert.h>
extern double effVariable(void* effector, const std::string* name);
namespace scripts {
static HullDef* makeHull() {
return new HullDef();
}
static Shipset* makeShipset() {
return new Shipset();
}
static HullDef* makeHull_cpy(const HullDef& other) {
return new HullDef(other);
}
static void readHulls(const std::string& fname, CScriptArray& array) {
std::vector<HullDef*> hulls;
readHullDefinitions(fname, hulls);
foreach(it, hulls) {
HullDef* def = *it;
array.InsertLast(&def);
def->drop();
}
}
static void writeHulls(const std::string& fname, CScriptArray& array) {
std::vector<HullDef*> hulls;
for(unsigned i = 0, cnt = array.GetSize(); i < cnt; ++i)
hulls.push_back(*(HullDef**)array.At(i));
writeHullDefinitions(fname, hulls);
}
static void descMake(void* mem) {
new(mem) Design::Descriptor();
((Design::Descriptor*)mem)->owner = Empire::getPlayerEmpire();
}
static void descDestroy(Design::Descriptor* desc) {
desc->~Descriptor();
}
static Design::Descriptor* descCopy(Design::Descriptor* desc, const Design::Descriptor* other) {
if(other->settings)
other->settings->AddRef();
if(other->hull)
other->hull->grab();
if(desc->settings)
desc->settings->Release();
if(desc->hull)
desc->hull->drop();
*desc = *other;
return desc;
}
static unsigned descAddSys(Design::Descriptor& desc, const SubsystemDef* def) {
desc.systems.push_back(Design::Descriptor::System());
desc.systems.back().type = def;
return (unsigned)desc.systems.size() - 1;
}
static void descApply(Design::Descriptor& desc, const SubsystemDef* def) {
desc.appliedSystems.push_back(def);
}
static void descSetDirection(Design::Descriptor& desc, const vec3d& dir) {
if(desc.systems.empty()) {
scripts::throwException("No subsystem exists.");
return;
}
desc.systems.back().direction = dir;
}
static void descAddHex(Design::Descriptor& desc, unsigned index, vec2u hex) {
if(index >= desc.systems.size()) {
scripts::throwException("Subsystem index out of bounds.");
return;
}
desc.systems[index].hexes.push_back(hex);
desc.systems[index].modules.push_back(0);
}
static void descAddHex_l(Design::Descriptor& desc, vec2u hex) {
if(desc.systems.empty()) {
scripts::throwException("No subsystem exists.");
return;
}
desc.systems.back().hexes.push_back(hex);
desc.systems.back().modules.push_back(0);
}
static void descAddHex_m(Design::Descriptor& desc, unsigned index, vec2u hex, const SubsystemDef::ModuleDesc* mod) {
if(index >= desc.systems.size()) {
scripts::throwException("Subsystem index out of bounds.");
return;
}
desc.systems[index].hexes.push_back(hex);
desc.systems[index].modules.push_back(mod);
}
static void descAddHex_lm(Design::Descriptor& desc, vec2u hex, const SubsystemDef::ModuleDesc* mod) {
if(desc.systems.empty()) {
scripts::throwException("No subsystem exists.");
return;
}
desc.systems.back().hexes.push_back(hex);
desc.systems.back().modules.push_back(mod);
}
static const Design* makeDesign(const Design::Descriptor& desc) {
try {
return new Design(desc);
}
catch(const char* err) {
scripts::throwException(err);
return 0;
}
}
static bool hasSubsys(const Design* dsg, const SubsystemDef* def) {
if(!def)
return false;
for(unsigned i = 0, cnt = dsg->subsystems.size(); i < cnt; ++i)
if(dsg->subsystems[i].type == def)
return true;
return false;
}
static double quadrantTotalHP(const Design* dsg, unsigned index) {
if(index >= 4)
return 0.0;
return dsg->quadrantTotalHP[index];
}
static unsigned moduleCount(const SubsystemDef* def) {
return def->modules.size();
}
static const SubsystemDef::ModuleDesc* getModule(const SubsystemDef* def, unsigned i) {
if(i >= def->modules.size()) {
scripts::throwException("Subsystem module index out of bounds.");
return 0;
}
return def->modules[i];
}
static unsigned effectorCount(const Subsystem* sys) {
return sys->type->effectors.size();
}
static const Effector* sysGetEffector(const Subsystem* sys, unsigned i) {
if(i >= sys->type->effectors.size()) {
scripts::throwException("Effector index out of bounds.");
return 0;
}
return &sys->effectors[i];
}
static void triggerEffector(const Effector* efft, Object* obj, Object* target, float efficiency = 1.f, double tOffset = 0.0) {
EffectorTarget targ;
targ.target = target;
targ.tracking = (target->position - obj->position).normalized();
targ.flags = TF_Firing | TF_WithinFireTolerance;
targ.hits = 0;
efft->trigger(obj, targ, efficiency, tOffset);
}
static void triggerEffector_t(const Effector* efft, Object* obj, Object* target, const vec3d& tracking, float efficiency = 1.f, double tOffset = 0.0) {
EffectorTarget targ;
targ.target = target;
targ.tracking = tracking;
targ.flags = TF_Firing | TF_TrackingProgress | TF_WithinFireTolerance;
targ.hits = 0;
efft->trigger(obj, targ, efficiency, tOffset);
}
static const SubsystemDef::ModuleDesc* getModule_n(const SubsystemDef* def, std::string& name) {
auto it = def->moduleIndices.find(name);
if(it == def->moduleIndices.end())
return 0;
return def->modules[it->second];
}
static bool sysDefHasMod(const SubsystemDef* def, std::string& name) {
auto it = def->modifierIds.find(name);
return it != def->modifierIds.end();
}
static void sysvar_aGet(asIScriptGeneric* f) {
unsigned index = (unsigned)(size_t)f->GetFunction()->GetUserData();
Subsystem* sys = (Subsystem*)f->GetObject();
if(index >= sys->type->variableIndices.size() || sys->type->variableIndices[index] < 0)
scripts::throwException("Subsystem variable does not apply to this subsystem.");
float val = sys->variables[sys->type->variableIndices[index]];
f->SetReturnFloat(val);
}
static void sysvar_aSet(asIScriptGeneric* f) {
unsigned index = (unsigned)(size_t)f->GetFunction()->GetUserData();
Subsystem* sys = (Subsystem*)f->GetObject();
float val = f->GetArgFloat(0);
if(index >= sys->type->variableIndices.size() || sys->type->variableIndices[index] < 0)
scripts::throwException("Subsystem variable does not apply to this subsystem.");
sys->variables[sys->type->variableIndices[index]] = val;
}
static void subSysVar(const std::string& name, int index) {
//Enumeration
EnumBind vars("SubsystemVariable", false);
vars[std::string("SV_") + name] = index;
//Accessor
ClassBind cls("Subsystem");
cls.addGenericMethod(format("float get_$1() const", name).c_str(), asFUNCTION(sysvar_aGet), (void*)(size_t)index);
cls.addGenericMethod(format("void set_$1(float)", name).c_str(), asFUNCTION(sysvar_aSet), (void*)(size_t)index);
}
static void hexVar(const std::string& name, int index) {
EnumBind vars("HexVariable", false);
vars[std::string("HV_") + name] = index;
}
static void shipVar(const std::string& name, int index) {
EnumBind vars("ShipVariable", false);
vars[std::string("ShV_") + name] = index;
}
static float dsgSysTotal(Design* dsg, int index) {
if(index < 0)
return 0;
unsigned cnt = dsg->subsystems.size();
float value = 0.f;
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = dsg->subsystems[i];
float* sysVal = sys.variable(index);
if(sysVal)
value += *sysVal;
}
return value;
}
static float dsgHexTotal(Design* dsg, int index) {
if(index < 0)
return 0;
unsigned cnt = dsg->subsystems.size();
float value = 0.f;
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = dsg->subsystems[i];
unsigned hexCnt = sys.hexes.size();
for(unsigned j = 0; j < hexCnt; ++j) {
float* hexVal = sys.hexVariable(index, j);
if(hexVal)
value += *hexVal;
}
}
return value;
}
static float dsgSysAvg(Design* dsg, int index) {
if(index < 0)
return 0;
unsigned cnt = dsg->subsystems.size();
float value = 0.f;
unsigned systems = 0;
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = dsg->subsystems[i];
float* sysVal = sys.variable(index);
if(sysVal) {
value += *sysVal;
systems += 1;
}
}
return value / float(systems);
}
static float dsgHexAvg(Design* dsg, int index) {
if(index < 0)
return 0;
unsigned cnt = dsg->subsystems.size();
float value = 0.f;
unsigned systems = 0;
for(unsigned i = 0; i < cnt; ++i) {
auto& sys = dsg->subsystems[i];
unsigned hexCnt = sys.hexes.size();
for(unsigned j = 0; j < hexCnt; ++j) {
float* hexVal = sys.hexVariable(index, j);
if(hexVal) {
value += *hexVal;
systems += 1;
}
}
}
return value / float(systems);
}
static float sysHexTotal(Subsystem* sys, int index) {
if(index < 0)
return 0;
float value = 0.f;
unsigned hexCnt = sys->hexes.size();
for(unsigned j = 0; j < hexCnt; ++j) {
float* hexVal = sys->hexVariable(index, j);
if(hexVal)
value += *hexVal;
}
return value;
}
static float* getSysVar(Subsystem* sys, int index) {
if(index < 0) {
scripts::throwException("Invalid subsystem variable.");
return 0;
}
if(index >= (int)sys->type->variableIndices.size() || sys->type->variableIndices[index] < 0) {
scripts::throwException("Subsystem variable does not apply to this subsystem.");
return 0;
}
return &sys->variables[sys->type->variableIndices[index]];
}
static float getSysHexVar(Subsystem* sys, int index, unsigned hexIndex) {
if(index < 0) {
scripts::throwException("Invalid hex variable.");
return 0;
}
if(index >= (int)sys->type->hexVariableIndices.size() || sys->type->hexVariableIndices[index] < 0) {
scripts::throwException("Hex variable does not apply to this subsystem.");
return 0;
}
if(hexIndex >= sys->hexes.size()) {
scripts::throwException("Hexagon index out of bounds.");
return 0;
}
return *sys->hexVariable(index, hexIndex);
}
static float* getSysHexVarRef(Subsystem* sys, int index, unsigned hexIndex) {
if(index < 0) {
scripts::throwException("Invalid hex variable.");
return 0;
}
if(index >= (int)sys->type->hexVariableIndices.size() || sys->type->hexVariableIndices[index] < 0) {
scripts::throwException("Hex variable does not apply to this subsystem.");
return 0;
}
if(hexIndex >= sys->hexes.size()) {
scripts::throwException("Hexagon index out of bounds.");
return 0;
}
return sys->hexVariable(index, hexIndex);
}
static float dsgGetVar(Design* dsg, Subsystem* sys, int index) {
float* ptr = getSysVar(sys, index);
if(ptr)
return *ptr;
else
return 0.0;
}
static float* dsgVarPtr(Design* dsg, Subsystem* sys, int index) {
return getSysVar(sys, index);
}
static float dsgGetShipVar(Design* dsg, unsigned index) {
if(index >= getShipVariableCount()) {
scripts::throwException("Invalid ship variable index.");
return 0;
}
return dsg->shipVariables[index];
}
static float* dsgShipVarPtr(Design* dsg, unsigned index) {
if(index >= getShipVariableCount()) {
scripts::throwException("Invalid ship variable index.");
return 0;
}
return &dsg->shipVariables[index];
}
static float* dsgHexVarPtr(Design* dsg, const vec2u& v, int index) {
//Get subsystem
if(v.x >= dsg->grid.width || v.y >= dsg->grid.height) {
scripts::throwException("Hex position out of bounds.");
return 0;
}
int sysIndex = dsg->grid[v];
if(sysIndex < 0) {
scripts::throwException("No subsystem on hex position.");
return 0;
}
auto& sys = dsg->subsystems[sysIndex];
//Get hex variable
int hexIndex = dsg->hexIndex[v];
float* ptr = sys.hexVariable(index, hexIndex);
if(!ptr) {
scripts::throwException("Hexagon variable does not apply to this subsystem.");
return 0;
}
return ptr;
}
static float dsgGetHexVar(Design* dsg, const vec2u& v, int index) {
//Get subsystem
if(v.x >= dsg->grid.width || v.y >= dsg->grid.height)
return 0;
int sysIndex = dsg->grid[v];
if(sysIndex < 0)
return 0;
auto& sys = dsg->subsystems[sysIndex];
//Get hex variable
int hexIndex = dsg->hexIndex[v];
float* ptr = sys.hexVariable(index, hexIndex);
if(!ptr)
return 0;
return *ptr;
}
static bool dsgHasHexVar(Design* dsg, const vec2u& v, int index) {
//Get subsystem
if(v.x >= dsg->grid.width || v.y >= dsg->grid.height)
return false;
int sysIndex = dsg->grid[v];
if(sysIndex < 0)
return false;
auto& sys = dsg->subsystems[sysIndex];
//Get hex variable
int hexIndex = dsg->hexIndex[v];
float* ptr = sys.hexVariable(index, hexIndex);
if(!ptr)
return false;
return true;
}
static bool hasSysVar(Subsystem* sys, int index) {
if(index < 0)
return false;
if(index >= (int)sys->type->variableIndices.size() || sys->type->variableIndices[index] < 0)
return false;
return true;
}
static bool hasSysHexVar(Subsystem* sys, int index) {
if(index < 0)
return false;
if(index >= (int)sys->type->hexVariableIndices.size() || sys->type->hexVariableIndices[index] < 0)
return false;
return true;
}
static void dsgRename(Design* dsg, const std::string& name) {
if(!dsg->used)
dsg->name = name;
}
static int dsgGetBuilt(Design* dsg) {
return dsg->built.get();
}
static void dsgDecBuilt(Design* dsg) {
--dsg->built;
}
static void dsgIncBuilt(Design* dsg) {
++dsg->built;
}
static int dsgGetActive(Design* dsg) {
return dsg->active.get();
}
static Effect* getSysEff(Subsystem* sys, unsigned index) {
if(index >= sys->type->effects.size()) {
scripts::throwException("Subsystem effect index out of bounds.");
return 0;
}
return &sys->effects[index];
}
static void makeEvt(void* mem) {
new(mem) EffectEvent();
}
static void delEvt(EffectEvent* evt) {
evt->~EffectEvent();
}
static Blueprint* dmgBlueprint(DamageEvent& evt) {
if(evt.target == nullptr)
return nullptr;
if(evt.target->type->blueprintOffset == 0)
return nullptr;
return (Blueprint*)(((size_t)evt.target.ptr) + evt.target->type->blueprintOffset);
}
static Blueprint* evtBlueprint(EffectEvent& evt) {
if(evt.obj == nullptr)
return nullptr;
if(evt.obj->type->blueprintOffset == 0)
return nullptr;
return (Blueprint*)(((size_t)evt.obj.ptr) + evt.obj->type->blueprintOffset);
}
template<class T>
static Subsystem* evtSource(T& evt) {
if(evt.source < 0 || !evt.obj)
return 0;
if(evt.obj->type->blueprintOffset == 0)
return 0;
Blueprint* bp = (Blueprint*)(((size_t)evt.obj.ptr) + evt.obj->type->blueprintOffset);
if(!bp || evt.source >= (int)bp->design->subsystems.size())
return 0;
return (Subsystem*)&bp->design->subsystems[evt.source];
}
template<class T>
static Subsystem* evtDest(T& evt) {
if(evt.destination < 0 || !evt.target)
return 0;
if(evt.target->type->blueprintOffset == 0)
return 0;
Blueprint* bp = (Blueprint*)(((size_t)evt.target.ptr) + evt.target->type->blueprintOffset);
if(!bp || evt.destination >= (int)bp->design->subsystems.size())
return 0;
return (Subsystem*)&bp->design->subsystems[evt.destination];
}
template<class T>
static Blueprint::SysStatus* evtSourceStatus(T& evt) {
if(evt.source < 0 || !evt.obj)
return 0;
if(evt.obj->type->blueprintOffset == 0)
return 0;
Blueprint* bp = (Blueprint*)(((size_t)evt.obj.ptr) + evt.obj->type->blueprintOffset);
if(!bp || evt.source >= (int)bp->design->subsystems.size())
return 0;
return &bp->subsystems[evt.source];
}
template<class T>
static Blueprint::SysStatus* evtDestStatus(T& evt) {
if(evt.destination < 0 || !evt.target)
return 0;
if(evt.target->type->blueprintOffset == 0)
return 0;
Blueprint* bp = (Blueprint*)(((size_t)evt.target.ptr) + evt.target->type->blueprintOffset);
if(!bp || evt.destination >= (int)bp->design->subsystems.size())
return 0;
return &bp->subsystems[evt.destination];
}
static void makeDamageEvt(void* mem) {
new(mem) DamageEvent();
}
static void delDamageEvt(DamageEvent* evt) {
evt->~DamageEvent();
}
static void emptyEff(void* mem) {
new(mem) Effect();
}
static void makeEff(void* mem, unsigned effType) {
new(mem) Effect(getEffectDefinition(effType));
}
static void emptyTimed(void* mem) {
new(mem) TimedEffect();
}
static void makeTimed(void* mem, unsigned effType, double time) {
new(mem) TimedEffect(getEffectDefinition(effType), time);
}
static void delTimed(TimedEffect* evt) {
evt->~TimedEffect();
}
static void effectType(const std::string& name, int index) {
EnumBind effType("EffectType", false);
effType[std::string("ET_")+name] = index;
}
static double* effValue(Effect* eff, unsigned index) {
if(index >= EFFECT_MAX_VALUES) {
scripts::throwException("Effect value index out of bounds.");
return 0;
}
return &eff->values[index];
}
static unsigned effectCount(const SubsystemDef* def) {
return def->effects.size();
}
static unsigned sysCount(const Design* design) {
return design->subsystems.size();
}
static const Subsystem* getSys(const Design* design, unsigned i) {
if(i >= design->subsystems.size())
return 0;
return &design->subsystems[i];
}
static const Subsystem* getHexSys(const Design* design, unsigned x, unsigned y) {
if(x >= design->grid.width || y >= design->grid.height)
return 0;
int index = design->grid.get(x, y);
if(index < 0)
return 0;
return &design->subsystems[index];
}
static const Subsystem* getHexSys_v(const Design* design, const vec2u& v) {
if(v.x >= design->grid.width || v.y >= design->grid.height)
return 0;
int index = design->grid[v];
if(index < 0)
return 0;
return &design->subsystems[index];
}
static const SubsystemDef::ModuleDesc* getHexModule(const Design* design, unsigned x, unsigned y) {
if(x >= design->grid.width || y >= design->grid.height)
return 0;
int index = design->grid.get(x, y);
if(index < 0)
return 0;
int hexIndex = design->hexIndex.get(x, y);
auto& sys = design->subsystems[index];
if((unsigned)hexIndex >= sys.modules.size())
assert(false);
return sys.modules[hexIndex];
}
static const SubsystemDef::ModuleDesc* getHexModule_v(const Design* design, const vec2u& v) {
if(v.x >= design->grid.width || v.y >= design->grid.height)
return 0;
int index = design->grid[v];
if(index < 0)
return 0;
int hexIndex = design->hexIndex[v];
auto& sys = design->subsystems[index];
return sys.modules[hexIndex];
}
static int getHexIndex(const Design* design, const vec2u& v) {
if(v.x >= design->grid.width || v.y >= design->grid.height)
return -1;
int index = design->grid.get(v.x, v.y);
if(index < 0)
return -1;
int hexIndex = design->hexIndex.get(v.x, v.y);
return hexIndex;
}
static int getHexStatusIndex(const Design* design, const vec2u& v) {
if(v.x >= design->grid.width || v.y >= design->grid.height)
return -1;
int index = design->grid.get(v.x, v.y);
if(index < 0)
return -1;
int hexIndex = design->hexStatusIndex.get(v.x, v.y);
return hexIndex;
}
static bool isValidhex(const Design* design, const vec2u& v) {
if(v.x >= design->grid.width || v.y >= design->grid.height)
return false;
return true;
}
static void setDesignObsolete(const Design* design, bool value) {
design->obsolete = value;
}
static void setDesignData(Design* design, asIScriptObject* obj) {
auto* man = getActiveManager();
if(design->data != nullptr) {
scripts::throwException("Setting design data on design that already has data.");
return;
}
design->data = new net::Message();
auto* writeFunc = (asIScriptFunction*)man->engine->GetUserData(scripts::EDID_SerializableWrite);
if(writeFunc) {
scripts::Call cl = man->call(writeFunc);
cl.setObject(obj);
cl.push(design->data);
cl.call();
}
design->bindData();
}
static unsigned hexCount(const Subsystem* sys) {
return sys->hexes.size();
}
static vec2u getHex(const Subsystem* sys, unsigned i) {
if(i >= sys->hexes.size()) {
scripts::throwException("Subsystem hex index out of bounds.");
return vec2u();
}
return sys->hexes[i];
}
static const SubsystemDef::ModuleDesc* getSysModule(const Subsystem* sys, unsigned i) {
if(i >= sys->modules.size()) {
scripts::throwException("Subsystem hex index out of bounds.");
return 0;
}
return sys->modules[i];
}
static unsigned designCount(const DesignClass* cls) {
return cls->designs.size();
}
static const Design* getDesign(const DesignClass* cls, unsigned i) {
if(i >= cls->designs.size()) {
scripts::throwException("Design index out of bounds.");
return 0;
}
const Design* dsg = cls->designs[i];
if(dsg)
dsg->grab();
return dsg;
}
static unsigned dsgErrorCount(const Design* dsg) {
return dsg->errors.size();
}
static const DesignError* dsgError(const Design* dsg, unsigned index) {
if(index >= dsg->errors.size()) {
scripts::throwException("Design error index out of bounds.");
return 0;
}
return &dsg->errors[index];
}
static void dsgAddError(Design* dsg, bool fatal, const std::string& text,
const Subsystem* sys, const SubsystemDef::ModuleDesc* module,
vec2u hex) {
dsg->errors.push_back(DesignError(fatal, text, sys, module, hex));
}
static void dsgAddErrorHex(Design* dsg, const vec2u& hex) {
dsg->errorHexes.insert((uint64_t)hex.x << 32 | (uint64_t)hex.y);
}
static bool dsgIsErrorHex(Design* dsg, const vec2u& hex) {
if(dsg->errorHexes.size() == 0)
return false;
auto it = dsg->errorHexes.find((uint64_t)hex.x << 32 | (uint64_t)hex.y);
return it != dsg->errorHexes.end();
}
static Color effTrailStart(const EffectorDef& def) {
return def.skins[0].trailStart;
}
static Color effTrailEnd(const EffectorDef& def) {
return def.skins[0].trailEnd;
}
static int effArg(const EffectorDef& def, const std::string& name) {
auto it = def.valueNames.find(name);
if(it == def.valueNames.end())
return -1;
return (int)it->second;
}
static double ERR_DOUBLE = 1e30;
class ScriptEffector : public Effector {
public:
ScriptEffector(const EffectorDef& def) : Effector(def) {
turretAngle = vec3d::front();
relativePosition = vec3d();
for(unsigned i = 0; i < type.valueCount; ++i) {
if(type.values[i].defaultValue)
values[i] = type.values[i].defaultValue->evaluate(effVariable, this);
else
values[i] = 0.0;
}
registerEffector(this);
}
double& getValue(const std::string& name) {
auto it = type.valueNames.find(name);
if(it == type.valueNames.end()) {
scripts::throwException("Value not found.");
return ERR_DOUBLE;
}
return values[it->second];
}
double& getValueByIndex(unsigned index) {
if(index >= type.valueCount) {
scripts::throwException("Value out of bounds.");
return ERR_DOUBLE;
}
return values[index];
}
void evaluate() {
initValues();
if(devices.network->isServer)
devices.network->sendEffectorUpdate(this);
}
};
static ScriptEffector* makeScriptEff(const EffectorDef& def) {
return new ScriptEffector(def);
}
static const EffectorDef* getEffDefType(const Effector& eff) {
return &eff.type;
}
class ScriptTurret {
mutable threads::atomic_int refs;
public:
const Effector& type;
double* states;
EffectorTarget target;
ScriptTurret(const Effector& efftr) : type(efftr), refs(1) {
type.grab();
states = new double[type.type.stateCount]();
- memset(&target, 0, sizeof(EffectorTarget));
+ memset(reinterpret_cast<void *>(&target), 0, sizeof(EffectorTarget));
target.tracking = vec3d::front();
}
~ScriptTurret() {
type.drop();
}
void grab() const {
++refs;
}
void drop() const {
if(!--refs)
delete this;
}
void update(Object* obj, double time, float efficiency) {
type.update(obj, time, states, target, efficiency);
}
void trigger(Object* obj, Object* targObj, float efficiency, double tOffset = 0) {
EffectorTarget ctarg = target;
ctarg.target = targObj;
type.trigger(obj, ctarg, efficiency, tOffset);
}
void save(SaveMessage& file) {
if(type.effectorId != 0) {
file.write0();
file << type.effectorId;
}
else {
file.write1();
file << type.inDesign->owner->id;
file << type.inDesign->id;
file << type.subsysIndex;
file << type.effectorIndex;
}
file.write(states, sizeof(double) * type.type.stateCount);
scripts::saveObject(file, target.target);
file << target.flags;
file << target.tracking;
file << target.hits;
}
};
static ScriptTurret* makeScriptTurret(const Effector& efftr) {
return new ScriptTurret(efftr);
}
static ScriptTurret* loadScriptTurret(SaveMessage& file) {
const Effector* efftr = nullptr;
if(!file.readBit()) {
unsigned id = 0;
file >> id;
efftr = getEffector(id);
}
else {
unsigned char empID;
file >> empID;
if(empID == INVALID_EMPIRE)
return nullptr;
int dsgId;
unsigned subsysIndex;
unsigned effectorIndex;
file >> dsgId >> subsysIndex >> effectorIndex;
Empire* dsgOwner = Empire::getEmpireByID(empID);
const Design* dsg = dsgOwner->getDesign(dsgId);
if(subsysIndex >= dsg->subsystems.size() || effectorIndex >= dsg->subsystems[subsysIndex].type->effectors.size())
throw "Invalid turret effector.";
efftr = &dsg->subsystems[subsysIndex].effectors[effectorIndex];
}
ScriptTurret* turr = new ScriptTurret(*efftr);
file.read(turr->states, sizeof(double) * efftr->type.stateCount);
scripts::loadObject(file, &turr->target.target);
file >> turr->target.flags;
file >> turr->target.tracking;
file >> turr->target.hits;
efftr->drop();
return turr;
}
static unsigned strvec_length(std::vector<std::string>& vec) {
return vec.size();
}
const std::string ERRSTR = "ERR";
static const std::string& strvec_get(std::vector<std::string>& vec, unsigned index) {
if(index >= vec.size())
return ERRSTR;
return vec[index];
}
static unsigned dblvec_length(std::vector<double>& vec) {
return vec.size();
}
static double dblvec_get(std::vector<double>& vec, unsigned index) {
if(index >= vec.size())
return 0.0;
return vec[index];
}
static void ssevtMake(void* mem) {
new(mem) SubsystemEvent();
memset(mem, 0, sizeof(SubsystemEvent));
}
static void ssevtDestroy(SubsystemEvent* mem) {
if(mem->data)
((CScriptAny*)mem->data)->Release();
if(mem->design)
mem->design->drop();
if(mem->obj)
mem->obj->drop();
mem->~SubsystemEvent();
}
static unsigned sysHookCount(const Subsystem* sys) {
return sys->hookClasses.size();
}
static asIScriptObject* sysHookGet(const Subsystem* sys, unsigned index) {
if(index >= sys->hookClasses.size())
return nullptr;
auto* obj = sys->hookClasses[index];
if(obj)
obj->AddRef();
return obj;
}
static unsigned getSSTag(const std::string& tag) {
return getSysTagIndex(tag);
}
void RegisterDesignBinds(bool server, bool declarations) {
if(declarations) {
ClassBind hull("Hull", asOBJ_REF);
ClassBind shipset("Shipset", asOBJ_REF);
ClassBind ds("Design", asOBJ_REF);
ClassBind dc("DesignClass", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind evt("Event", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CD, sizeof(EffectEvent));
ClassBind dev("DamageEvent", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CD, sizeof(DamageEvent));
ClassBind def("EffectDef", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind deftr("EffectorDef", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind eff("Effect", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C, sizeof(Effect));
ClassBind efftr("Effector", asOBJ_REF);
ClassBind turr("Turret", asOBJ_REF);
ClassBind timed("TimedEffect", asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CD, sizeof(TimedEffect));
ClassBind sysdef("SubsystemDef", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind moddef("ModuleDef", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind sys("Subsystem", asOBJ_REF | asOBJ_NOCOUNT);
ClassBind desc("DesignDescriptor", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(Design::Descriptor));
return;
}
//Tags
EnumBind tags("SubsystemTag");
tags["ST_NULL"] = -1;
enumerateSysTags([&tags](const std::string& name, int index) {
tags[std::string("ST_")+name] = index;
});
bind("SubsystemTag getSubsystemTag(const string& tag)", asFUNCTION(getSSTag));
//Shipsets
ClassBind shipset("Shipset");
shipset.addFactory("Shipset@ Hull()", asFUNCTION(makeShipset));
shipset.addBehaviour(asBEHAVE_ADDREF, "void f()", asMETHOD(Shipset, grab));
shipset.addBehaviour(asBEHAVE_RELEASE, "void f()", asMETHOD(Shipset, drop));
shipset.addMember("uint id", offsetof(Shipset, id));
shipset.addMember("string ident", offsetof(Shipset, ident));
shipset.addMember("string name", offsetof(Shipset, name));
shipset.addMember("string dlc", offsetof(Shipset, dlc));
shipset.addMember("bool available", offsetof(Shipset, available));
shipset.addMethod("uint get_hullCount() const", asMETHOD(Shipset, getHullCount));
shipset.addMethod("bool hasHull(const Hull& hull) const", asMETHOD(Shipset, hasHull));
shipset.addMethod("const Hull@+ get_hulls(uint index) const", asMETHODPR(Shipset, getHull, (unsigned) const, const HullDef*));
shipset.addMethod("const Hull@+ getHull(const string& ident) const", asMETHODPR(Shipset, getHull, (const std::string&) const, const HullDef*));
bind("uint getShipsetCount()", asFUNCTION(getShipsetCount));
bind("const Shipset& getShipset(uint id)", asFUNCTIONPR(getShipset, (unsigned), const Shipset*));
bind("const Shipset& getShipset(const string& ident)", asFUNCTIONPR(getShipset, (const std::string&), const Shipset*));
//Ship skins
ClassBind shipskin("ShipSkin", asOBJ_REF | asOBJ_NOCOUNT);
shipskin.addMember("string ident", offsetof(ShipSkin, ident));
shipskin.addMember("const Material@ material", offsetof(ShipSkin, material));
shipskin.addMember("const Model@ model", offsetof(ShipSkin, mesh));
shipskin.addMember("Sprite icon", offsetof(ShipSkin, icon));
shipset.addMethod("const ShipSkin& getSkin(const string& name) const", asMETHOD(Shipset, getSkin));
//Hull shape
ClassBind hull("Hull");
hull.addFactory("Hull@ Hull()", asFUNCTION(makeHull));
hull.addFactory("Hull@ Hull(const Hull& other)", asFUNCTION(makeHull_cpy));
hull.addBehaviour(asBEHAVE_ADDREF, "void f()", asMETHOD(HullDef, grab));
hull.addBehaviour(asBEHAVE_RELEASE, "void f()", asMETHOD(HullDef, drop));
hull.addMember("uint id", offsetof(HullDef, id));
hull.addMember("string ident", offsetof(HullDef, ident));
hull.addMember("string name", offsetof(HullDef, name));
hull.addMember("string backgroundName", offsetof(HullDef, backgroundName));
hull.addMember("string modelName", offsetof(HullDef, meshName));
hull.addMember("string materialName", offsetof(HullDef, materialName));
hull.addMember("string iconName", offsetof(HullDef, iconName));
hull.addMember("const Material@ background", offsetof(HullDef, background));
hull.addMember("const Material@ material", offsetof(HullDef, material));
hull.addMember("const Model@ model", offsetof(HullDef, mesh));
hull.addMember("const SpriteSheet@ iconSheet", offsetof(HullDef, iconSheet));
hull.addMember("Sprite guiIcon", offsetof(HullDef, guiIcon));
hull.addMember("Sprite fleetIcon", offsetof(HullDef, fleetIcon));
hull.addMember("uint iconIndex", offsetof(HullDef, iconIndex));
hull.addMember("vec2i gridSize", offsetof(HullDef, gridSize));
hull.addMember("recti gridOffset", offsetof(HullDef, gridOffset));
hull.addMember("double minSize", offsetof(HullDef, minSize));
hull.addMember("double maxSize", offsetof(HullDef, maxSize));
hull.addMember("HexGridi exterior", offsetof(HullDef, exterior));
hull.addMember("HexGridb active", offsetof(HullDef, active));
hull.addMember("double backgroundScale", offsetof(HullDef, backgroundScale));
hull.addMember("double modelScale", offsetof(HullDef, modelScale));
hull.addMember("uint activeCount", offsetof(HullDef, activeCount));
hull.addMember("uint exteriorCount", offsetof(HullDef, exteriorCount));
hull.addMember("Hull@ baseHull", offsetof(HullDef, baseHull));
hull.addMember("bool special", offsetof(HullDef, special));
hull.addMethod("Hull& opAssign(const Hull&in other)", asMETHOD(HullDef, operator=));
hull.addMethod("bool hasTag(const string&in tag) const", asMETHOD(HullDef, hasTag));
hull.addMethod("bool isExterior(const vec2u& hex) const", asMETHOD(HullDef, isExterior));
hull.addMethod("bool isExteriorInDirection(const vec2u& hex, HexGridAdjacency adj) const", asMETHOD(HullDef, isExteriorInDirection));
hull.addMethod("double getMatchDistance(const vec2d& pos) const",
asMETHODPR(HullDef, getMatchDistance, (const vec2d&) const, double));
hull.addMethod("double getMatchDistance(const DesignDescriptor& desc) const",
asMETHODPR(HullDef, getMatchDistance, (void*) const, double));
bind("uint getHullCount()", asFUNCTION(getHullCount));
bind("const Hull@+ getHullDefinition(uint id)",
asFUNCTIONPR(getHullDefinition, (unsigned), const HullDef*));
bind("const Hull@+ getHullDefinition(const string &in ident)",
asFUNCTIONPR(getHullDefinition, (const std::string&), const HullDef*));
bind("void readHullDefinitions(const string&in filename, array<Hull@>& hulls)", asFUNCTION(readHulls));
bind("void writeHullDefinitions(const string&in filename, array<Hull@>& hulls)", asFUNCTION(writeHulls));
//Effect definitions
EnumBind status("EffectStatus");
status["ES_Active"] = ES_Active;
status["ES_Suspended"] = ES_Suspended;
status["ES_Ended"] = ES_Ended;
EnumBind effType("EffectType");
enumerateEffectDefinitions(effectType);
ClassBind def("EffectDef");
def.addMember("string name", offsetof(EffectDef, name));
def.addMember("EffectType type", offsetof(EffectDef, id));
def.addMember("uint valueCount", offsetof(EffectDef, valueCount));
bind("const EffectDef@ getEffectDefinition(EffectType type)", asFUNCTIONPR(getEffectDefinition, (int), const EffectDef*));
bind("const EffectDef@ getEffectDefinition(const string &in name)", asFUNCTIONPR(getEffectDefinition, (const std::string&), const EffectDef*));
//Event data
ClassBind evt("Event");
evt.addConstructor("void f()", asFUNCTION(makeEvt));
evt.addExternBehaviour(asBEHAVE_DESTRUCT, "void f()", asFUNCTION(delEvt));
evt.addMember("double time", offsetof(EffectEvent, time));
evt.addMember("vec3d impact", offsetof(EffectEvent, impact))
doc("Impact location relative to the object.");
evt.addMember("float efficiency", offsetof(EffectEvent, efficiency));
evt.addMember("float partiality", offsetof(EffectEvent, partiality));
evt.addMember("float workingPercent", offsetof(EffectEvent, partiality));
evt.addMember("float custom1", offsetof(EffectEvent, custom1));
evt.addMember("float custom2", offsetof(EffectEvent, custom2));
evt.addMember("Object@ obj", offsetof(EffectEvent, obj));
evt.addMember("Object@ target", offsetof(EffectEvent, target));
evt.addMember("EffectStatus status", offsetof(EffectEvent, status));
evt.addMember("int source_index", offsetof(EffectEvent, source));
evt.addMember("int destination_index", offsetof(EffectEvent, destination));
evt.addMember("vec2d direction", offsetof(EffectEvent, direction));
evt.addExternMethod("Blueprint@ get_blueprint()", asFUNCTION(evtBlueprint));
evt.addExternMethod("const Subsystem@ get_source()", asFUNCTION(evtSource<EffectEvent>));
evt.addExternMethod("const Subsystem@ get_destination()", asFUNCTION(evtDest<EffectEvent>));
evt.addExternMethod("SysStatus@ get_source_status()", asFUNCTION(evtSourceStatus<EffectEvent>));
evt.addExternMethod("SysStatus@ get_destination_status()", asFUNCTION(evtDestStatus<EffectEvent>));
EnumBind des("DamageEventStatus");
des["DE_Continue"] = DE_Continue;
des["DE_SkipHex"] = DE_SkipHex;
des["DE_EndDamage"] = DE_EndDamage;
EnumBind dmgflag("DamageFlags");
dmgflag["DF_Flag1"] = 1;
dmgflag["DF_Flag2"] = 2;
dmgflag["DF_Flag3"] = 4;
dmgflag["DF_Flag4"] = 8;
dmgflag["DF_Flag5"] = 0x10;
dmgflag["DF_Flag6"] = 0x20;
dmgflag["DF_Flag7"] = 0x40;
dmgflag["DF_Flag8"] = 0x80;
dmgflag["DF_DestroyedObject"] = DF_DestroyedObject;
//Damage event data
ClassBind dev("DamageEvent");
dev.addConstructor("void f()", asFUNCTION(makeDamageEvt));
dev.addExternBehaviour(asBEHAVE_DESTRUCT, "void f()", asFUNCTION(delDamageEvt));
dev.addMember("vec3d impact", offsetof(DamageEvent, impact))
doc("Impact location relative to the object.");
dev.addMember("double damage", offsetof(DamageEvent, damage));
dev.addMember("float pierce", offsetof(DamageEvent, pierce));
dev.addMember("float partiality", offsetof(DamageEvent, partiality));
dev.addMember("float custom1", offsetof(DamageEvent, custom1));
dev.addMember("float custom2", offsetof(DamageEvent, custom2));
dev.addMember("uint flags", offsetof(DamageEvent, flags));
dev.addMember("Object@ obj", offsetof(DamageEvent, obj));
dev.addMember("Object@ target", offsetof(DamageEvent, target));
dev.addMember("int source_index", offsetof(DamageEvent, source));
dev.addMember("int destination_index", offsetof(DamageEvent, destination));
dev.addMember("bool spillable", offsetof(DamageEvent, spillable));
dev.addExternMethod("Blueprint@ get_blueprint()", asFUNCTION(dmgBlueprint));
dev.addExternMethod("const Subsystem@ get_source()", asFUNCTION(evtSource<DamageEvent>));
dev.addExternMethod("const Subsystem@ get_destination()", asFUNCTION(evtDest<DamageEvent>));
dev.addExternMethod("SysStatus@ get_source_status()", asFUNCTION(evtSourceStatus<DamageEvent>));
dev.addExternMethod("SysStatus@ get_destination_status()", asFUNCTION(evtDestStatus<DamageEvent>));
//Instantiated effects
ClassBind eff("Effect");
eff.addConstructor("void f()", asFUNCTION(emptyEff));
eff.addConstructor("void f(EffectType type)", asFUNCTION(makeEff));
eff.addExternMethod("double& opIndex(uint num)", asFUNCTION(effValue));
eff.addMember("const EffectDef@ type", offsetof(Effect, type));
eff.addMember("double value0", offsetof(Effect, values[0]));
eff.addMember("double value1", offsetof(Effect, values[1]));
eff.addMember("double value2", offsetof(Effect, values[2]));
eff.addMember("double value3", offsetof(Effect, values[3]));
eff.addMember("double value4", offsetof(Effect, values[4]));
eff.addMember("double value5", offsetof(Effect, values[5]));
//Timed effect
ClassBind timed("TimedEffect");
timed.addConstructor("void f()", asFUNCTION(emptyTimed));
timed.addConstructor("void f(EffectType type, double time)", asFUNCTION(makeTimed));
timed.addExternBehaviour(asBEHAVE_DESTRUCT, "void f()", asFUNCTION(delTimed));
timed.addMember("double remaining", offsetof(TimedEffect, remaining));
timed.addMember("Effect effect", offsetof(TimedEffect, effect));
timed.addMember("Event event", offsetof(TimedEffect, event));
//Effector definitions
ClassBind effd("EffectorDef");
effd.addMember("uint id", offsetof(EffectorDef, index));
effd.addMember("string name", offsetof(EffectorDef, name));
effd.addMember("uint valueCount", offsetof(EffectorDef, valueCount));
effd.addExternMethod("Color get_trailStart() const", asFUNCTION(effTrailStart));
effd.addExternMethod("Color get_trailEnd() const", asFUNCTION(effTrailEnd));
effd.addExternMethod("int getArgumentIndex(const string& name)", asFUNCTION(effArg));
{
Namespace ns("effector");
for(int i = 0, cnt = getEffectorDefinitionCount(); i < cnt; ++i) {
const EffectorDef* def = getEffectorDefinition(i);
bindGlobal(format("const ::EffectorDef $1", def->name).c_str(), (void*)def);
}
}
bind("EffectorDef@ getEffectorDef(uint id)", asFUNCTIONPR(getEffectorDefinition, (unsigned), const EffectorDef*));
bind("EffectorDef@ getEffectorDef(const string& ident)", asFUNCTIONPR(getEffectorDefinition, (const std::string&), const EffectorDef*));
//Script effectors
ClassBind efft("Effector");
efft.setReferenceFuncs(asMETHOD(ScriptEffector, grab), asMETHOD(ScriptEffector, drop));
efft.addMember("vec3d turretAngle", offsetof(Effector, turretAngle));
efft.addMember("vec3d relativePosition", offsetof(Effector, relativePosition));
efft.addMember("const double fireArc", offsetof(Effector, fireArc));
efft.addMember("const double targetTolerance", offsetof(Effector, targetTolerance));
efft.addMember("const double fireTolerance", offsetof(Effector, fireTolerance));
efft.addMember("const double range", offsetof(Effector, range));
efft.addMember("const double lifetime", offsetof(Effector, lifetime));
efft.addMember("const double tracking", offsetof(Effector, tracking));
efft.addMember("const double speed", offsetof(Effector, speed));
efft.addMember("const double spread", offsetof(Effector, spread));
efft.addMember("const uint capTarget", offsetof(Effector, capTarget));
if(server) {
efft.addFactory("Effector@ f(const EffectorDef& def)", asFUNCTION(makeScriptEff));
efft.addExternMethod("const EffectorDef@ get_type() const", asFUNCTION(getEffDefType));
efft.addMember("uint id", offsetof(Effector, effectorId));
efft.addMember("uint sysIndex", offsetof(Effector, subsysIndex));
efft.addMember("uint index", offsetof(Effector, effectorIndex));
efft.addMember("double relativeSize", offsetof(Effector, relativeSize));
efft.addMethod("double& opIndex(uint index)", asMETHOD(ScriptEffector, getValueByIndex));
efft.addMethod("double& opIndex(const string& name)", asMETHOD(ScriptEffector, getValue));
efft.addMethod("void evaluate()", asMETHOD(ScriptEffector, evaluate));
efft.addExternMethod("void trigger(Object& obj, Object& target, float efficiency = 1.f, double tOffset = 0.0) const", asFUNCTION(triggerEffector));
efft.addExternMethod("void trigger(Object& obj, Object& target, const vec3d& tracking, float efficiency = 1.f, double tOffset = 0.0) const", asFUNCTION(triggerEffector_t));
}
bind("Effector@ getEffectorByID(uint id)", asFUNCTION(getEffector));
//Script turrets
ClassBind turr("Turret");
turr.setReferenceFuncs(asMETHOD(ScriptTurret, grab), asMETHOD(ScriptTurret, drop));
if(server) {
turr.addFactory("Turret@ f(const Effector& efftr)", asFUNCTION(makeScriptTurret));
turr.addFactory("Turret@ f(SaveFile& file)", asFUNCTION(loadScriptTurret));
turr.addMember("Object@ target", offsetof(ScriptTurret, target)+offsetof(EffectorTarget, target));
turr.addMember("vec3d tracking", offsetof(ScriptTurret, target)+offsetof(EffectorTarget, tracking));
turr.addMember("uint8 hits", offsetof(ScriptTurret, target)+offsetof(EffectorTarget, hits));
turr.addMember("uint flags", offsetof(ScriptTurret, target)+offsetof(EffectorTarget, flags));
turr.addMethod("void update(Object& obj, double time, float efficiency = 1.f)", asMETHOD(ScriptTurret, update));
turr.addMethod("void trigger(Object& obj, Object& target, float efficiency = 1.f, double tOffset = 0.0)", asMETHOD(ScriptTurret, trigger));
turr.addMethod("void save(SaveFile& file)", asMETHOD(ScriptTurret, save));
}
EnumBind tfl("TurretFlags");
tfl["TF_Target"] = TF_Target;
tfl["TF_Group"] = TF_Group;
tfl["TF_Preference"] = TF_Preference;
tfl["TF_Firing"] = TF_Firing;
tfl["TF_Retarget"] = TF_Retarget;
tfl["TF_TrackingProgress"] = TF_TrackingProgress;
tfl["TF_ClearTracking"] = TF_ClearTracking;
tfl["TF_WithinFireTolerance"] = TF_WithinFireTolerance;
//Subsystem definitions
ClassBind sysdef("SubsystemDef");
sysdef.addMember("string id", offsetof(SubsystemDef, id));
sysdef.addMember("string name", offsetof(SubsystemDef, name));
sysdef.addMember("string description", offsetof(SubsystemDef, description));
sysdef.addMember("int elevation", offsetof(SubsystemDef, elevation));
sysdef.addMember("Color color", offsetof(SubsystemDef, baseColor));
sysdef.addMember("Color typeColor", offsetof(SubsystemDef, typeColor));
sysdef.addMember("bool hasCore", offsetof(SubsystemDef, hasCore));
sysdef.addMember("bool passExterior", offsetof(SubsystemDef, passExterior));
sysdef.addMember("bool fauxExterior", offsetof(SubsystemDef, fauxExterior));
sysdef.addMember("bool isHull", offsetof(SubsystemDef, isHull));
sysdef.addMember("bool isApplied", offsetof(SubsystemDef, isApplied));
sysdef.addMember("bool isContiguous", offsetof(SubsystemDef, isContiguous));
sysdef.addMember("bool exteriorCore", offsetof(SubsystemDef, exteriorCore));
sysdef.addMember("bool defaultUnlock", offsetof(SubsystemDef, defaultUnlock));
sysdef.addMember("const Sprite picture", offsetof(SubsystemDef, picture));
sysdef.addMember("int index", offsetof(SubsystemDef, index));
sysdef.addMember("const ModuleDef@ coreModule", offsetof(SubsystemDef, coreModule));
sysdef.addMember("const ModuleDef@ defaultModule", offsetof(SubsystemDef, defaultModule));
sysdef.addExternMethod("uint get_effectCount() const", asFUNCTION(effectCount));
sysdef.addMethod("bool hasTag(const string &in tag) const", asMETHODPR(SubsystemDef, hasTag, (const std::string&) const, bool));
sysdef.addMethod("bool hasTag(SubsystemTag tag) const", asMETHODPR(SubsystemDef, hasTag, (int) const, bool));
sysdef.addMethod("const string& getTagValue(SubsystemTag tag, uint index = 0) const", asMETHOD(SubsystemDef, getTagValue));
sysdef.addMethod("uint getTagValueCount(SubsystemTag tag) const", asMETHOD(SubsystemDef, getTagValueCount));
sysdef.addMethod("bool hasTagValue(SubsystemTag tag, const string& value) const", asMETHOD(SubsystemDef, hasTagValue));
sysdef.addMethod("bool hasHullTag(const string &in tag) const", asMETHOD(SubsystemDef, hasHullTag));
sysdef.addMethod("bool canUseOn(const Hull@ hull) const", asMETHOD(SubsystemDef, canUseOn));
sysdef.addExternMethod("bool hasModifier(const string&in mod) const", asFUNCTION(sysDefHasMod));
sysdef.addExternMethod("uint get_moduleCount() const", asFUNCTION(moduleCount));
sysdef.addExternMethod("const ModuleDef@ get_modules(uint index) const", asFUNCTION(getModule));
sysdef.addExternMethod("const ModuleDef@ module(const string&in) const", asFUNCTION(getModule_n));
bind("int getSubsystemDefCount()", asFUNCTION(getSubsystemDefCount));
bind("const SubsystemDef@ getSubsystemDef(int index)", asFUNCTIONPR(getSubsystemDef, (int), const SubsystemDef*));
bind("const SubsystemDef@ getSubsystemDef(const string& id)", asFUNCTIONPR(getSubsystemDef, (const std::string&), const SubsystemDef*));
{
Namespace ns("subsystem");
for(int i = 0, cnt = getSubsystemDefCount(); i < cnt; ++i) {
const SubsystemDef* def = getSubsystemDef(i);
bindGlobal(format("const ::SubsystemDef $1", def->id).c_str(), (void*)def);
}
}
//Module description
ClassBind mod("ModuleDef");
mod.addMember("int index", offsetof(SubsystemDef::ModuleDesc, index));
mod.addMember("string id", offsetof(SubsystemDef::ModuleDesc, id));
mod.addMember("string name", offsetof(SubsystemDef::ModuleDesc, name));
mod.addMember("string description", offsetof(SubsystemDef::ModuleDesc, description));
mod.addMember("Color color", offsetof(SubsystemDef::ModuleDesc, color));
mod.addMember("bool required", offsetof(SubsystemDef::ModuleDesc, required));
mod.addMember("bool unique", offsetof(SubsystemDef::ModuleDesc, unique));
mod.addMember("bool vital", offsetof(SubsystemDef::ModuleDesc, vital));
mod.addMember("bool defaultUnlock", offsetof(SubsystemDef::ModuleDesc, defaultUnlock));
mod.addMember("const Sprite sprite", offsetof(SubsystemDef::ModuleDesc, sprite));
mod.addMember("int drawMode", offsetof(SubsystemDef::ModuleDesc, drawMode));
mod.addMethod("bool hasTag(const string &in tag) const", asMETHODPR(SubsystemDef::ModuleDesc, hasTag, (const std::string&) const, bool));
mod.addMethod("bool hasTag(SubsystemTag tag) const", asMETHODPR(SubsystemDef::ModuleDesc, hasTag, (int) const, bool));
mod.addMethod("const string& getTagValue(SubsystemTag tag, uint index = 0) const", asMETHOD(SubsystemDef::ModuleDesc, getTagValue));
mod.addMethod("uint getTagValueCount(SubsystemTag tag) const", asMETHOD(SubsystemDef::ModuleDesc, getTagValueCount));
mod.addMethod("bool hasTagValue(SubsystemTag tag, const string& value) const", asMETHOD(SubsystemDef::ModuleDesc, hasTagValue));
EnumBind vars("SubsystemVariable");
EnumBind hexvars("HexVariable");
EnumBind shipvars("ShipVariable");
bind("SubsystemVariable getSubsystemVariable(const string&in name)", asFUNCTION(getVariableIndex));
bind("HexVariable getHexVariable(const string&in name)", asFUNCTION(getHexVariableIndex));
bind("ShipVariable getShipVariable(const string&in name)", asFUNCTION(getShipVariableIndex));
//Subsystem instances
ClassBind sys("Subsystem");
sys.addMember("const SubsystemDef@ type", offsetof(Subsystem, type));
sys.addMember("vec2u core", offsetof(Subsystem, core));
sys.addMember("bool hasErrors", offsetof(Subsystem, hasErrors));
sys.addMember("int exteriorHexes", offsetof(Subsystem, exteriorHexes));
sys.addMember("vec3d direction", offsetof(Subsystem, direction));
sys.addMember("const Design@ inDesign", offsetof(Subsystem, inDesign));
sys.addMember("uint index", offsetof(Subsystem, index));
sys.addMember("uint dataOffset", offsetof(Subsystem, dataOffset));
sys.addExternMethod("const float& opIndex(SubsystemVariable var) const", asFUNCTION(getSysVar));
sys.addExternMethod("float& opIndex(SubsystemVariable var)", asFUNCTION(getSysVar));
sys.addExternMethod("bool has(SubsystemVariable var) const", asFUNCTION(hasSysVar));
sys.addExternMethod("bool has(HexVariable var) const", asFUNCTION(hasSysHexVar));
sys.addExternMethod("const Effect& opIndex(uint ind) const", asFUNCTION(getSysEff));
sys.addExternMethod("Effect& opIndex(uint ind)", asFUNCTION(getSysEff));
sys.addExternMethod("uint get_effectorCount() const", asFUNCTION(effectorCount));
sys.addExternMethod("const Effector@ get_effectors(uint index) const", asFUNCTION(sysGetEffector));
sys.addExternMethod("float total(HexVariable var) const", asFUNCTION(sysHexTotal));
sys.addExternMethod("uint get_hexCount() const", asFUNCTION(hexCount));
sys.addExternMethod("vec2u hexagon(uint i) const", asFUNCTION(getHex));
sys.addExternMethod("const ModuleDef@ module(uint i) const", asFUNCTION(getSysModule));
sys.addExternMethod("const float hexVariable(HexVariable, uint) const", asFUNCTION(getSysHexVar));
sys.addExternMethod("float& hexVariable(HexVariable, uint)", asFUNCTION(getSysHexVarRef));
sys.addExternMethod("const float& variable(SubsystemVariable) const", asFUNCTION(getSysVar));
sys.addExternMethod("float& variable(SubsystemVariable)", asFUNCTION(getSysVar));
//Bind subsystem variables
enumerateVariables(subSysVar);
enumerateHexVariables(hexVar);
enumerateShipVariables(shipVar);
//Designs based on hull shapes
ClassBind desc("DesignDescriptor");
ClassBind ds("Design");
ds.addBehaviour(asBEHAVE_ADDREF, "void f()", asMETHOD(Design, grab));
ds.addBehaviour(asBEHAVE_RELEASE, "void f()", asMETHOD(Design, drop));
ds.addMember("int id", offsetof(Design, id));
ds.addMember("string name", offsetof(Design, name));
ds.addMember("const Hull@ hull", offsetof(Design, hull));
ds.addMember("double size", offsetof(Design, size));
ds.addMember("double hexSize", offsetof(Design, hexSize));
ds.addMember("bool obsolete", offsetof(Design, obsolete));
ds.addMember("uint interiorHexes", offsetof(Design, interiorHexes));
ds.addMember("uint exteriorHexes", offsetof(Design, exteriorHexes));
ds.addMember("uint usedHexCount", offsetof(Design, usedHexCount));
ds.addMember("uint dataCount", offsetof(Design, dataCount));
ds.addMember("uint effectorCount", offsetof(Design, effectorCount));
ds.addMember("int revision", offsetof(Design, revision));
ds.addMember("Empire@ owner", offsetof(Design, owner));
ds.addMember("double totalHP", offsetof(Design, totalHP));
ds.addMember("bool outdated", offsetof(Design, outdated));
ds.addMember("bool used", offsetof(Design, used));
ds.addMember("Color color", offsetof(Design, color));
ds.addMember("Color dullColor", offsetof(Design, dullColor));
ds.addMember("Sprite icon", offsetof(Design, icon));
ds.addMember("Sprite distantIcon", offsetof(Design, distantIcon));
ds.addMember("Sprite fleetIcon", offsetof(Design, fleetIcon));
ds.addMember("bool forceHull", offsetof(Design, forceHull));
if(server)
ds.addMember("const Serializable@ settings", offsetof(Design, serverData));
else
ds.addMember("const Serializable@ settings", offsetof(Design, clientData));
ds.addMember("const Design@ newer", offsetof(Design, newer));
ds.addMember("const Design@ original", offsetof(Design, original));
ds.addMember("const Design@ updated", offsetof(Design, updated));
ds.addMember("double topHP", offsetof(Design, quadrantTotalHP[0]));
ds.addMember("double rightHP", offsetof(Design, quadrantTotalHP[1]));
ds.addMember("double bottomHP", offsetof(Design, quadrantTotalHP[2]));
ds.addMember("double leftHP", offsetof(Design, quadrantTotalHP[3]));
ds.addExternMethod("double get_quadrantTotalHP(uint index) const", asFUNCTION(quadrantTotalHP));
ds.addMethod("uint getQuadrant(const vec2u& pos) const", asMETHOD(Design, getQuadrant));
ds.addMethod("const Design& newest() const", asMETHOD(Design, newest));
ds.addMethod("const Design& next() const", asMETHOD(Design, next));
ds.addMethod("const Design& mostUpdated() const", asMETHOD(Design, mostUpdated));
ds.addMethod("const Design& base() const", asMETHOD(Design, base));
ds.addMethod("bool hasTag(const string &in tag) const", asMETHODPR(Design, hasTag, (const std::string&) const, bool));
ds.addMethod("bool hasTag(SubsystemTag tag) const", asMETHODPR(Design, hasTag, (int) const, bool));
ds.addExternMethod("bool hasSubsystem(const SubsystemDef&) const", asFUNCTION(hasSubsys));
ds.addMethod("void toDescriptor(DesignDescriptor& desc) const", asMETHOD(Design, toDescriptor));
ds.addExternMethod("void rename(const string &in name) const", asFUNCTION(dsgRename))
doc("Renames a design, but only if it is not in use.", "");
ds.addExternMethod("int get_built() const", asFUNCTION(dsgGetBuilt));
ds.addExternMethod("void incBuilt() const", asFUNCTION(dsgIncBuilt));
ds.addExternMethod("void decBuilt() const", asFUNCTION(dsgDecBuilt));
ds.addExternMethod("int get_active() const", asFUNCTION(dsgGetActive));
ds.addExternMethod("float variable(ShipVariable var) const", asFUNCTION(dsgGetShipVar));
ds.addExternMethod("float& variable(ShipVariable var)", asFUNCTION(dsgShipVarPtr));
ds.addExternMethod("float variable(const Subsystem& sys, SubsystemVariable var) const", asFUNCTION(dsgGetVar));
ds.addExternMethod("float& variable(Subsystem& sys, SubsystemVariable var)", asFUNCTION(dsgVarPtr));
ds.addExternMethod("bool has(const vec2u& hex, HexVariable var) const", asFUNCTION(dsgHasHexVar));
ds.addExternMethod("float variable(const vec2u& hex, HexVariable var) const", asFUNCTION(dsgGetHexVar));
ds.addExternMethod("float& variable(const vec2u& hex, HexVariable var)", asFUNCTION(dsgHexVarPtr));
ds.addExternMethod("float total(SubsystemVariable var) const", asFUNCTION(dsgSysTotal));
ds.addExternMethod("float total(HexVariable var) const", asFUNCTION(dsgHexTotal));
ds.addExternMethod("float average(SubsystemVariable var) const", asFUNCTION(dsgSysAvg));
ds.addExternMethod("float average(HexVariable var) const", asFUNCTION(dsgHexAvg));
ds.addExternMethod("uint get_subsystemCount() const", asFUNCTION(sysCount));
ds.addExternMethod("const Subsystem@ get_subsystems(uint i) const", asFUNCTION(getSys));
ds.addExternMethod("const Subsystem@ subsystem(uint i) const", asFUNCTION(getSys));
ds.addExternMethod("const Subsystem@ subsystem(uint x, uint y) const", asFUNCTION(getHexSys));
ds.addExternMethod("const Subsystem@ subsystem(const vec2u& hex) const", asFUNCTION(getHexSys_v));
ds.addExternMethod("const ModuleDef@ module(uint x, uint y) const", asFUNCTION(getHexModule));
ds.addExternMethod("const ModuleDef@ module(const vec2u& hex) const", asFUNCTION(getHexModule_v));
ds.addExternMethod("const int hexIndex(const vec2u& hex) const", asFUNCTION(getHexIndex));
ds.addExternMethod("const int hexStatusIndex(const vec2u& hex) const", asFUNCTION(getHexStatusIndex));
ds.addExternMethod("bool validHex(const vec2u& hex) const", asFUNCTION(isValidhex));
ds.addExternMethod("void setObsolete(bool obsolete) const", asFUNCTION(setDesignObsolete));
ds.addExternMethod("void setSettings(const Serializable& ser) const", asFUNCTION(setDesignData));
//Design errors
ClassBind de("DesignError", asOBJ_REF | asOBJ_NOCOUNT);
de.addMember("bool fatal", offsetof(DesignError, fatal));
de.addMember("string text", offsetof(DesignError, text));
de.addMember("const Subsystem@ subsys", offsetof(DesignError, subsys));
de.addMember("const ModuleDef@ module", offsetof(DesignError, module));
de.addMember("vec2i hex", offsetof(DesignError, hex));
ds.addMethod("bool hasFatalErrors() const", asMETHOD(Design, hasFatalErrors));
ds.addExternMethod("uint get_errorCount() const", asFUNCTION(dsgErrorCount));
ds.addExternMethod("DesignError@ get_errors(uint) const", asFUNCTION(dsgError));
ds.addExternMethod("void addError(bool fatal, const string&in text, const Subsystem@ subsys, const ModuleDef@ module, vec2u hex)", asFUNCTION(dsgAddError));
ds.addExternMethod("void addErrorHex(const vec2u& hex)", asFUNCTION(dsgAddErrorHex));
ds.addExternMethod("bool isErrorHex(const vec2u& hex) const", asFUNCTION(dsgIsErrorHex));
//Design classes
ClassBind dc("DesignClass");
dc.addMember("uint id", offsetof(DesignClass, id));
dc.addMember("string name", offsetof(DesignClass, name));
dc.addExternMethod("uint get_designCount() const", asFUNCTION(designCount));
dc.addExternMethod("const Design@ get_designs(uint i) const", asFUNCTION(getDesign));
ds.addMember("const DesignClass@ cls", offsetof(Design, cls));
//Design descriptor for instantiation
desc.addConstructor("void f()", asFUNCTION(descMake));
desc.addDestructor("void f()", asFUNCTION(descDestroy));
desc.addExternMethod("DesignDescriptor& opAssign(const DesignDescriptor&in other)", asFUNCTION(descCopy));
desc.addMember("string name", offsetof(Design::Descriptor, name));
desc.addMember("string className", offsetof(Design::Descriptor, className));
desc.addMember("string hullName", offsetof(Design::Descriptor, hullName));
desc.addMember("bool staticHull", offsetof(Design::Descriptor, staticHull));
desc.addMember("bool forceHull", offsetof(Design::Descriptor, forceHull));
desc.addMember("Serializable@ settings", offsetof(Design::Descriptor, settings));
desc.addMember("const Hull@ hull", offsetof(Design::Descriptor, hull));
desc.addMember("double size", offsetof(Design::Descriptor, size));
desc.addMember("vec2u gridSize", offsetof(Design::Descriptor, gridSize));
desc.addMember("Empire@ owner", offsetof(Design::Descriptor, owner));
desc.addExternMethod("uint addSystem(const SubsystemDef& type)", asFUNCTION(descAddSys));
desc.addExternMethod("void setDirection(const vec3d& dir)", asFUNCTION(descSetDirection));
desc.addExternMethod("void addHex(uint num, vec2u pos)", asFUNCTION(descAddHex));
desc.addExternMethod("void addHex(vec2u pos)", asFUNCTION(descAddHex_l));
desc.addExternMethod("void addHex(uint num, vec2u pos, const ModuleDef& mod)", asFUNCTION(descAddHex_m));
desc.addExternMethod("void addHex(vec2u pos, const ModuleDef& mod)", asFUNCTION(descAddHex_lm));
desc.addExternMethod("void applySubsystem(const SubsystemDef& type)", asFUNCTION(descApply));
bind("const Design@ makeDesign(const DesignDescriptor &in desc)", asFUNCTION(makeDesign));
//Hooks
InterfaceBind sh("SubsystemHook");
ClassBind se("SubsystemEvent", asOBJ_VALUE, sizeof(SubsystemEvent));
se.addConstructor("void f()", asFUNCTION(ssevtMake));
se.addDestructor("void f()", asFUNCTION(ssevtDestroy));
se.addMember("Object@ obj", offsetof(SubsystemEvent, obj));
se.addMember("const Design@ design", offsetof(SubsystemEvent, design));
se.addMember("const Subsystem@ subsystem", offsetof(SubsystemEvent, subsystem));
se.addMember("Blueprint@ blueprint", offsetof(SubsystemEvent, blueprint));
se.addMember("any@ data", offsetof(SubsystemEvent, data));
se.addMember("float efficiency", offsetof(SubsystemEvent, efficiency));
se.addMember("float workingPercent", offsetof(SubsystemEvent, partiality));
se.addMember("float partiality", offsetof(SubsystemEvent, partiality));
if(server) {
ClassBind strlist("StringList", asOBJ_REF | asOBJ_NOCOUNT);
strlist.addExternMethod("uint get_length()", asFUNCTION(strvec_length));
strlist.addExternMethod("const string& opIndex(uint num)", asFUNCTION(strvec_get));
ClassBind dblList("DoubleList", asOBJ_REF | asOBJ_NOCOUNT);
dblList.addExternMethod("uint get_length()", asFUNCTION(dblvec_length));
dblList.addExternMethod("double opIndex(uint num)", asFUNCTION(dblvec_get));
sh.addMethod("bool init(Design& design, Subsystem& subsystem, StringList& arguments, DoubleList& values) const", &Subsystem::ScriptInitFunction);
sh.addMethod("void start(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Start]);
sh.addMethod("void tick(SubsystemEvent& event, double time) const", &Subsystem::ScriptHookFunctions[EH_Tick]);
sh.addMethod("void suspend(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Suspend]);
sh.addMethod("void resume(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Continue]);
sh.addMethod("void destroy(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Destroy]);
sh.addMethod("void end(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_End]);
sh.addMethod("void change(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Change]);
sh.addMethod("void save(SubsystemEvent& event, SaveFile& file) const", &Subsystem::ScriptHookFunctions[EH_Save]);
sh.addMethod("void load(SubsystemEvent& event, SaveFile& file) const", &Subsystem::ScriptHookFunctions[EH_Load]);
sh.addMethod("void ownerChange(SubsystemEvent& event, Empire@ prevEmpire, Empire@ newEmpire) const", &Subsystem::ScriptHookFunctions[EH_Owner_Change]);
sh.addMethod("DamageEventStatus damage(SubsystemEvent& event, DamageEvent& damage, const vec2u& position) const", &Subsystem::ScriptHookFunctions[EH_Damage]);
sh.addMethod("DamageEventStatus globalDamage(SubsystemEvent& event, DamageEvent& damage, const vec2u& position, vec2d& endPoint) const", &Subsystem::ScriptHookFunctions[EH_GlobalDamage]);
sh.addMethod("void preRetrofit(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Retrofit_Pre]);
sh.addMethod("void postRetrofit(SubsystemEvent& event) const", &Subsystem::ScriptHookFunctions[EH_Retrofit_Post]);
sys.addExternMethod("uint get_hookCount() const", asFUNCTION(sysHookCount));
sys.addExternMethod("SubsystemHook@ get_hooks(uint index) const", asFUNCTION(sysHookGet));
}
}
};
diff --git a/source/util/include/frustum.h b/source/util/include/frustum.h
index 2eeed8d..b735235 100644
--- a/source/util/include/frustum.h
+++ b/source/util/include/frustum.h
@@ -1,37 +1,37 @@
#pragma once
#include "plane.h"
#include "line3d.h"
#include "aabbox.h"
struct frustum {
//Planes are: Near, Left, Right, Top, Bottom, Far
planed planes[6];
AABBoxd bound;
frustum() {}
frustum(const line3dd& topLeft, const line3dd& topRight, const line3dd& botLeft, const line3dd& botRight) {
planes[0] = planed(topLeft.start, botLeft.start, topRight.start);
planes[1] = planed(topLeft.start, topLeft.end, botLeft.start);
planes[2] = planed(topRight.end, topRight.start, botRight.start);
planes[3] = planed(topLeft.start, topRight.start, topRight.end);
planes[4] = planed(botRight.start, botLeft.start, botRight.end);
planes[5] = planed(topLeft.end, topRight.end, botLeft.end);
bound.reset(topLeft);
bound.addLine(topRight);
bound.addLine(botLeft);
bound.addLine(botRight);
}
void operator=(const frustum& other) {
- memcpy(this, &other, sizeof(frustum));
+ memcpy(reinterpret_cast<void *>(this), &other, sizeof(frustum));
}
bool overlaps(const vec3d& center, double radius) const {
for(unsigned i = 0; i < 6; ++i)
if(planes[i].distanceFromPlane(center) <= -radius)
return false;
return true;
}
-};
\ No newline at end of file
+};

File Metadata

Mime Type
text/x-diff
Expires
Thu, May 14, 11:44 PM (1 d, 2 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63717
Default Alt Text
(110 KB)

Event Timeline