Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+164
View File
@@ -0,0 +1,164 @@
#pragma once
#include "design/subsystem.h"
#include "design/hull.h"
#include "util/refcount.h"
#include "image.h"
#include <unordered_set>
class SaveFile;
namespace net {
struct Message;
};
struct DesignClass {
unsigned id;
std::string name;
std::vector<const Design*> designs;
};
struct DesignError {
bool fatal;
std::string text;
const Subsystem* subsys;
const SubsystemDef::ModuleDesc* module;
vec2i hex;
DesignError(bool Fatal, std::string Text,
const Subsystem* Subsys = 0, const SubsystemDef::ModuleDesc* Module = 0,
vec2i Hex = vec2i(-1, -1))
: fatal(Fatal), text(Text), subsys(Subsys), module(Module), hex(Hex) {
}
};
class Empire;
class Design : public AtomicRefCounted {
public:
//Metadata
const HullDef* hull;
std::string name;
double size;
double hexSize;
unsigned interiorHexes;
unsigned exteriorHexes;
unsigned stateCount;
unsigned effectorStateCount;
unsigned effectorCount;
unsigned dataCount;
bool initialized;
double totalHP;
double quadrantTotalHP[4];
mutable bool outdated;
Color color;
Color dullColor;
//Errors
std::vector<DesignError> errors;
bool hasFatalErrors() const;
bool hasTag(const std::string& tag) const;
std::unordered_set<int> numTags;
bool hasTag(int index) const;
//Subsystems
std::vector<Subsystem> subsystems;
std::vector<Subsystem*> damageOrder;
std::vector<SubsystemDef::ShipModifier> modifiers;
HexGrid<int> grid;
HexGrid<int> hexIndex;
HexGrid<int> hexStatusIndex;
std::vector<vec2u> hexes;
std::unordered_set<uint64_t> errorHexes;
unsigned usedHexCount;
float* shipVariables;
vec2u cropMin;
vec2u cropMax;
//Ownership and usage data
mutable unsigned id;
mutable Empire* owner;
mutable bool used;
mutable bool obsolete;
mutable int revision;
mutable threads::atomic_int built;
mutable threads::atomic_int active;
mutable render::Sprite icon;
mutable render::Sprite distantIcon;
mutable render::Sprite fleetIcon;
bool forceHull;
mutable net::Message* data;
mutable asIScriptObject* clientData;
mutable asIScriptObject* serverData;
//Manual updates bump revision numbers and
//use these 'horizontal' lists.
mutable heldPointer<const Design> newer;
const Design* newest() const;
const Design* next() const;
//Automatic updates do not bump revisions and
//use this 'vertical' list of designs.
mutable heldPointer<const Design> original;
mutable heldPointer<const Design> updated;
const Design* mostUpdated() const;
const Design* base() const;
mutable DesignClass* cls;
//Designs are created via descriptors
struct Descriptor {
struct System {
const SubsystemDef* type;
vec3d direction;
std::vector<vec2u> hexes;
std::vector<const SubsystemDef::ModuleDesc*> modules;
System() : type(nullptr), direction(vec3d::front()) {
}
};
Empire* owner;
const HullDef* hull;
std::string name;
std::string className;
std::string hullName;
double size;
vec2u gridSize;
std::vector<Descriptor::System> systems;
std::vector<const SubsystemDef*> appliedSystems;
bool staticHull;
bool forceHull;
asIScriptObject* settings;
Descriptor() : owner(0), hull(0), size(1), staticHull(false), forceHull(false), gridSize(1,1), settings(nullptr) {
}
~Descriptor() {
if(hull)
hull->drop();
if(settings)
settings->Release();
}
};
Design(const Design::Descriptor& desc);
Design(net::Message& msg);
Design(SaveFile& file);
Design();
~Design();
void init(net::Message& msg);
void init(const Design::Descriptor& desc);
void write(net::Message& msg) const;
void save(SaveFile& file) const;
void toDescriptor(Design::Descriptor& desc) const;
void makeDistanceMap(Image& img, vec2i pos, vec2i size) const;
void bindData();
void buildDamageOrder();
unsigned getQuadrant(const vec2u& pos) const;
void writeData(net::Message& msg) const;
void initData(net::Message& msg);
};
+378
View File
@@ -0,0 +1,378 @@
#include "design/effect.h"
#include "compat/misc.h"
#include "main/references.h"
#include "main/logging.h"
#include "network/message.h"
#include "str_util.h"
#include <unordered_map>
static std::vector<EffectDef*> effectDefinitions;
static umap<std::string, int> effectIndices;
static const char* callback_retval[EH_COUNT] = {
"void",
"void",
"void",
"void",
"void",
"void",
"void",
"DamageEventStatus",
"DamageEventStatus",
"void",
"void",
"void",
"void",
"void",
};
static const char* callback_basedecl[EH_COUNT] = {
"Event&",
"Event&",
"Event&",
"Event&",
"Event&",
"Event&",
"Event&",
"DamageEvent&, const vec2u&",
"DamageEvent&, vec2u&, vec2d&",
"Event&",
"Event&",
"Event&, Empire@, Empire@",
"Event&",
"Event&",
};
void clearEffectDefinitions() {
effectIndices.clear();
foreach(it, effectDefinitions)
delete *it;
effectDefinitions.clear();
}
void loadEffectDefinitions(const std::string& filename) {
EffectDef* def = 0;
DataHandler datahandler;
datahandler("Effect", [&](std::string& value) {
auto it = effectIndices.find(value);
if(it != effectIndices.end()) {
def = 0;
error("Error: Duplicate effect %s.", value.c_str());
return;
}
def = new EffectDef();
def->name = value;
def->id = (unsigned)effectDefinitions.size();
def->valueCount = 0;
effectDefinitions.push_back(def);
effectIndices[def->name] = def->id;
});
datahandler("Value", [&](std::string& value) {
if(!def)
return;
if(def->valueCount >= EFFECT_MAX_VALUES) {
error("(Effect %s): Error: Maximum of 6 values allowed per effect.", def->name.c_str());
return;
}
EffectDef::ValueDesc desc;
auto pos = value.find('=');
if(pos != std::string::npos) {
if(pos < value.size() - 1)
desc.defaultValue = Formula::fromInfix(value.substr(pos+1).c_str());
value = trim(value.substr(0, pos));
}
auto it = def->valueNames.find(value);
if(it != def->valueNames.end()) {
error("(Effect %s): Error: Duplicate effect value %s.", def->name.c_str(), value.c_str());
return;
}
def->valueNames[value] = def->valueCount;
def->values.push_back(desc);
++def->valueCount;
});
datahandler("Start", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Start] = value;
});
datahandler("Destroy", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Destroy] = value;
});
datahandler("End", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_End] = value;
});
datahandler("Suspend", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Suspend] = value;
});
datahandler("Continue", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Continue] = value;
});
datahandler("Change", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Change] = value;
});
datahandler("Damage", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Damage] = value;
});
datahandler("Tick", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Tick] = value;
});
datahandler("GlobalDamage", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_GlobalDamage] = value;
});
datahandler("RetrofitPre", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Retrofit_Pre] = value;
});
datahandler("RetrofitPost", [&](std::string& value) {
if(def)
def->hookDefinitions[EH_Retrofit_Post] = value;
});
datahandler.read(filename);
}
EffectDef::EffectDef() : id(-1), valueCount(0), hooks() {
}
void EffectDef::setHook(EffectHook hook, const std::string& ref) {
std::vector<std::string> args;
split(ref, args, "::");
if(args.size() != 2) {
error("(Effect %s): Error: Invalid script function reference.", name.c_str());
hooks[hook] = 0;
return;
}
//Find the module the function is in
scripts::Module* modu = devices.scripts.server->getModule(args[0].c_str());
if(!modu) {
error("(Effect %s): Error: Invalid script module '%s'.", name.c_str(), args[0].c_str());
hooks[hook] = 0;
return;
}
//Build declaration for function
std::string def;
def = callback_retval[hook];
def += " "+args[1]+"(";
def += callback_basedecl[hook];
for(unsigned i = 0; i < valueCount; ++i) {
def += ",double";
}
def += ")";
//Find the function
asIScriptFunction* func = modu->getFunction(def.c_str());
if(!func) {
error("(Effect %s): Error: Could not find script function '%s'.", name.c_str(), def.c_str());
hooks[hook] = 0;
return;
}
hooks[hook] = func;
}
Effect::Effect() : type(0) {
}
Effect::Effect(const EffectDef* Type) : type(Type) {
}
void Effect::call(EffectHook hook, EffectEvent& event) const {
if(!type)
return;
auto* func = type->hooks[hook];
if(!func)
return;
scripts::Call cl = devices.scripts.server->call(func);
if(cl.ctx) {
cl.push((void*)&event);
for(unsigned i = 0; i < type->valueCount; ++i)
cl.push(values[i]);
cl.call();
}
}
DamageEventStatus Effect::damage(DamageEvent& event, const vec2u& position) const {
if(!type || type->hooks[EH_Damage] == nullptr)
return DE_Continue;
scripts::Call cl = devices.scripts.server->call(type->hooks[EH_Damage]);
unsigned status = DE_Continue;
if(cl.ctx) {
cl.push((void*)&event);
cl.push((void*)&position);
for(unsigned i = 0; i < type->valueCount; ++i)
cl.push(values[i]);
cl.call(status);
}
return (DamageEventStatus)status;
}
void Effect::ownerChange(EffectEvent& event, Empire* prevEmpire, Empire* newEmpire) const {
if(!type || type->hooks[EH_Owner_Change] == nullptr)
return;
scripts::Call cl = devices.scripts.server->call(type->hooks[EH_Owner_Change]);
if(cl.ctx) {
cl.push((void*)&event);
cl.push((void*)prevEmpire);
cl.push((void*)newEmpire);
for(unsigned i = 0; i < type->valueCount; ++i)
cl.push(values[i]);
cl.call();
}
}
DamageEventStatus Effect::globalDamage(DamageEvent& event, vec2u& position, vec2d& endPoint) const {
if(!type || type->hooks[EH_GlobalDamage] == nullptr)
return DE_Continue;
scripts::Call cl = devices.scripts.server->call(type->hooks[EH_GlobalDamage]);
unsigned status = DE_Continue;
if(cl.ctx) {
cl.push((void*)&event);
cl.push((void*)&position);
cl.push((void*)&endPoint);
for(unsigned i = 0; i < type->valueCount; ++i)
cl.push(values[i]);
cl.call(status);
}
return (DamageEventStatus)status;
}
void Effect::writeData(net::Message& msg) const {
if(type) {
msg.write1();
msg.writeLimited(type->id, (unsigned)effectDefinitions.size()-1);
}
else {
msg.write0();
}
for(size_t i = 0; i < EFFECT_MAX_VALUES; ++i)
msg << (float)values[i];
}
void Effect::readData(net::Message& msg) {
if(msg.readBit()) {
unsigned typeId = msg.readLimited((unsigned)effectDefinitions.size()-1);
type = getEffectDefinition(typeId);
}
else {
type = nullptr;
}
for(size_t i = 0; i < EFFECT_MAX_VALUES; ++i) {
float v;
msg >> v;
values[i] = v;
}
}
TimedEffect::TimedEffect()
: remaining(0.0) {
}
TimedEffect::TimedEffect(const TimedEffect& other)
: remaining(other.remaining) {
event = other.event;
effect = other.effect;
}
TimedEffect::~TimedEffect() {
}
TimedEffect::TimedEffect(const EffectDef* Type, double Time)
: effect(Type), remaining(Time) {
}
TimedEffect::TimedEffect(const Effect& Effect, double Time)
: remaining(Time) {
effect = Effect;
}
void TimedEffect::call(EffectHook hook) {
effect.call(hook, event);
}
void TimedEffect::tick(double time) {
event.time = time;
effect.call(EH_Tick, event);
if(event.status != ES_Suspended) {
remaining -= time;
if(remaining <= 0.0)
event.status = ES_Ended;
}
}
EffectEvent::EffectEvent()
: time(0.0), efficiency(1.f), partiality(1.f), source(-1), destination(-1), obj(0),
target(0), status(ES_Active), custom1(0.f), custom2(0.f) {
}
EffectEvent::~EffectEvent() {
}
DamageEvent::DamageEvent()
: damage(0.0), pierce(0.f), partiality(1.f), flags(0), source(-1), destination(-1), obj(0),
target(0), custom1(0), custom2(0), spillable(true) {
}
DamageEvent::~DamageEvent() {
}
const EffectDef* getEffectDefinition(const std::string& name) {
auto it = effectIndices.find(name);
if(it == effectIndices.end())
return 0;
return effectDefinitions[it->second];
}
const EffectDef* getEffectDefinition(int index) {
if(index < 0 || index >= (int)effectDefinitions.size())
return 0;
return effectDefinitions[index];
}
unsigned getEffectDefinitionCount() {
return effectDefinitions.size();
}
void enumerateEffectDefinitions(void (*cb)(const std::string&,int)) {
foreach(it, effectDefinitions)
cb((*it)->name, (*it)->id);
}
void bindEffectHooks() {
foreach(it, effectDefinitions) {
foreach(h, (*it)->hookDefinitions) {
(*it)->setHook(h->first, h->second);
}
}
}
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include "util/formula.h"
#include "compat/misc.h"
#include "obj/object.h"
#include "util/refcount.h"
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#ifndef EFFECT_MAX_VALUES
#define EFFECT_MAX_VALUES 6
#endif
class asIScriptFunction;
namespace net {
struct Message;
};
enum EffectHook {
EH_Start,
EH_Tick,
EH_Suspend,
EH_Continue,
EH_Destroy,
EH_End,
EH_Change,
EH_Damage,
EH_GlobalDamage,
EH_Retrofit_Pre,
EH_Retrofit_Post,
EH_Owner_Change,
EH_Save,
EH_Load,
EH_COUNT
};
class EffectDef {
public:
std::string name;
int id;
umap<std::string, unsigned> valueNames;
unsigned valueCount;
struct ValueDesc {
Formula* defaultValue;
ValueDesc() : defaultValue(nullptr) {}
};
std::vector<ValueDesc> values;
std::map<EffectHook, std::string> hookDefinitions;
asIScriptFunction* hooks[EH_COUNT];
void setHook(EffectHook hook, const std::string& ref);
EffectDef();
};
enum EffectStatus {
ES_Active,
ES_Suspended,
ES_Ended,
};
class Subsystem;
class EffectEvent {
public:
vec3d impact;
vec2d direction;
heldPointer<Object> obj;
heldPointer<Object> target;
double time;
float efficiency;
float partiality;
int source;
int destination;
float custom1;
float custom2;
EffectStatus status;
EffectEvent();
~EffectEvent();
};
enum DamageEventStatus {
DE_Continue,
DE_SkipHex,
DE_EndDamage,
};
class DamageEvent {
public:
vec3d impact;
double damage;
float pierce;
float partiality;
float custom1;
float custom2;
unsigned flags;
bool spillable;
int source;
int destination;
heldPointer<Object> obj;
heldPointer<Object> target;
DamageEvent();
~DamageEvent();
};
class Effect {
public:
const EffectDef* type;
double values[EFFECT_MAX_VALUES];
Effect();
Effect(const EffectDef* Type);
void call(EffectHook hook, EffectEvent& event) const;
DamageEventStatus damage(DamageEvent& event, const vec2u& position) const;
DamageEventStatus globalDamage(DamageEvent& event, vec2u& position, vec2d& endPoint) const;
void ownerChange(EffectEvent& event, Empire* prevEmpire, Empire* newEmpire) const;
void writeData(net::Message& msg) const;
void readData(net::Message& msg);
};
class TimedEffect {
public:
EffectEvent event;
Effect effect;
double remaining;
TimedEffect();
TimedEffect(const TimedEffect& other);
TimedEffect(const EffectDef* Type, double Time);
TimedEffect(const Effect& Effect, double Time);
~TimedEffect();
void call(EffectHook hook);
void tick(double time);
};
void loadEffectDefinitions(const std::string& filename);
const EffectDef* getEffectDefinition(const std::string& name);
const EffectDef* getEffectDefinition(int index);
unsigned getEffectDefinitionCount();
void enumerateEffectDefinitions(void (*cb)(const std::string&,int));
void bindEffectHooks();
void clearEffectDefinitions();
File diff suppressed because it is too large Load Diff
+261
View File
@@ -0,0 +1,261 @@
#pragma once
#include "util/formula.h"
#include "scripts/manager.h"
#include "design/effect.h"
#include "render/render_state.h"
#include "threads.h"
#include <vector>
#include <unordered_map>
class Object;
class Effector;
class Design;
struct EffectorTarget;
class HullDef;
namespace net {
struct Message;
};
namespace resource {
class Sound;
};
namespace scene {
class Node;
struct ParticleSystemDesc;
};
typedef Object*(*nativeTargetAlgorithm)(const Effector*, Object*, EffectorTarget* targ);
struct TargetAlgorithm {
union {
nativeTargetAlgorithm native;
asIScriptFunction* script;
};
bool isNative;
};
typedef double(*nativeTargetWeighter)(const Effector*, Object*, Object*, void*);
struct TargetWeighter {
union {
nativeTargetWeighter native;
asIScriptFunction* script;
};
bool isNative;
void* arg;
TargetWeighter() {
script = 0;
isNative = false;
arg = 0;
}
};
struct EffectorTarget;
enum EffectorActivationType {
EAT_Inactive,
EAT_Activate,
EAT_Repeat
};
typedef EffectorActivationType(*nativeEffectorActivation)(const Effector*, Object*, EffectorTarget& targ, double&, double*, double*);
struct EffectorActivation {
union {
nativeEffectorActivation native;
asIScriptFunction* script;
};
bool isNative;
};
enum EffectorPhysicalType {
EPT_Instant,
EPT_Projectile,
EPT_Missile,
EPT_AimedMissile,
EPT_Beam,
};
enum EffectorGraphicType {
EGT_Sprite,
EGT_Beam,
EGT_Line
};
enum EffectorEfficiencyMode {
EEM_Normal,
EEM_Reload_Partial,
EEM_Duration_Partial,
EEM_Reload,
EEM_Duration,
};
struct EffectorSkin {
EffectorGraphicType graphicType;
double graphicSize;
double length;
std::string trailMatID;
const render::RenderState* trailMat;
Color trailStart, trailEnd, color;
std::string def_material;
const render::RenderState* material;
std::string def_impact;
const scene::ParticleSystemDesc* impact;
float fire_pitch_variance;
std::vector<std::string> fire_sound_names;
std::vector<const resource::Sound*> fire_sounds;
std::string def_impact_sound;
const resource::Sound* impact_sound;
EffectorSkin();
};
class EffectorDef {
public:
unsigned index;
std::string name;
std::unordered_map<std::string, unsigned> valueNames;
Formula* range, *lifetime, *tracking, *speed, *spread, *capTarget;
Formula* fireArc, *targetTolerance, *fireTolerance;
std::string def_algorithm;
std::string def_activation;
std::string def_canTarget;
std::string def_autoTarget;
std::string def_onTrigger;
TargetAlgorithm algorithm;
EffectorActivation activation;
asIScriptFunction* onTrigger;
std::vector<TargetWeighter> canTargetWeighters;
Formula* canTarget;
std::vector<TargetWeighter> autoTargetWeighters;
Formula* autoTarget;
//Whether the projectile should hit a target according to physical behaviors, or only the chosen target
bool physicalImpact;
bool passthroughInvalid;
bool pierces;
float recoverTime;
EffectorEfficiencyMode efficiencyMode;
EffectorPhysicalType physicalType;
double physicalSize;
std::unordered_map<std::string, unsigned> skinNames;
unsigned valueCount;
struct ValueDesc {
Formula* defaultValue;
ValueDesc() : defaultValue(nullptr) {}
};
std::vector<ValueDesc> values;
unsigned stateCount;
std::vector<Formula*> arguments;
std::vector<Formula*> triggerArguments;
const EffectDef* effect;
std::vector<Formula*> effectValues;
std::vector<EffectorSkin> skins;
void triggerGraphics(Object* obj, EffectorTarget& targ, const Effector* effector, double* time = 0, vec2d* direction = 0, float efficiency = 1.f, double tOffset = 0) const;
EffectorDef();
};
enum TargetFlags {
TF_Target = 0,
TF_Preference = 0x1,
TF_Group = 0x2,
TF_Firing = 0x4,
TF_Retarget = 0x8,
TF_TrackingProgress = 0x10,
TF_ClearTracking = 0x20,
TF_WithinFireTolerance = 0x40,
};
struct EffectorTarget {
Object* target;
unsigned flags;
unsigned char hits;
vec3d tracking;
};
class Effector {
mutable threads::atomic_int refs;
public:
const Design* inDesign;
unsigned subsysIndex;
unsigned effectorIndex;
mutable unsigned effectorId;
unsigned skinIndex;
vec3d relativePosition;
vec3d turretAngle;
double relativeSize;
bool enabled;
const EffectorDef& type;
double* values;
double range, lifetime, tracking, speed, spread;
double fireArc, targetTolerance, fireTolerance;
unsigned capTarget;
unsigned stateOffset;
Effect effect;
Effector(const EffectorDef& def);
~Effector();
void initValues();
void load(SaveFile& file);
void save(SaveFile& file) const;
static const Effector* receiveUpdate(net::Message& msg);
void sendUpdate(net::Message& msg) const;
void sendDestruction(net::Message& msg) const;
void grab() const;
void drop() const;
bool isInRange(Object* obj, Object* target, bool considerArc = true) const;
bool canTarget(Object* obj, Object* target) const;
bool autoTarget(Object* obj, Object* target) const;
double getTargetWeight(Object* obj, Object* target) const;
void trigger(Object* obj, EffectorTarget& targ, float efficiency, double tOffset = 0) const;
void triggerEffect(Object* obj, Object* target, const vec3d& impactOffset, float efficiency, float partiality, double delay = 0.0) const;
void update(Object* obj, double time, double* states, EffectorTarget& target, float efficiency, bool holdFire = false) const;
void setRelativePosition(vec2u hex, const HullDef* hull, vec3d direction);
void writeData(net::Message& msg) const;
Effector(net::Message& msg);
};
void clearEffectorDefinitions();
void loadEffectorDefinitions(const std::string& filename);
unsigned getEffectorDefinitionCount();
const EffectorDef* getEffectorDefinition(const std::string& name);
const EffectorDef* getEffectorDefinition(unsigned index);
void bindEffectorHooks(bool shadow = false);
void bindEffectorResources();
extern std::unordered_map<unsigned, const Effector*> effectorMap;
void registerEffector(const Effector* eff);
void unregisterEffector(const Effector* eff);
const Effector* getEffector(unsigned id);
void clearEffectors();
void saveEffectors(SaveFile& file);
void loadEffectors(SaveFile& file);
void postLoadEffectors();
+305
View File
@@ -0,0 +1,305 @@
#include "effector_functions.h"
#include "obj/object.h"
#include "util/random.h"
#include "threads.h"
#include "empire.h"
#include "obj/blueprint.h"
#include "assert.h"
#include "main/logging.h"
#ifndef TARGET_SINGLE_TARGET_DEPTH
#define TARGET_SINGLE_TARGET_DEPTH 3
#endif
//* Algorithms
struct SingleTargetFinder {
const Effector* eff;
EffectorTarget* efftarg;
Object* obj;
Object* best;
double maxWeight;
bool result(Object* targ) {
//Ignore ourselves
if(targ == obj)
return false;
//Ignore objects out of range
if(!eff->isInRange(obj, targ))
return false;
double weight = eff->getTargetWeight(obj, targ);
//Weights under zero mean to ignore this object
if(weight <= 0.0)
return false;
//Modify weight with targeting preference
if(efftarg->target) {
if(efftarg->flags & TF_Group) {
if(efftarg->target->group != targ->group)
weight /= 10.0;
} else if(efftarg->flags & TF_Preference) {
if(efftarg->target != targ)
weight /= 10.0;
}
}
//Weights of at least one mean to immediately target this
if(weight >= 1.0) {
best = targ;
return true;
}
//Store the object with the highest weight, so
//we have something to target if nothing is
//randomed.
if(weight > maxWeight) {
best = targ;
maxWeight = weight;
}
return false;
}
};
static Object* SingleTarget(const Effector* eff, Object* obj, EffectorTarget* efftarg) {
SingleTargetFinder finder;
finder.efftarg = efftarg;
finder.eff = eff;
finder.obj = obj;
finder.best = 0;
finder.maxWeight = 0;
obj->findTargets(finder, TARGET_SINGLE_TARGET_DEPTH, Object::RANDOMIZE_TARGETS);
return finder.best;
}
//* Weighters
static double NotOurs(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj->owner == targ->owner)
return 0.0;
return 1.0;
}
static double Ours(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj->owner != targ->owner)
return 0.0;
return 1.0;
}
static double isEnemy(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(targ->owner && obj->owner != targ->owner && targ->owner->valid()) {
if(obj->owner && (obj->owner->hostileMask & targ->owner->mask) != 0)
return 1.0;
else
return 0.0;
}
else
return 0.0;
}
static double isDamageable(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(targ->getFlag(objNoDamage))
return 0.0;
else
return 1.0;
}
static double isAttackable(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj->owner == targ->owner || targ->owner == nullptr || !targ->owner->valid())
return 1.0;
return isEnemy(eff, obj, targ, arg);
}
static double hasDamagedBlueprint(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj != nullptr && obj->type->blueprintOffset != 0) {
auto* blueprint = (Blueprint*)(((size_t)obj) + obj->type->blueprintOffset);
return blueprint->currentHP < blueprint->design->totalHP - 0.0001 ? 1.0 : 0.0;
}
return 0.0;
}
double isType(const Effector* eff, Object* obj, Object* targ, void* arg) {
if((void*)targ->type != arg)
return 0.0;
return 1.0;
}
static double Distance(const Effector* eff, Object* obj, Object* targ, void* arg) {
return obj->position.distanceTo(targ->position);
}
double hasTag(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(targ != nullptr && targ->isValid() && targ->type->blueprintOffset != 0) {
auto* blueprint = (Blueprint*)(((size_t)targ) + targ->type->blueprintOffset);
if(!blueprint || !blueprint->design)
return 0.0;
return blueprint->design->hasTag((int)(size_t)arg) ? 1.0 : 0.0;
}
return 0.0;
}
static double targRadius(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(targ == nullptr)
return 1.0;
return targ->radius;
}
static double originRadius(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj == nullptr)
return 1.0;
return obj->radius;
}
static double sizeDifference(const Effector* eff, Object* obj, Object* targ, void* arg) {
if(obj == nullptr || targ == nullptr)
return 1.0;
if(obj->radius > targ->radius)
return obj->radius / targ->radius;
else
return targ->radius / obj->radius;
}
//* Activation
static EffectorActivationType Always(const Effector* eff, Object* obj, EffectorTarget& targ, double& time, double* args, double* states) {
return EAT_Activate;
}
static EffectorActivationType Timed(const Effector* eff, Object* obj, EffectorTarget& targ, double& time, double* args, double* states) {
if(!(targ.flags & TF_WithinFireTolerance)) {
if(states[0] > 0.0)
states[0] = std::max(states[0] - time, 0.0);
return EAT_Inactive;
}
else if(states[0] <= time) {
time = states[0];
states[0] = args[0];
if(args[0] <= 0.000001)
return EAT_Inactive;
return EAT_Repeat;
}
states[0] -= time;
return EAT_Inactive;
}
static EffectorActivationType VariableTimed(const Effector* eff, Object* obj, EffectorTarget& targ, double& time, double* args, double* states) {
if(!(targ.flags & TF_WithinFireTolerance)) {
if(states[0] > 0.0)
states[0] = std::max(states[0] - time, 0.0);
return EAT_Inactive;
}
else if(states[0] <= time) {
time = states[0];
if(targ.target)
states[0] = args[0] * randomd(1.0 - args[1], 1.0 + args[1]);
else
states[0] = args[0];
if(args[0] <= 0.000001)
return EAT_Inactive;
return EAT_Repeat;
}
states[0] -= time;
return EAT_Inactive;
}
static EffectorActivationType StaggeredTimed(const Effector* eff, Object* obj, EffectorTarget& targ, double& time, double* args, double* states) {
if(!(targ.flags & TF_WithinFireTolerance)) {
double stagger = args[1] * args[0] * (-eff->relativePosition.x + 1.0) * 0.5;
states[0] = std::max(states[0] - time, stagger);
return EAT_Inactive;
}
else if(states[0] <= time) {
time = states[0];
states[0] = args[0];
if(args[0] <= 0.000001)
return EAT_Inactive;
return EAT_Repeat;
}
states[0] -= time;
return EAT_Inactive;
}
//Fires arg[1] shots at arg[0] second intervals, then reloads over arg[2] seconds
static EffectorActivationType Magazine(const Effector* eff, Object* obj, EffectorTarget& targ, double& time, double* args, double* states) {
if(!(targ.flags & TF_WithinFireTolerance) || states[1] <= 0.0) {
if(states[0] > 0.0) {
states[0] = states[0] - time;
if(states[0] <= 0.0) {
states[1] = args[1];
states[0] = 0.0;
}
}
else {
states[1] = args[1];
}
return EAT_Inactive;
}
else if(states[0] <= time) {
time = states[0];
states[1] -= 1.0;
if(states[1] <= 0.000001)
states[0] = args[2];
else
states[0] = args[0];
if(args[0] <= 0.000001)
return EAT_Inactive;
return EAT_Repeat;
}
states[0] -= time;
return EAT_Inactive;
}
//Build maps
decltype(TargetAlgorithms) makeAlgoList() {
decltype(TargetAlgorithms) list;
list["SingleTarget"] = SingleTarget;
return list;
}
decltype(TargetWeighters) makeWeighterList() {
decltype(TargetWeighters) list;
list["NotOurs"] = NotOurs;
list["Ours"] = Ours;
list["isEnemy"] = isEnemy;
list["isAttackable"] = isAttackable;
list["hasDamagedBlueprint"] = hasDamagedBlueprint;
list["isDamageable"] = isDamageable;
list["targRadius"] = targRadius;
list["originRadius"] = originRadius;
list["sizeDifference"] = sizeDifference;
list["Distance"] = Distance;
return list;
}
decltype(EffectorActivation) makeActivationList() {
decltype(EffectorActivation) list;
auto addFunc = [&](std::string name, nativeEffectorActivation f, unsigned stateCount, unsigned argCount)
{
auto& cb = list[name];
cb.func = f;
cb.stateCount = stateCount;
cb.argCount = argCount;
};
addFunc("Always", Always, 0, 0);
addFunc("Timed", Timed, 1, 1);
addFunc("VariableTimed", VariableTimed, 1, 2);
addFunc("StaggeredTimed", StaggeredTimed, 1, 2);
addFunc("Magazine", Magazine, 2, 3);
return list;
}
umap<std::string, nativeTargetAlgorithm> TargetAlgorithms = makeAlgoList();
umap<std::string, nativeTargetWeighter> TargetWeighters = makeWeighterList();
umap<std::string, ActivationCB> EffectorActivation = makeActivationList();
+15
View File
@@ -0,0 +1,15 @@
#include "design/effector.h"
#include "compat/misc.h"
extern umap<std::string, nativeTargetAlgorithm> TargetAlgorithms;
extern umap<std::string, nativeTargetWeighter> TargetWeighters;
struct ActivationCB {
nativeEffectorActivation func;
unsigned stateCount, argCount;
};
extern umap<std::string, ActivationCB> EffectorActivation;
double isType(const Effector* eff, Object* obj, Object* targ, void* arg);
double hasTag(const Effector* eff, Object* obj, Object* targ, void* arg);
+912
View File
@@ -0,0 +1,912 @@
#include "design/hull.h"
#include "str_util.h"
#include "main/logging.h"
#include "main/references.h"
#include "compat/misc.h"
#include "design/design.h"
#include "design/subsystem.h"
#include "design/subsystem.h"
#include "threads.h"
#include "vec3.h"
#include "line3d.h"
#include <algorithm>
#include "files.h"
#include <math.h>
#include <cmath>
#include <assert.h>
#include <stdint.h>
static std::vector<Shipset*> shipsets;
static umap<std::string, unsigned> shipsetIndices;
static std::vector<HullDef*> hullDefinitions;
static umap<std::string, unsigned> hullIndices;
static unsigned computedHulls = 0;
void computeHulls(unsigned amount) {
unsigned offset = randomi(0, hullDefinitions.size()-1);
for(unsigned n = 0, cnt = hullDefinitions.size(); n < cnt; ++n) {
if(hullDefinitions[(offset+n)%cnt]->calculateImpacts()) {
++computedHulls;
if(computedHulls >= hullDefinitions.size())
return;
--amount;
if(amount == 0)
return;
}
}
}
bool isFinishedComputingHulls() {
return computedHulls >= hullDefinitions.size();
}
unsigned getHullCount() {
return (unsigned)hullDefinitions.size();
}
const HullDef* getHullDefinition(unsigned id) {
if(id >= hullDefinitions.size())
return 0;
return hullDefinitions[id];
}
const HullDef* getHullDefinition(const std::string& name) {
auto it = hullIndices.find(name);
if(it == hullIndices.end())
return 0;
return hullDefinitions[it->second];
}
HullDef::HullDef()
: id(-1), background(0), material(0), mesh(0),
iconSheet(0), iconIndex(0), gridSize(16, 8), backgroundScale(1.0), modelScale(1.0),
active(gridSize), exterior(gridSize), activeCount(0), exteriorCount(0),
minSize(1.0), maxSize(-1.0), baseHull(this), shape(nullptr), special(false)
{
active.clear(false);
exterior.clear(false);
}
HullDef::HullDef(const HullDef& other) {
*this = other;
}
void clearHullDefinitions() {
foreach(it, hullDefinitions) {
auto* hull = *it;
hull->baseHull = nullptr;
hull->drop();
}
hullDefinitions.clear();
hullIndices.clear();
computedHulls = 0;
}
void loadHullDefinitions(const std::string& filename) {
std::vector<HullDef*> defs;
readHullDefinitions(filename, defs);
foreach(it, defs) {
(*it)->calculateDist();
(*it)->calculateExterior();
(*it)->id = (unsigned)hullDefinitions.size();
hullDefinitions.push_back(*it);
hullIndices[(*it)->ident] = (*it)->id;
}
}
bool meshIntersect(const Mesh& mesh, const line3dd& line, vec3d& output) {
double closestDist = 1e100;
vec3d closestIntersect;
for(unsigned i = 0, cnt = mesh.faces.size(); i < cnt; ++i) {
const Mesh::Face& face = mesh.faces[i];
vec3d v1 = vec3d(mesh.vertices[face.a].position);
vec3d v2 = vec3d(mesh.vertices[face.b].position);
vec3d v3 = vec3d(mesh.vertices[face.c].position);
vec3d point;
if(line.intersectTriangle(v1, v2, v3, point)) {
double dist = line.start.distanceToSQ(point);
if(dist < closestDist) {
closestIntersect = point;
closestDist = dist;
}
}
}
if(!closestIntersect.zero()) {
output = closestIntersect;
return true;
}
return false;
}
bool HullDef::calculateImpacts() {
if(!impacts.empty())
return false;
if(!this->mesh)
return true;
const Mesh& mesh = this->mesh->getMesh();
if(mesh.faces.empty())
return false;
//double t = devices.driver->getAccurateTime();
std::vector<vec3d> lineImpacts;
lineImpacts.resize(8 * 64);
impacts.resize(8 * 64);
//Trace the impact lines
for(unsigned n = 0; n < 8; ++n) {
double theta = (double)n / 8.0 * pi;
for(unsigned i = 0; i < 64; ++i) {
double phi = (double)i / 64.0 * twopi;
double st = sin(theta);
vec3d start = vec3d(
st * cos(phi),
cos(theta),
st * sin(phi)
);
line3dd ray(start, vec3d(0.0));
vec3d point;
if(meshIntersect(mesh, ray, point))
lineImpacts[n*64 + i] = point;
else
lineImpacts[n*64 + i] = vec3d();
}
}
//Pre-calculate the closest impact points for angles
for(unsigned n = 0; n < 8; ++n) {
double theta = (double)n / 8.0 * pi;
for(unsigned i = 0; i < 64; ++i) {
double phi = (double)i / 64.0 * twopi;
double st = sin(theta);
vec3d point = vec3d(
st * cos(phi),
cos(theta),
st * sin(phi)
);
double d = 1e80;
vec3d imp;
for(unsigned j = 0, jcnt = lineImpacts.size(); j < jcnt; ++j) {
double dist = lineImpacts[j].distanceToSQ(point);
if(dist < d) {
d = dist;
imp = lineImpacts[j];
}
}
impacts[n*64 + i] = imp;
}
}
//double tend = devices.driver->getAccurateTime();
//print("Impacts %s - %gms", ident.c_str(), (tend-t)*1000.0);
return true;
}
vec3d HullDef::getImpact(const vec3d& offset, double radius, bool constant) const {
if(baseHull != nullptr && baseHull != this)
return baseHull->getImpact(offset, radius);
if(impacts.size() == 0)
return offset;
vec3d uoffset = offset / radius;
double theta = std::fmod(twopi + acos(uoffset.y / uoffset.getLength()), twopi);
double phi = std::fmod(twopi + atan2(uoffset.z, uoffset.x), twopi);
unsigned row = (unsigned)floor(theta / pi * 8.0 + 0.5) % 8;
unsigned index;
if(constant)
index = ((unsigned)floor(phi / twopi * 64.0 + 0.5)) % 64;
else
index = ((unsigned)floor(phi / twopi * 64.0 + 0.5) + randomi(-1,1)) % 64;
vec3d imp = impacts[row*64 + index];
if(imp.zero())
return offset;
return imp * radius;
}
vec3d HullDef::getClosestImpact(const vec3d& offset) const {
if(baseHull != nullptr && baseHull != this)
return baseHull->getClosestImpact(offset);
if(impacts.size() == 0)
return offset;
double d = 1e80;
vec3d imp;
for(unsigned j = 0, jcnt = impacts.size(); j < jcnt; ++j) {
double dist = impacts[j].distanceToSQ(offset);
if(dist < d) {
d = dist;
imp = impacts[j];
}
}
return imp;
}
void HullDef::calculateExterior() {
//Top & Bottom Lines
for(unsigned i = 0, cnt = gridSize.x; i < cnt; ++i) {
if(i % 2 == 0) {
calculateExterior(vec2u(i, 0), HEX_DownLeft);
calculateExterior(vec2u(i, 0), HEX_Down);
calculateExterior(vec2u(i, 0), HEX_DownRight);
calculateExterior(vec2u(i, gridSize.y-1), HEX_Up);
}
else {
calculateExterior(vec2u(i, 0), HEX_Down);
calculateExterior(vec2u(i, gridSize.y-1), HEX_UpLeft);
calculateExterior(vec2u(i, gridSize.y-1), HEX_Up);
calculateExterior(vec2u(i, gridSize.y-1), HEX_UpRight);
}
}
//Right & Left Lines
for(unsigned i = 0, cnt = gridSize.y; i < cnt; ++i) {
calculateExterior(vec2u(0, i), HEX_DownRight);
calculateExterior(vec2u(0, i), HEX_UpRight);
calculateExterior(vec2u(gridSize.x-1, i), HEX_DownLeft);
calculateExterior(vec2u(gridSize.x-1, i), HEX_UpLeft);
}
}
void HullDef::calculateExterior(vec2u pos, unsigned direction) {
unsigned mask = (1<<((direction+3)%6));
do {
if((mask & flagExteriorFaux) && !(exterior[pos] & (flagExteriorFaux | flagExteriorPass)) && active[pos])
break;
exterior[pos] |= mask;
if(active[pos]) {
if(exterior[pos] & flagExteriorPass)
continue;
if(exterior[pos] & flagExteriorFaux) {
mask |= flagExteriorFaux;
continue;
}
break;
}
}
while(exterior.advance(pos, (HexGridAdjacency)direction));
}
void HullDef::calculateDist() {
if(shape == nullptr || shape->format != FMT_RGBA)
return;
if(shapeMapped)
return;
//First, make it binary. This is a distance map, so every active
//pixel should be 0 and every inactive should be full distance.
for(unsigned x = 0; x < shape->width; ++x) {
for(unsigned y = 0; y < shape->height; ++y) {
Color& col = shape->get_rgba(x, y);
if(col.a != 0)
col.a = 0;
else
col.a = 0xff;
}
}
//Calculate distances
for(unsigned x = 0; x < shape->width; ++x) {
for(unsigned y = 0; y < shape->height; ++y) {
if(shape->get_rgba(x, y).a == 0)
calculateDist(vec2u(x,y), 0);
}
}
saveImage(shape, shapeMap.c_str());
}
void HullDef::calculateDist(vec2u pos, int dist) {
assert(dist <= shape->get_rgba(pos.x, pos.y).a);
shape->get_rgba(pos.x, pos.y).a = std::min(dist, 255);
int wmod = std::max(512 / (int)shape->width, 1);
int hmod = std::max(512 / (int)shape->height, 1);
if(pos.x > 0) {
if(shape->get_rgba(pos.x-1, pos.y).a > dist+wmod)
calculateDist(vec2u(pos.x-1, pos.y), dist+wmod);
}
if(pos.x < shape->width-1) {
if(shape->get_rgba(pos.x+1, pos.y).a > dist+wmod)
calculateDist(vec2u(pos.x+1, pos.y), dist+wmod);
}
if(pos.y > 0) {
if(shape->get_rgba(pos.x, pos.y-1).a > dist+hmod)
calculateDist(vec2u(pos.x, pos.y-1), dist+hmod);
}
if(pos.y < shape->height-1) {
if(shape->get_rgba(pos.x, pos.y+1).a > dist+hmod)
calculateDist(vec2u(pos.x, pos.y+1), dist+hmod);
}
}
double HullDef::getMatchDistance(const vec2d& pos) const {
if(shape == nullptr)
return 128.0;
return shape->getTexel(pos.x, pos.y).a;
}
double HullDef::getMatchDistance(void* descPtr) const {
auto& desc = *(Design::Descriptor*)descPtr;
double dist = 0.0;
vec2u grid = desc.gridSize;
unsigned count = grid.x * grid.y;
uint8_t* cache;
if(count < 2500)
cache = (uint8_t*)alloca(count * sizeof(uint8_t));
else
cache = (uint8_t*)malloc(count * sizeof(uint8_t));
memset(cache, 0, count * sizeof(uint8_t));
for(size_t i = 0, cnt = desc.systems.size(); i < cnt; ++i) {
auto& sys = desc.systems[i];
for(size_t j = 0, jcnt = sys.hexes.size(); j < jcnt; ++j) {
vec2u hex = sys.hexes[j];
if(hex.x < grid.x && hex.y < grid.y) {
unsigned index = hex.y * grid.x + hex.x;
cache[index] = 1;
}
}
}
for(unsigned x = 0; x < grid.x; ++x) {
for(unsigned y = 0; y < grid.y; ++y) {
vec2d pctPos = HexGrid<>::getEffectivePosition(vec2u(x, y));
pctPos.x += 0.75 * 0.5;
pctPos.y += 0.5;
pctPos.x /= ((double)grid.x) * 0.75;
pctPos.y /= (double)grid.y;
double d = getMatchDistance(pctPos);
unsigned index = y * grid.x + x;
if(cache[index])
dist += d*d;
else
dist += 0.5 * (255.0 - d);
}
}
if(count >= 2500)
free(cache);
return dist;
}
bool HullDef::checkConnected() {
HexGrid<bool> connected(active.width, active.height);
connected.clear(false);
bool found = false;
for(unsigned x = 0; x < active.width && !found; ++x) {
for(unsigned y = 0; y < active.height && !found; ++y) {
if(active.get(x,y)) {
fillConnected(connected, vec2u(x,y));
found = true;
}
}
}
for(unsigned x = 0; x < active.width; ++x) {
for(unsigned y = 0; y < active.height; ++y) {
if(active.get(x,y) && !connected.get(x,y))
return false;
}
}
return true;
}
void HullDef::fillConnected(HexGrid<bool>& connected, vec2u pos) {
connected[pos] = true;
for(unsigned i = 0; i < 6; ++i) {
vec2u hex = pos;
if(active.advance(hex, (HexGridAdjacency)i)) {
if(active[hex] && !connected[hex])
fillConnected(connected, hex);
}
}
}
bool HullDef::isExterior(const vec2u& hex) const {
if(!exterior.valid(hex))
return false;
return exterior.get(hex) & 0x00ffffff;
}
bool HullDef::isExteriorInDirection(const vec2u& hex, unsigned dir) const {
if(!exterior.valid(hex))
return false;
return exterior.get(hex) & (1<<dir);
}
void readHullDefinitions(const std::string& filename, std::vector<HullDef*>& hulls) {
HullDef* def = 0;
int x = 0, y = 0;
DataHandler datahandler;
datahandler.lineHandler([&](std::string& line) {
if(!def)
return;
if(y >= def->gridSize.height) {
error("Error: Too many rows for hull '%s'.", def->ident.c_str());
return;
}
x = 0;
line = trim(line, "\r\n\t");
unsigned cnt = (unsigned)line.size();
for(unsigned i = 0; i < cnt; ++i) {
if(x >= def->gridSize.width) {
error("Error: Too many characters on row for hull '%s'.", def->ident.c_str());
break;
}
bool& active = def->active.get(x, y);
int& exterior = def->exterior.get(x, y);
switch(line[i]) {
case '-':
active = false;
exterior = 0;
++x;
break;
case 'X':
case 'x':
active = true;
exterior = 0;
++def->activeCount;
++x;
break;
case '#':
active = true;
exterior = 0xff;
++def->activeCount;
++def->exteriorCount;
++x;
break;
case ' ':
break;
default:
error("Error: Invalid character '%c' on row for hull '%s'.", line[i], def->ident.c_str());
break;
}
}
++y;
});
datahandler("Hull", [&](std::string& value) {
def = new HullDef();
def->ident = value;
def->name = "__"+def->ident+"__";
hulls.push_back(def);
x = 0;
y = 0;
});
datahandler("Name", [&](std::string& value) {
if(!def)
return;
def->name = devices.locale.localize(value);
});
datahandler("Tags", [&](std::string& value) {
if(!def)
return;
split(value, def->tags, ',', true);
for(size_t i = 0, cnt = def->tags.size(); i < cnt; ++i)
def->numTags.insert(getSysTagIndex(def->tags[i], true));
});
datahandler("Subsystem", [&](std::string& value) {
if(!def)
return;
def->subsystems.push_back(value);
});
datahandler("Background", [&](std::string& value) {
if(!def)
return;
const render::RenderState* bg = &devices.library.getMaterial(value);
if(!bg) {
error("(Hull %s): Error: Unknown material %s.", def->ident.c_str(), value.c_str());
return;
}
def->background = bg;
def->backgroundName = value;
});
datahandler("BackgroundScale", [&](std::string& value) {
if(!def)
return;
def->backgroundScale = toNumber<double>(value);
});
datahandler("ModelScale", [&](std::string& value) {
if(!def)
return;
def->modelScale = toNumber<double>(value);
});
datahandler("Material", [&](std::string& value) {
if(!def)
return;
const render::RenderState* mat = &devices.library.getMaterial(value);
if(!mat) {
error("(Hull %s): Error: Unknown material %s.", def->ident.c_str(), value.c_str());
return;
}
def->materialName = value;
def->material = mat;
});
datahandler("Model", [&](std::string& value) {
if(!def)
return;
const render::RenderMesh* mesh = &devices.library.getMesh(value);
if(!mesh) {
if(devices.render)
error("(Hull %s): Error: Unknown model %s.", def->ident.c_str(), value.c_str());
return;
}
def->meshName = value;
def->mesh = mesh;
});
datahandler("Shape", [&](std::string& value) {
if(!def)
return;
std::string fname = devices.mods.resolve(value);
def->shapeMap = fname+".map.png";
if(fileExists(def->shapeMap)) {
def->shape = loadImage(def->shapeMap.c_str());
def->shapeMapped = true;
}
else {
def->shape = loadImage(fname.c_str());
def->shapeMapped = false;
}
});
datahandler("GridSize", [&](std::string& value) {
if(!def)
return;
std::vector<std::string> args;
split(value, args, ',');
if(args.size() != 2) {
error("(Hull %s): Error: Invalid grid size specification.", def->ident.c_str());
return;
}
def->gridSize = vec2i(toNumber<int>(args[0]), toNumber<int>(args[1]));
def->active.resize(def->gridSize);
def->active.clear(false);
def->exterior.resize(def->gridSize);
def->exterior.clear(0);
});
datahandler("GridOffset", [&](std::string& value) {
if(!def)
return;
std::vector<std::string> args;
split(value, args, ',');
if(args.size() != 4) {
error("(Hull %s): Error: Invalid grid offset specification.", def->ident.c_str());
return;
}
def->gridOffset = recti(toNumber<int>(args[0]), toNumber<int>(args[1]),
toNumber<int>(args[2]), toNumber<int>(args[3]));
});
datahandler("GuiIcon", [&](std::string& value) {
if(def) {
def->guiIcon = devices.library.getSprite(value);
def->guiIconName = value;
}
});
datahandler("FleetIcon", [&](std::string& value) {
if(def) {
def->fleetIcon = devices.library.getSprite(value);
def->fleetIconName = value;
}
});
datahandler("IconSheet", [&](std::string& value) {
if(def) {
def->iconSheet = &devices.library.getSpriteSheet(value);
def->iconName = value;
}
});
datahandler("IconIndex", [&](std::string& value) {
if(def)
def->iconIndex = toNumber<unsigned>(value);
});
datahandler("MinSize", [&](std::string& value) {
if(def)
def->minSize = toNumber<double>(value);
});
datahandler("MaxSize", [&](std::string& value) {
if(def)
def->maxSize = toNumber<double>(value);
});
datahandler("Special", [&](std::string& value) {
if(def)
def->special = toBool(value);
});
datahandler.read(filename);
}
bool HullDef::hasTag(const std::string& tag) const {
auto it = std::find(tags.begin(), tags.end(), tag);
return it != tags.end();
}
void writeHullDefinitions(const std::string& filename, std::vector<HullDef*>& hulls) {
std::ofstream file(filename);
foreach(it, hulls) {
const HullDef* def = *it;
file << "Hull: " << def->ident << "\n";
file << "\tName: " << def->name << "\n";
file << "\tBackground: " << def->backgroundName << "\n";
if(def->backgroundScale != 1.0)
file << "\tBackgroundScale: " << def->backgroundScale << "\n";
file << "\tMaterial: " << def->materialName << "\n";
file << "\tModel: " << def->meshName << "\n";
file << "\tGuiIcon: " << def->guiIconName << "\n";
if(!def->fleetIconName.empty())
file << "\tFleetIcon: " << def->fleetIconName << "\n";
file << "\tIconSheet: " << def->iconName << "\n";
file << "\tIconIndex: " << def->iconIndex << "\n";
file << "\n";
if(!def->tags.empty()) {
file << "\tTags: ";
bool first = true;
for(auto t = def->tags.begin(), tend = def->tags.end(); t != tend; ++t) {
if(!first)
file << ", ";
first = false;
file << *t;
}
file << "\n\n";
}
file << "\tGridSize: " << def->gridSize.x << ", " << def->gridSize.y << "\n";
file << "\tGridOffset: " << def->gridOffset.topLeft.x << "," << def->gridOffset.topLeft.y;
file << ", " << def->gridOffset.botRight.x << "," << def->gridOffset.botRight.y << "\n";
file << "\n";
for(int y = 0; y < def->gridSize.y; ++y) {
file << "\t";
for(int x = 0; x < def->gridSize.x; ++x) {
if(x != 0)
file << " ";
if(def->active.get(x, y)) {
if(def->exterior.get(x, y)) {
file << "#";
}
else {
file << "X";
}
}
else {
file << "-";
}
}
file << "\n";
}
file << "\n";
}
}
unsigned Shipset::getHullCount() const {
return hulls.size();
}
ShipSkin* Shipset::getSkin(const std::string& name) const {
auto it = skins.find(name);
if(it == skins.end())
return nullptr;
return it->second;
}
Shipset::~Shipset() {
for(auto it = skins.begin(); it != skins.end(); ++it)
delete it->second;
}
const HullDef* Shipset::getHull(unsigned index) const {
if(index >= hulls.size())
return nullptr;
return hulls[index];
}
const HullDef* Shipset::getHull(const std::string& ident) const {
for(auto i = hulls.begin(), end = hulls.end(); i != end; ++i)
if((*i)->ident == ident)
return *i;
return nullptr;
}
bool Shipset::hasHull(const HullDef* hull) const {
for(auto i = hulls.begin(), end = hulls.end(); i != end; ++i)
if(*i == hull)
return true;
return false;
}
void loadShipset(const std::string& filename) {
Shipset* set = nullptr;
ShipSkin* skin = nullptr;
DataHandler datahandler;
datahandler("Shipset", [&](std::string& value) {
if(shipsetIndices.find(value) != shipsetIndices.end())
error("Duplicate Shipset ID: %s", value.c_str());
set = new Shipset();
set->ident = value;
set->available = true;
set->id = (unsigned)shipsets.size();
shipsetIndices[value] = set->id;
shipsets.push_back(set);
});
datahandler("Name", [&](std::string& value) {
if(set)
set->name = value;
});
datahandler("DLC", [&](std::string& value) {
if(set)
set->dlc = value;
});
datahandler("Available", [&](std::string& value) {
if(set)
set->available = toBool(value, true);
});
datahandler("Hull", [&](std::string& value) {
if(set) {
auto* hull = getHullDefinition(value);
if(hull)
set->hulls.push_back(hull);
else
error("Could not find hull %s for shipset %s", value.c_str(), set->ident.c_str());
}
});
datahandler("Skin", [&](std::string& value) {
if(set) {
skin = new ShipSkin();
skin->ident = value;
set->skins[value] = skin;
}
});
datahandler("Model", [&](std::string& value) {
if(skin && set) {
const render::RenderMesh* mesh = &devices.library.getMesh(value);
if(!mesh) {
if(devices.render)
error("(%s Ship Skin %s): Error: Unknown model %s.", set->ident.c_str(), skin->ident.c_str(), value.c_str());
return;
}
skin->mesh = mesh;
}
});
datahandler("Material", [&](std::string& value) {
if(skin && set) {
const render::RenderState* mat = &devices.library.getMaterial(value);
if(!mat) {
if(devices.render)
error("(%s Ship Skin %s): Error: Unknown material %s.", set->ident.c_str(), skin->ident.c_str(), value.c_str());
return;
}
skin->material = mat;
}
});
datahandler("Icon", [&](std::string& value) {
if(skin && set) {
skin->icon = devices.library.getSprite(value);
}
});
datahandler.read(filename);
}
void initAllShipset() {
auto* set = new Shipset();
set->ident = "ALL";
set->available = false;
set->id = (unsigned)shipsets.size();
for(size_t i = 0, cnt = shipsets.size(); i < cnt; ++i) {
auto* other = shipsets[i];
if(!other->available)
continue;
for(size_t n = 0, ncnt = other->hulls.size(); n < ncnt; ++n)
set->hulls.push_back(other->hulls[n]);
}
shipsetIndices[set->ident] = set->id;
shipsets.push_back(set);
}
void clearShipsets() {
shipsets.clear(); shipsets.shrink_to_fit();
shipsetIndices.clear();
}
unsigned getShipsetCount() {
return (unsigned)shipsets.size();
}
const Shipset* getShipset(unsigned id) {
if(id >= (unsigned)shipsets.size())
return nullptr;
return shipsets[id];
}
const Shipset* getShipset(const std::string& ident) {
auto i = shipsetIndices.find(ident);
if(i == shipsetIndices.end())
return nullptr;
return shipsets[i->second];
}
+125
View File
@@ -0,0 +1,125 @@
#pragma once
#include <vector>
#include "util/hex_grid.h"
#include "util/refcount.h"
#include "vec2.h"
#include "rect.h"
#include "render/render_state.h"
#include "render/render_mesh.h"
#include "render/spritesheet.h"
#include <unordered_set>
#include <unordered_map>
class Shipset;
class HullDef : public AtomicRefCounted {
public:
static const unsigned flagExteriorPass = 1 << 31;
static const unsigned flagExteriorFaux = 1 << 30;
unsigned id;
std::string ident;
std::string name;
std::string backgroundName;
std::string meshName;
std::string materialName;
std::string iconName;
std::string guiIconName;
std::string fleetIconName;
std::vector<std::string> tags;
std::vector<std::string> subsystems;
std::unordered_set<int> numTags;
const render::RenderState* background;
const render::RenderState* material;
const render::RenderMesh* mesh;
const render::SpriteSheet* iconSheet;
render::Sprite guiIcon;
render::Sprite fleetIcon;
unsigned iconIndex;
vec2i gridSize;
recti gridOffset;
double backgroundScale;
double modelScale;
HexGrid<bool> active;
HexGrid<int> exterior;
unsigned activeCount;
unsigned exteriorCount;
double minSize;
double maxSize;
mutable heldPointer<const HullDef> baseHull;
Image* shape;
std::string shapeMap;
bool shapeMapped;
bool special;
HullDef();
HullDef(const HullDef& other);
std::vector<vec3d> impacts;
bool calculateImpacts();
vec3d getImpact(const vec3d& offset, double radius, bool constant = false) const;
vec3d getClosestImpact(const vec3d& offset) const;
void calculateDist();
void calculateDist(vec2u pos, int dist);
void calculateExterior();
void calculateExterior(vec2u pos, unsigned direction);
bool checkConnected();
void fillConnected(HexGrid<bool>& connected, vec2u hex);
double getMatchDistance(const vec2d& pos) const;
double getMatchDistance(void* desc) const;
bool isExterior(const vec2u& hex) const;
bool isExteriorInDirection(const vec2u& hex, unsigned dir) const;
bool hasTag(const std::string& tag) const;
};
struct ShipSkin {
std::string ident;
const render::RenderState* material;
const render::RenderMesh* mesh;
render::Sprite icon;
};
class Shipset : public AtomicRefCounted {
public:
unsigned id;
std::vector<const HullDef*> hulls;
bool available;
std::string ident, name;
std::string dlc;
std::unordered_map<std::string,ShipSkin*> skins;
~Shipset();
ShipSkin* getSkin(const std::string& name) const;
bool hasHull(const HullDef* hull) const;
unsigned getHullCount() const;
const HullDef* getHull(unsigned index) const;
const HullDef* getHull(const std::string& ident) const;
};
void loadHullDefinitions(const std::string& filename);
void clearHullDefinitions();
void readHullDefinitions(const std::string& filename, std::vector<HullDef*>& hulls);
void writeHullDefinitions(const std::string& filename, std::vector<HullDef*>& hulls);
unsigned getHullCount();
const HullDef* getHullDefinition(unsigned id);
const HullDef* getHullDefinition(const std::string& ident);
void computeHulls(unsigned amount);
bool isFinishedComputingHulls();
void loadShipset(const std::string& filename);
void initAllShipset();
void clearShipsets();
unsigned getShipsetCount();
const Shipset* getShipset(unsigned id);
const Shipset* getShipset(const std::string& ident);
+749
View File
@@ -0,0 +1,749 @@
#include "projectiles.h"
#include "line3d.h"
#include "main/references.h"
#include "processing.h"
#include "physics/physics_world.h"
#include "obj/object.h"
#include "empire.h"
#include "design/effector.h"
#include "threads.h"
#include "scene/node.h"
#include "scene/particle_system.h"
#include "memory/AllocOnlyPool.h"
#include "threads.h"
#include "obj/lock.h"
#include "scene/animation/anim_projectile.h"
#include "util/save_file.h"
#include "design/design.h"
#include "obj/blueprint.h"
#include "network/network_manager.h"
#include "ISound.h"
#include "ISoundDevice.h"
#include <climits>
#include <algorithm>
memory::AllocOnlyPool<Projectile,threads::Mutex> projPool(8192);
void initNewThread();
void cleanupThread();
struct ProjEffect : public ObjectMessage {
Object* source;
const Effector* effector;
vec3d relImpact;
float effectiveness;
float partiality;
float delay;
ProjEffect(const Effector* fctr, Object* target, Object* src, vec3d relImpactPt, float eff, float part, float del) : ObjectMessage(target), source(src), effector(fctr), relImpact(relImpactPt), effectiveness(eff), partiality(part), delay(del) {
source->grab();
effector->grab();
}
~ProjEffect() {
object->drop();
source->drop();
effector->drop();
}
void process() override {
effector->triggerEffect(source, object, relImpact, effectiveness, partiality, delay);
}
};
struct ProjImpactEffect : public scene::NodeEvent {
const scene::ParticleSystemDesc* system;
vec3d from, vel;
float scale;
float delay;
ProjImpactEffect(scene::Node* parentNode, const scene::ParticleSystemDesc* sys, const vec3d& From, const vec3d& Vel, float Scale, float Delay) : NodeEvent(parentNode), system(sys), from(From), vel(Vel), scale(Scale), delay(Delay) {
}
void process() override {
vec3d pos;
quaterniond rot;
if(node) {
vec3d d = (from - node->abs_position).normalized();
pos = (from - node->abs_position);
rot = quaterniond::fromImpliedTransform(vec3d::front(), d);
}
else {
pos = from;
}
auto* pSys = scene::playParticleSystem(system, node, pos, rot, vel, scale, delay);
if(pSys)
pSys->drop();
}
~ProjImpactEffect() {
}
};
double clamp(double v, double low, double high) {
if(v <= low)
return low;
else if(v >= high)
return high;
else
return v;
}
static inline double sqr(double x) {
return x * x;
}
//Look for a new objec hostile to the owner
Object* findTarget(Empire* owner, const vec3d& position, double radius, const void* ofType = nullptr) {
if(!owner)
return nullptr;
Object* obj = nullptr;
devices.physics->findInBox(AABBoxd::fromCircle(position, radius), [&obj,owner,&position,radius,ofType](const PhysicsItem& item) {
if(obj)
return;
if(item.type == PIT_Object) {
Object* target = item.object;
if(!target->isVisibleTo(owner) || !target->isValid())
return;
if(ofType && target->type != ofType)
return;
if(position.distanceToSQ(target->position) > (radius + target->radius) * (radius + target->radius))
return;
obj = target;
obj->grab();
}
}, owner->hostileMask);
return obj;
}
bool Projectile::tick(double time) {
//Rotate to track the target
if(type == PT_Missile) {
if(target && target->isValid()) {
if(recover <= 0.f) {
vec3d dir = (target->position - position).normalized();
vec3d projImpact = target->position + dir;
if(target->type->blueprintOffset != 0) {
auto* blueprint = (Blueprint*)(((size_t)target) + target->type->blueprintOffset);
const Design* dsg = blueprint->design;
if(dsg != nullptr) {
vec3d impactOffset = target->rotation.inverted() * dir;
projImpact = target->rotation * dsg->hull->getImpact(impactOffset, target->radius, true) + target->position;
}
}
dir = (projImpact - position).normalized();
double speed = velocity.getLength();
vec3d vdir = velocity / speed;
double angle = acos(clamp(vdir.dot(dir),-1,1)), rot = tracking * time;
if(angle < rot) {
velocity = dir * speed;
}
else if(angle >= pi * 0.9) {
quaterniond dirq = quaterniond::fromImpliedTransform(vec3d::front(), dir);
quaterniond vdirq = quaterniond::fromImpliedTransform(vec3d::front(), vdir);
quaterniond result = vdirq.slerp(dirq, rot / angle);
velocity = (result * vec3d::front()).normalized(speed);
}
else {//if(angle < 0.05) {
//Lerp is a good enough approximation
// -- It really is not :( stupid missiles can't even turn 180 degrees
velocity = vdir.interpolate(dir, rot / angle).normalized(speed);
}
if(graphics) {
auto* anim = (scene::ProjectileAnim*)graphics->animator.ptr;
anim->velocity = velocity;
}
}
}
else if(source && source->isValid()) {
if(target) {
auto* prevTarget = target;
target = findTarget(source->owner, target->position, 50.0, target->type);
prevTarget->drop();
}
else {
target = findTarget(source->owner, position, 50.0);
}
//If we didn't quickly find a target, we die twice as fast
if(target == 0 && !effector->type.pierces)
lifetime *= 0.5f;
}
else {
//Become a dummy when the target is already dead
//Also live only half the remaining time
lifetime *= 0.5f;
if(target) {
target->drop();
target = 0;
}
}
}
else if(type == PT_Beam) {
//Beams can't fire when the source is dead
if(!source->isValid())
return true;
if(target) {
if(target->isValid()) {
if(tracking > 0) {
vec3d targDir = (target->position - (source->position + position)).normalize();
double range = velocity.getLength();
vec3d curDir = velocity / range;
double dot = std::max(std::min(targDir.dot(curDir), 1.0), -1.0);
double angDiff = acos(dot);
double track = tracking * time;
if(angDiff <= track)
velocity = targDir * range;
else
velocity = curDir.slerp(targDir,track/angDiff) * range;
}
}
else {
//Our current target is dead, find a new one
auto* prevTarget = target;
if(source)
target = findTarget(source->owner, target->position, 50.0, target->type);
else
target = 0;
prevTarget->drop();
}
}
}
line3dd line(position, vec3d());
if(type != PT_Beam) {
line.end = position + velocity * time;
}
else {
line.start += source->position;
line.end = line.start + velocity;
}
Object* other = 0;
vec3d impactPt;
double prevDistSQ = 0;
double projScale = scale;
//If our line still collides with our last impacted object, we can check for only intervening objects
if(lastImpact && lastImpact->isValid()) {
vec3d closePt = line.getClosestPoint(lastImpact->position,false);
double distSQ = closePt.distanceToSQ(lastImpact->position);
double width = lastImpact->radius + scale;
if(distSQ <= width * width) {
other = lastImpact;
other->grab();
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
if(!effector || !effector->type.pierces)
line.end = impactPt;
prevDistSQ = closePt.distanceToSQ(line.start);
}
}
if(mode != PM_OnlyHitsTarget) {
if(mode == PM_PassthroughInvalid) {
const Effector* eff = effector;
Object* src = source;
devices.physics->findInBox(AABBoxd(line, scale), [&other,&line,&prevDistSQ,&impactPt,projScale,eff,src](const PhysicsItem& item) {
//if(item.type != PIT_Object)
// return;
Object* obj = item.object;
vec3d closePt = line.getClosestPoint(obj->position,false);
double distSQ = closePt.distanceToSQ(obj->position);
double width = obj->radius + projScale;
if(distSQ <= width * width && (!eff || eff->canTarget(src, obj))) {
double objDistSQ = closePt.distanceToSQ(line.start);
if(!other || objDistSQ < prevDistSQ) {
if(other)
other->drop();
other = obj;
other->grab();
prevDistSQ = objDistSQ;
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
}
}
}, source->owner ? (source->owner->hostileMask | 0x1) : ~0x0);
}
else {
devices.physics->findInBox(AABBoxd(line, scale), [&other,&line,&prevDistSQ,&impactPt,projScale](const PhysicsItem& item) {
//if(item.type != PIT_Object)
// return;
Object* obj = item.object;
vec3d closePt = line.getClosestPoint(obj->position,false);
double distSQ = closePt.distanceToSQ(obj->position);
double width = obj->radius + projScale;
if(distSQ <= width * width) {
double objDistSQ = closePt.distanceToSQ(line.start);
if(!other || objDistSQ < prevDistSQ) {
if(other)
other->drop();
other = obj;
other->grab();
prevDistSQ = objDistSQ;
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
}
}
}, source->owner ? (source->owner->hostileMask | 0x1) : ~0x0);
}
}
else if(target && target->isValid()){
vec3d closePt = line.getClosestPoint(target->position,false);
double distSQ = closePt.distanceToSQ(target->position);
double width = target->radius + projScale;
if(distSQ <= width * width) {
if(other)
other->drop();
other = target;
other->grab();
impactPt = closePt - line.getDirection() * sqrt(width*width - distSQ);
}
}
if(other != nullptr && other->type->blueprintOffset != 0) {
auto* blueprint = (Blueprint*)(((size_t)other) + other->type->blueprintOffset);
const Design* dsg = blueprint->design;
if(dsg != nullptr) {
vec3d impactOffset = impactPt - other->position;
impactOffset = other->rotation.inverted() * impactOffset;
impactPt = other->rotation * dsg->hull->getImpact(impactOffset, other->radius, type == PT_Beam) + other->position;
}
}
if(recover > 0.f) {
if(other && other == lastImpact) {
other->drop();
other = nullptr;
}
recover -= time;
if(recover <= 0.f)
recover = 0.f;
}
if(other) {
if(impact)
*impact = impactPt;
if(type == PT_Beam || effector->type.pierces) {
other->grab();
if(lastImpact)
lastImpact->drop();
lastImpact = other;
}
auto* player = Empire::getPlayerEmpire();
if(effector) {
if(player && other->isVisibleTo(player)) {
if(auto* sfx = effector->type.skins[effector->skinIndex].impact_sound) {
if(sfx->loaded && !audio::disableSFX) {
auto* sound = devices.sound->play3D(sfx->source, snd_vec(other->position), false, true);
if(sound) {
if(source)
sound->setVolume((float)((scale + 1.0) / (scale + 7.0)));
sound->setPitch((float)randomd(0.95,1.05));
float dist = other->position.distanceTo(devices.render->cam_pos);
float lo = dist / (dist + scale * 500.0);
if(lo > 0.05)
sound->setLowPass(lo);
sound->resume();
}
}
}
}
}
LockGroup* lockGroup = other->lockGroup;
float delay = 0.f;
if(effector && lockGroup) {
if(type == PT_Missile && missileData) {
double speed = velocity.getLength();
delay = impactPt.distanceTo(position) / speed * 1.5f;
}
if(effector->type.skins[effector->skinIndex].impact && (!player || other->isVisibleTo(player)))
scene::queueNodeEvent(new ProjImpactEffect(other->node, effector->type.skins[effector->skinIndex].impact, impactPt, other->velocity, (float)sqrt(effector->relativeSize * (source ? source->radius : 1.0)), delay));
if(!devices.network->isClient)
lockGroup->addMessage(new ProjEffect(effector, other, source, impactPt - other->position, efficiency, type == PT_Beam ? (float)time : 1.f, delay));
else
other->drop();
}
else {
other->drop();
}
if(type != PT_Beam) {
if(effector && effector->type.pierces) {
recover = effector->type.recoverTime;
if(target && other == target) {
target->drop();
target = nullptr;
}
}
else {
if(type == PT_Missile && missileData) {
auto& data = **missileData;
double gameTime = devices.driver->getGameTime();
double speed = velocity.getLength();
data.lastUpdate = gameTime;
data.aliveUntil = gameTime + delay;
data.pos = impactPt;
data.vel = vec3f((impactPt - position).normalized(speed));
}
return true;
}
}
}
else if(impact) {
*impact = line.end;
}
if(type != PT_Beam) {
position = line.end;
if(type == PT_Missile && missileData) {
auto& data = **missileData;
data.lastUpdate = devices.driver->getGameTime();
data.pos = position;
data.vel = vec3f(velocity);
}
}
lifetime -= (float)time;
return lifetime <= 0;
}
void* Projectile::operator new(size_t size) {
return projPool.alloc();
}
void Projectile::operator delete(void* p) {
return projPool.dealloc((Projectile*)p);
}
Projectile::Projectile(ProjType Type) : lastTick(devices.driver->getGameTime()), source(0), target(0), lastImpact(0), graphics(0), type(Type), impact(0), efficiency(1.f), endNotice(0), mode(PM_Normal), recover(0.f) {
}
Projectile::~Projectile() {
if(graphics) {
graphics->markForDeletion();
graphics->drop();
}
if(type != PT_Missile) {
if(endNotice) {
**endNotice = true;
endNotice->drop();
}
}
else {
if(missileData) {
if((*missileData)->aliveUntil < 0)
(*missileData)->aliveUntil = 0;
missileData->drop();
}
}
if(source)
source->drop();
if(target)
target->drop();
if(lastImpact)
lastImpact->drop();
if(effector)
effector->drop();
}
extern double frameTime_s;
Projectile* Projectile::load(SaveFile& file) {
const Effector* efftr = nullptr;
unsigned char empID;
int dsgId;
unsigned subsysIndex, effectorIndex, effId;
if((bool)file) {
empID = file;
if(empID == INVALID_EMPIRE)
return 0;
dsgId = file;
subsysIndex = file;
effectorIndex = file;
Empire* dsgOwner = Empire::getEmpireByID(empID);
const Design* dsg = dsgOwner->getDesign(dsgId);
if(dsg == nullptr || subsysIndex >= dsg->subsystems.size() || effectorIndex >= dsg->subsystems[subsysIndex].type->effectors.size()) {
//throw "Invalid projectile effector.";
// Just cancel the projectile later so we can continue the load.
}
else {
efftr = &dsg->subsystems[subsysIndex].effectors[effectorIndex]; //No refcounting on design effectors
}
}
else {
effId = file;
efftr = getEffector(effId); //Reference is transfered to projectile
}
//Find effector
Projectile* proj = new Projectile((ProjType)file.read<unsigned char>());
proj->effector = efftr;
//Load data
file >> proj->lastTick;
file >> proj->lifetime;
proj->source = file.readExistingObject();
proj->target = file.readExistingObject();
file >> proj->position;
file >> proj->velocity;
file >> proj->scale;
file >> proj->tracking;
file >> proj->efficiency;
//Actually screw this, these things cause nothing but problems
delete proj;
return 0;
if(!proj->source || !proj->effector) {
//This can happen if the object that fired the projectile
//was already destroyed when the save occured. We have no way
//of instantiating these or their effects at the moment, because
//we know absolutely nothing about the source.
//TODO: Preserve these in some way, they can still impact and matter
//quite a bit.
delete proj;
return 0;
}
//TODO: The effects are non-trivial, figure out how to restore them
//Create graphics
/*proj->graphics = proj->effector->type.createGraphics(proj->effector, proj->scale);
if(!proj->graphics) {
delete proj;
throw "Invalid projectile effector graphics.";
}
proj->graphics->position = proj->position;
proj->graphics->rebuildTransformation();
proj->graphics->animator = new scene::ProjectileAnim(proj->velocity, 1.f);
registerProjectile(proj);
proj->graphics->queueReparent(devices.scene);
return proj;*/
registerProjectile(proj);
return proj;
}
void Projectile::save(SaveFile& file) {
if(effector && effector->effectorId != 0) {
file << false;
file << effector->effectorId;
}
else {
file << true;
if(!effector || !effector->inDesign || !effector->inDesign->owner) {
file << INVALID_EMPIRE;
return;
}
file << effector->inDesign->owner->id;
file << effector->inDesign->id;
file << effector->subsysIndex;
file << effector->effectorIndex;
}
file << (unsigned char)type;
file << lastTick;
file << lifetime;
file << source;
file << target;
file << position;
file << velocity;
file << scale;
file << tracking;
file << efficiency;
}
threads::Mutex projAddLock, projfillLock;
threads::Signal activeProjThreads;
threads::atomic_int pull_index, push_index, processed_count;
std::vector<double> projThreadTimes;
std::vector<Projectile*>* source = new std::vector<Projectile*>, *dest = new std::vector<Projectile*>;
std::vector<Projectile*> queuedProjs;
static void fillProjectiles() {
dest->resize(push_index);
if(!queuedProjs.empty()) {
projAddLock.lock();
dest->insert(dest->end(), queuedProjs.begin(), queuedProjs.end());
queuedProjs.clear();
projAddLock.release();
}
std::swap(source, dest);
dest->resize(source->size());
processed_count = 0;
push_index = 0;
pull_index = 0;
}
volatile bool ProjectilesActive = false;
volatile bool EndProjectiles = false;
volatile bool PauseProjectiles = false;
volatile bool ProjectilesPaused = false;
class ProcessProjectiles : public processing::Action {
bool host, active;
public:
ProcessProjectiles(bool Host) : host(Host), active(true) {
}
~ProcessProjectiles() {
//This happens when the game is ending. We may not complete an entire cycle at the right time.
if(host)
ProjectilesActive = false;
else if(active)
activeProjThreads.signalDown();
}
bool run() {
if(host && activeProjThreads.check(0)) {
if(EndProjectiles)
return true;
ProjectilesActive = true;
if(PauseProjectiles) {
ProjectilesActive = false;
ProjectilesPaused = true;
return false;
}
else if(ProjectilesPaused) {
ProjectilesPaused = false;
}
else {
fillProjectiles();
}
if(!source->empty()) {
activeProjThreads.signal(4);
for(unsigned i = 0; i < 4; ++i)
processing::queueAction(new ProcessProjectiles(false));
}
else {
ProjectilesActive = false;
return false;
}
}
double curTime = devices.driver->getGameTime();
unsigned tickMax = 1000;
while(tickMax--) {
int index = pull_index++;
if(index >= (int)source->size()) {
if(host) {
ProjectilesActive = false;
return false;
}
else {
active = false;
activeProjThreads.signalDown();
return true;
}
}
//Take the next projectile, tick it, and put it on the output stack (unless it needs deleted)
Projectile* proj = source->at(index);
double t = curTime - proj->lastTick;
if(t < 0.125) {
int outIndex = push_index++;
dest->at(outIndex) = proj;
}
else {
if(!proj->tick(t)) {
proj->lastTick = curTime;
int outIndex = push_index++;
dest->at(outIndex) = proj;
}
else {
delete proj;
}
}
}
return false;
}
};
void registerProjectile(Projectile* proj) {
projAddLock.lock();
queuedProjs.push_back(proj);
projAddLock.release();
}
void initProjectiles() {
EndProjectiles = false;
PauseProjectiles = false;
processing::queueAction(new ProcessProjectiles(true));
}
void stopProjectiles() {
EndProjectiles = true;
while(ProjectilesActive)
threads::sleep(1);
activeProjThreads.wait(0);
source->clear(); source->shrink_to_fit();
dest->clear(); dest->shrink_to_fit();
for(auto i = queuedProjs.begin(), end = queuedProjs.end(); i != end; ++i)
delete *i;
queuedProjs.clear();
pull_index = 0;
push_index = 0;
processed_count = 0;
}
void saveProjectiles(SaveFile& file) {
unsigned cnt = (unsigned)(queuedProjs.size() + push_index);
file << cnt;
cnt = (unsigned)queuedProjs.size();
for(unsigned i = 0; i < cnt; ++i)
queuedProjs[i]->save(file);
cnt = (unsigned)push_index;
for(unsigned i = 0; i < cnt; ++i)
(*dest)[i]->save(file);
}
void loadProjectiles(SaveFile& file) {
unsigned cnt = file;
for(unsigned i = 0; i < cnt; ++i)
Projectile::load(file);
}
void pauseProjectiles() {
PauseProjectiles = true;
if(!ProjectilesActive)
return;
activeProjThreads.wait(0);
while(!ProjectilesPaused && ProjectilesActive)
threads::sleep(1);
}
void resumeProjectiles() {
PauseProjectiles = false;
}
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include "vec3.h"
#include "threads.h"
#include <stddef.h>
namespace scene {
class Node;
};
struct MissileData {
vec3d pos;
double lastUpdate;
vec3f vel;
double aliveUntil;
};
class Effector;
class Object;
class SaveFile;
enum ProjType {
PT_Bullet,
PT_Beam,
PT_Missile
};
enum ProjMode {
PM_Normal,
PM_OnlyHitsTarget,
PM_PassthroughInvalid
};
struct Projectile {
//NOTE: Velocity is reused to specify a beam's target
vec3d position, velocity;
double lastTick;
vec3d* impact;
const Effector* effector;
Object* source, *target, *lastImpact;
scene::Node* graphics;
union {
threads::SharedData<bool>* endNotice;
threads::SharedData<MissileData>* missileData;
};
float lifetime;
float scale;
//radians per second of turning
float tracking;
//efficiency of the subsystem
float efficiency;
//recovery time in which nothing can be hit
float recover;
ProjType type;
ProjMode mode;
//Advances projectile and performs collision
//Returns true if the projectile is dead
bool tick(double time);
Projectile(ProjType Type);
~Projectile();
static Projectile* load(SaveFile& file);
void save(SaveFile& file);
void* operator new(size_t size);
void operator delete(void*);
};
void registerProjectile(Projectile* proj);
void initProjectiles();
void stopProjectiles();
void saveProjectiles(SaveFile& file);
void loadProjectiles(SaveFile& file);
void pauseProjectiles();
void resumeProjectiles();
File diff suppressed because it is too large Load Diff
+398
View File
@@ -0,0 +1,398 @@
#pragma once
#include "design/effect.h"
#include "design/effector.h"
#include "vec2.h"
#include "util/formula.h"
#include "util/basic_type.h"
#include "compat/misc.h"
#include "render/spritesheet.h"
#include <string>
#include <vector>
#include <unordered_set>
#include "util/hex_grid.h"
class SaveFile;
#ifndef MODIFY_STAGE_MAXARGS
#define MODIFY_STAGE_MAXARGS 8
#endif
enum SysVariableType {
SVT_SubsystemVariable,
SVT_HexVariable,
SVT_ShipVariable,
};
enum TemplateCondition {
TC_Tag,
TC_Modifier,
TC_Variable,
TC_HexVariable,
TC_ShipVariable,
TC_Subsystem,
TC_NOT = 1<<20,
};
class Design;
class Subsystem;
class HullDef;
class SubsystemDef {
public:
struct Variable {
Formula* formula;
std::string str_formula;
std::string name;
int index;
int globalId;
SysVariableType type;
bool dependent;
};
struct Effect {
const EffectDef* type;
std::vector<Formula*> values;
std::vector<std::string> str_values;
};
struct Effector {
const EffectorDef* type;
bool enabled;
unsigned skinIndex;
std::string skinName;
std::vector<Formula*> values;
std::vector<std::string> str_values;
};
struct StateDesc {
BasicTypes type;
Formula* formula;
std::string str_formula;
};
struct HookDesc {
std::string name;
std::vector<std::string> str_args;
std::vector<Formula*> formulas;
mutable std::vector<double> argValues;
HookDesc() {}
HookDesc(const std::string& str);
};
struct ModifyStage {
unsigned index;
int stage;
int umodifid;
std::unordered_map<std::string, int> argumentNames;
std::vector<std::pair<int, std::string>> str_variables;
std::vector<std::pair<int, std::string>> str_hexVariables;
std::vector<std::pair<int, std::string>> str_shipVariables;
std::unordered_map<int, Formula*> variables;
std::unordered_map<int, Formula*> hexVariables;
std::unordered_map<int, Formula*> shipVariables;
ModifyStage() : stage(0), umodifid(-1), index(0) {}
void applyVariables(Subsystem* sys) const;
void applyHexVariables(Subsystem* sys, int hexIndex) const;
void applyShipVariables(Design* dsg, Subsystem* sys) const;
};
struct AppliedStage {
const ModifyStage* stage;
float arguments[MODIFY_STAGE_MAXARGS];
Formula* formulas[MODIFY_STAGE_MAXARGS];
void clear() {
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i) {
if(formulas[i]) {
delete formulas[i];
formulas[i] = 0;
}
}
}
AppliedStage() {
memset(this, 0, sizeof(AppliedStage));
}
};
struct ShipModifier {
AppliedStage stage;
std::string modifyName;
std::vector<std::string> str_arguments;
std::vector<std::pair<int,std::string>> conditions;
};
struct Assert {
std::string str_formula;
Formula* formula;
std::string message;
bool fatal;
bool unique;
};
struct ModuleDesc {
int index;
std::string id;
std::string name;
std::string description;
Color color;
std::string umodident;
int umodid;
std::string def_onEnable;
std::string def_onDisable;
asIScriptFunction* scr_onEnable;
asIScriptFunction* scr_onDisable;
std::vector<ModifyStage> modifiers;
std::vector<ModifyStage> uniqueModifiers;
std::vector<AppliedStage> appliedStages;
std::vector<std::string> str_appliedStages;
std::vector<AppliedStage> uniqueAppliedStages;
std::vector<std::string> str_uniqueAppliedStages;
std::vector<AppliedStage> hexAppliedStages;
std::vector<std::string> str_hexAppliedStages;
std::vector<SubsystemDef::ShipModifier> adjacentModifiers;
std::vector<SubsystemDef::Effect> effects;
std::unordered_set<std::string> tags;
std::unordered_set<int> numTags;
std::unordered_map<int, std::vector<std::string>> tagValues;
bool hasTag(const std::string& tag) const;
bool hasTag(int index) const;
const std::string& getTagValue(int index, unsigned num = 0) const;
unsigned getTagValueCount(int index) const;
bool hasTagValue(int index, const std::string& value) const;
std::string def_onCheckErrors;
asIScriptFunction* scr_onCheckErrors;
bool onCheckErrors(Design* design, Subsystem* sys, const vec2u& hex) const;
std::vector<HookDesc> hooks;
std::vector<Assert> asserts;
std::string spriteMat;
render::Sprite sprite;
int drawMode;
bool required;
bool unique;
bool vital;
bool defaultUnlock;
void onEnable(EffectEvent& evt, const vec2u& position) const;
void onDisable(EffectEvent& evt, const vec2u& position) const;
ModuleDesc()
: index(-1), scr_onEnable(0), scr_onDisable(0),
drawMode(0), required(false), unique(false), vital(false),
defaultUnlock(false), scr_onCheckErrors(nullptr) {
}
};
std::string name;
std::string description;
std::string id;
int index;
int ordering;
int damageOrder;
int elevation;
Color baseColor;
Color typeColor;
std::string hexMat;
std::string picMat;
render::Sprite picture;
std::vector<int> variableIndices;
std::vector<SubsystemDef::Variable> variables;
std::vector<int> hexVariableIndices;
std::vector<SubsystemDef::Variable> hexVariables;
std::vector<int> shipVariableIndices;
std::vector<SubsystemDef::Variable> shipVariables;
std::vector<SubsystemDef::Effect> effects;
std::vector<SubsystemDef::Effector> effectors;
std::vector<SubsystemDef::StateDesc> states;
std::vector<SubsystemDef::ModuleDesc*> modules;
std::vector<SubsystemDef::ShipModifier> shipModifiers;
std::vector<SubsystemDef::ShipModifier> postModifiers;
std::vector<SubsystemDef::ShipModifier> adjacentModifiers;
std::unordered_map<std::string, int> moduleIndices;
const SubsystemDef::ModuleDesc* defaultModule;
const SubsystemDef::ModuleDesc* coreModule;
std::vector<ModifyStage*> modifiers;
std::unordered_map<std::string, ModifyStage*> modifierIds;
std::vector<Assert> asserts;
uset<std::string> tags;
std::vector<std::string> hullTags;
uset<int> numTags;
std::unordered_map<int, std::vector<std::string>> tagValues;
void finalize();
bool hasTag(const std::string& tag) const;
bool hasTag(int index) const;
const std::string& getTagValue(int index, unsigned num = 0) const;
unsigned getTagValueCount(int index) const;
bool hasTagValue(int index, const std::string& value) const;
std::string def_onCheckErrors;
asIScriptFunction* scr_onCheckErrors;
std::vector<HookDesc> hooks;
bool onCheckErrors(Design* design, Subsystem* sys) const;
bool canUseOn(const HullDef* hull) const;
bool hasHullTag(const std::string& tag) const;
bool hasCore;
bool isContiguous;
bool exteriorCore;
bool defaultUnlock;
bool isHull;
bool isApplied;
bool hexLimitArc;
bool passExterior;
bool fauxExterior;
bool alwaysTakeDamage;
SubsystemDef();
~SubsystemDef();
};
extern int SV_Size, HV_Resistance, HV_HP, ShV_HexSize;
extern umap<std::string, int> subsystemIndices;
extern umap<std::string, int> variableIndices;
extern umap<std::string, int> hexVariableIndices;
extern umap<std::string, int> shipVariableIndices;
void clearSubsystemDefinitions();
void loadSubsystemDefinitions(const std::string& filename);
const SubsystemDef* getSubsystemDef(const std::string& name);
const SubsystemDef* getSubsystemDef(int id);
int getSubsystemDefCount();
void enumerateVariables(std::function<void(const std::string&,int)>);
int getVariableIndex(const std::string& name);
const std::string& getVariableId(int index);
void enumerateHexVariables(std::function<void(const std::string&,int)>);
int getHexVariableIndex(const std::string& name);
const std::string& getHexVariableId(int index);
void enumerateShipVariables(std::function<void(const std::string&,int)>);
unsigned getShipVariableCount();
int getShipVariableIndex(const std::string& name);
const std::string& getShipVariableId(int index);
void enumerateSysTags(std::function<void(const std::string&,int)>);
int getSysTagIndex(const std::string& name, bool create = false);
Formula* parseFormula(const std::string& str, const SubsystemDef* def = 0, const SubsystemDef::ModifyStage* modifier = 0);
void bindSubsystemMaterials();
void bindSubsystemHooks();
void finalizeSubsystems();
void executeSubsystemTemplates();
class Blueprint;
struct SubsystemEvent {
const Subsystem* subsystem;
const Design* design;
Object* obj;
Blueprint* blueprint;
void* data;
float efficiency;
float partiality;
};
class asIScriptFunction;
class asIScriptObject;
class SaveMessage;
class Subsystem {
public:
const SubsystemDef* type;
std::vector<vec2u> hexes;
std::vector<const SubsystemDef::ModuleDesc*> modules;
std::vector<SubsystemDef::ShipModifier> adjacentModifiers;
std::vector<std::vector<SubsystemDef::ShipModifier>> hexAdjacentModifiers;
std::vector<int> moduleCounts;
vec2u core;
vec3d direction;
int exteriorHexes;
bool hasErrors;
static asIScriptFunction* ScriptInitFunction;
static asIScriptFunction* ScriptHookFunctions[EH_COUNT];
Effector* effectors;
float* variables;
float* baseVariables;
float* hexVariables;
float* hexBaseVariables;
BasicType* defaults;
unsigned stateOffset;
unsigned effectorOffset;
unsigned dataOffset;
std::vector<Effect> effects;
std::vector<std::vector<unsigned>> hexEffects;
std::vector<asIScriptObject*> hookClasses;
void addHook(Design* design, const SubsystemDef::HookDesc& desc);
const Design* inDesign;
unsigned index;
int getModuleCount(int index);
float* variable(int index);
const float* variable(int index) const;
float* hexVariable(int index, int hexIndex);
const float* hexVariable(int index, int hexIndex) const;
Subsystem();
Subsystem(const SubsystemDef& def);
Subsystem(SaveFile& file);
~Subsystem();
void save(SaveFile& file) const;
void postLoad(Design* design);
void init(const SubsystemDef& def);
void init(SaveFile& file);
void initVariables(Design* design);
void initEffects(Design* design);
void initLinks(Design* design);
void evaluatePost(Design* design);
void evaluateAsserts(Design* design);
void skinEffectors(Empire& emp);
void applyAdjacencies(Design* design);
DamageEventStatus damage(DamageEvent& event, const vec2u& position) const;
DamageEventStatus globalDamage(DamageEvent& event, vec2u& position, vec2d& endPoint) const;
bool hasGlobalDamage() const;
void ownerChange(EffectEvent& event, Empire* prevEmpire, Empire* newEmpire) const;
void call(EffectHook hook, EffectEvent& event) const;
void tick(EffectEvent& event) const;
void save(EffectEvent& event, SaveMessage& msg) const;
void load(EffectEvent& event, SaveMessage& msg) const;
void markConnected(HexGrid<bool>& grid, vec2u hex);
void writeData(net::Message& msg) const;
Subsystem(net::Message& msg);
};