Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F117896
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
110 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R80 StarRuler2-Source
Attached
Detach File
Event Timeline