332 lines
7.6 KiB
ActionScript
332 lines
7.6 KiB
ActionScript
import resources;
|
|
import regions.regions;
|
|
import saving;
|
|
import anomalies;
|
|
import bool getCheatsEverOn() from "cheats";
|
|
|
|
Anomaly@ createAnomaly(const vec3d& position, uint typeID) {
|
|
ObjectDesc desc;
|
|
desc.type = OT_Anomaly;
|
|
desc.radius = 15.0;
|
|
desc.position = position;
|
|
desc.name = getAnomalyType(typeID).name;
|
|
desc.flags |= objNoDamage;
|
|
|
|
Anomaly@ anomaly = Anomaly(desc);
|
|
anomaly.rotation = quaterniond_fromAxisAngle(random3d(1.0), randomd(-pi,pi));
|
|
anomaly.setup(typeID);
|
|
return anomaly;
|
|
}
|
|
|
|
tidy class AnomalyScript {
|
|
const AnomalyType@ type;
|
|
const AnomalyState@ state;
|
|
array<const AnomalyOption@> options;
|
|
array<float> progresses(getEmpireCount(), 0.0);
|
|
bool delta = false;
|
|
bool choiceDelta = false;
|
|
StrategicIconNode@ icon;
|
|
|
|
float get_progress(Player& player, const Anomaly& obj) {
|
|
if(player == SERVER_PLAYER)
|
|
return 1.0;
|
|
else if(player.emp is null || !player.emp.valid)
|
|
return 0.0;
|
|
else
|
|
return progresses[player.emp.index];
|
|
}
|
|
|
|
float getEmpireProgress(Empire@ emp) {
|
|
if(emp is null || !emp.valid)
|
|
return 0.0;
|
|
else
|
|
return progresses[emp.index];
|
|
}
|
|
|
|
string get_narrative(Player& player, const Anomaly& obj) {
|
|
if(get_progress(player, obj) < 1.f)
|
|
return type.desc;
|
|
else if(state !is null && state.narrative.length > 0)
|
|
return state.narrative;
|
|
else
|
|
return type.narrative;
|
|
}
|
|
|
|
uint get_anomalyType(Player& player, const Anomaly& obj) {
|
|
if(player != SERVER_PLAYER && (get_progress(player, obj) < 1.f || type is null))
|
|
return 0;
|
|
else
|
|
return type.id;
|
|
}
|
|
|
|
bool get_isOptionSafe(uint index) {
|
|
if(index < options.length)
|
|
return options[index].isSafe;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
uint getOptionCount() const {
|
|
return options.length;
|
|
}
|
|
|
|
uint get_optionCount(Player& player, const Anomaly& obj) {
|
|
if(get_progress(player, obj) < 1.f)
|
|
return 0;
|
|
else
|
|
return options.length;
|
|
}
|
|
|
|
uint get_option(Player& player, const Anomaly& obj, uint index) {
|
|
if(get_progress(player, obj) < 1.f || index >= options.length)
|
|
return 0;
|
|
else
|
|
return options[index].id;
|
|
}
|
|
|
|
void clearOptions() {
|
|
options.length = 0;
|
|
}
|
|
|
|
void addOption(uint id) {
|
|
options.insertLast(type.options[id]);
|
|
}
|
|
|
|
string get_model(Player& player, const Anomaly& obj) {
|
|
if(type is null)
|
|
return "";
|
|
else if(get_progress(player, obj) >= 1.f && state !is null && state.modelName.length > 0)
|
|
return state.modelName;
|
|
else
|
|
return type.modelName;
|
|
}
|
|
|
|
string get_material(Player& player, const Anomaly& obj) {
|
|
if(type is null)
|
|
return "";
|
|
else if(get_progress(player, obj) >= 1.f && state !is null && state.matName.length > 0)
|
|
return state.matName;
|
|
else
|
|
return type.matName;
|
|
}
|
|
|
|
void setup(Anomaly& obj, uint typeID) {
|
|
@type = getAnomalyType(typeID);
|
|
if(type is null)
|
|
@type = getAnomalyType(0);
|
|
|
|
auto@ state = type.getState();
|
|
progressToState(obj, state.id);
|
|
|
|
makeMesh(obj);
|
|
}
|
|
|
|
void progressToState(Anomaly& obj, uint stateID) {
|
|
@state = type.states[stateID];
|
|
if(state !is null) {
|
|
for(uint i = 0; i < state.options.length; ++i) {
|
|
auto option = state.options[i];
|
|
if(option.chance < 1.f && randomd() > option.chance)
|
|
continue;
|
|
float chance = state.option_chances[i];
|
|
if(chance < 1.f && randomd() > chance)
|
|
continue;
|
|
options.insertLast(option);
|
|
}
|
|
}
|
|
if(options.length == 0) {
|
|
playParticleSystem("AnomalyCollapse", obj.position, obj.rotation, obj.radius, obj.visibleMask);
|
|
obj.destroy();
|
|
}
|
|
choiceDelta = true;
|
|
}
|
|
|
|
void choose(Anomaly& obj, Empire@ emp, uint option, Object@ target = null) {
|
|
if(option >= options.length || getEmpireProgress(emp) < 1.f)
|
|
return;
|
|
|
|
auto@ opt = options[option];
|
|
|
|
Targets@ targs;
|
|
if(opt.targets.length != 0) {
|
|
@targs = Targets(opt.targets);
|
|
@targs.fill(0).obj = target;
|
|
}
|
|
|
|
options.length = 0;
|
|
opt.choose(obj, emp, targs);
|
|
|
|
if(options.length == 0) {
|
|
playParticleSystem("AnomalyCollapse", obj.position, obj.rotation, obj.radius, obj.visibleMask);
|
|
obj.destroy();
|
|
}
|
|
choiceDelta = true;
|
|
}
|
|
|
|
void choose(Player& player, Anomaly& obj, uint option, Object@ target = null) {
|
|
choose(obj, player.emp, option, target);
|
|
}
|
|
|
|
void addProgress(Anomaly& obj, Empire@ emp, float amount) {
|
|
uint index = uint(emp.index);
|
|
if(index < progresses.length) {
|
|
float p = progresses[index];
|
|
if(p < 1.f) {
|
|
p += amount / type.scanTime;
|
|
delta = true;
|
|
if(p >= 1.f) {
|
|
emp.notifyAnomaly(obj);
|
|
progresses[index] = 1.f;
|
|
|
|
if(emp.valid && !getCheatsEverOn()) {
|
|
if(emp is playerEmpire) {
|
|
unlockAchievement("ACH_SCAN_ANOMALY");
|
|
modStat("STAT_ANOMS", 1);
|
|
}
|
|
|
|
if(mpServer && emp.player !is null)
|
|
clientAchievement(emp.player, "ACH_SCAN_ANOMALY");
|
|
}
|
|
}
|
|
else {
|
|
progresses[index] = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void load(Anomaly& obj, SaveFile& file) {
|
|
loadObjectStates(obj, file);
|
|
@type = getAnomalyType(file.readIdentifier(SI_AnomalyType));
|
|
if(type is null)
|
|
setup(obj, getDistributedAnomalyType().id);
|
|
else
|
|
makeMesh(obj);
|
|
|
|
uint count = 0;
|
|
file >> count;
|
|
options.length = count;
|
|
for(uint i = 0; i < count; ++i) {
|
|
uint id = 0;
|
|
file >> id;
|
|
@options[i] = type.options[id % type.options.length];
|
|
}
|
|
|
|
for(uint i = 0; i < progresses.length; ++i)
|
|
file >> progresses[i];
|
|
|
|
uint stateId = 0;
|
|
file >> stateId;
|
|
if(stateId < type.states.length)
|
|
@state = type.states[stateId];
|
|
}
|
|
|
|
void save(Anomaly& obj, SaveFile& file) {
|
|
saveObjectStates(obj, file);
|
|
file.writeIdentifier(SI_AnomalyType, type.id);
|
|
file << uint(options.length);
|
|
for(uint i = 0; i < options.length; ++i)
|
|
file << options[i].id;
|
|
for(uint i = 0; i < progresses.length; ++i)
|
|
file << progresses[i];
|
|
if(state is null) {
|
|
uint id = uint(-1);
|
|
file << id;
|
|
}
|
|
else
|
|
file << state.id;
|
|
}
|
|
|
|
void postInit(Anomaly& obj) {
|
|
updateRegion(obj);
|
|
}
|
|
|
|
void destroy(Anomaly& obj) {
|
|
if(obj.region !is null)
|
|
obj.region.removeStrategicIcon(-1, icon);
|
|
icon.markForDeletion();
|
|
leaveRegion(obj);
|
|
}
|
|
|
|
void makeMesh(Anomaly& obj) {
|
|
MeshDesc mesh;
|
|
if(type !is null) {
|
|
@mesh.model = getModel(type.modelName);
|
|
@mesh.material = getMaterial(type.matName);
|
|
}
|
|
else {
|
|
@mesh.model = model::Debris;
|
|
@mesh.material = material::Asteroid;
|
|
}
|
|
mesh.memorable = true;
|
|
bindMesh(obj, mesh);
|
|
|
|
@icon = StrategicIconNode();
|
|
icon.establish(obj, 0.0225, spritesheet::AnomalyIcons, 0);
|
|
icon.memorable = true;
|
|
|
|
if(obj.region !is null) {
|
|
obj.region.addStrategicIcon(-1, obj, icon);
|
|
Node@ node = obj.getNode();
|
|
if(node !is null)
|
|
node.hintParentObject(obj.region, false);
|
|
}
|
|
}
|
|
|
|
double tick(Anomaly& obj, double time) {
|
|
if(icon !is null)
|
|
icon.visible = obj.isVisibleTo(playerEmpire);
|
|
Region@ reg = obj.region;
|
|
if(reg !is null)
|
|
obj.donatedVision |= reg.DonateVisionMask;
|
|
return 0.2;
|
|
}
|
|
|
|
void writeProgress(Message& msg) {
|
|
for(uint i = 0; i < progresses.length; ++i) {
|
|
float p = progresses[i];
|
|
if(p <= 0.0) {
|
|
msg.write0();
|
|
}
|
|
else {
|
|
msg.write1();
|
|
msg.writeFixed(p, 0.0, 1.0, 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeChoices(Message& msg) {
|
|
msg.writeSmall(type.id);
|
|
msg.writeSmall(state.id);
|
|
msg.writeSmall(options.length);
|
|
for(uint i = 0; i < options.length; ++i)
|
|
msg.writeLimited(options[i].id, type.options.length);
|
|
}
|
|
|
|
void syncInitial(const Anomaly& obj, Message& msg) {
|
|
writeChoices(msg);
|
|
writeProgress(msg);
|
|
}
|
|
|
|
bool syncDelta(const Anomaly& obj, Message& msg) {
|
|
if(!delta && !choiceDelta)
|
|
return false;
|
|
writeProgress(msg);
|
|
if(choiceDelta) {
|
|
msg.write1();
|
|
writeChoices(msg);
|
|
}
|
|
else {
|
|
msg.write0();
|
|
}
|
|
delta = false;
|
|
choiceDelta = false;
|
|
return true;
|
|
}
|
|
|
|
void syncDetailed(const Anomaly& obj, Message& msg) {
|
|
writeProgress(msg);
|
|
writeChoices(msg);
|
|
}
|
|
};
|