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
+331
View File
@@ -0,0 +1,331 @@
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);
}
};
+264
View File
@@ -0,0 +1,264 @@
import artifacts;
import regions.regions;
import saving;
import systems;
import object_creation;
import statuses;
from artifact_seeding import ArtifactCount;
from empire import Creeps;
Artifact@ createArtifact(const vec3d& position, const ArtifactType@ type, Region@ region = null) {
if(type is null)
return null;
ObjectDesc desc;
desc.type = OT_Artifact;
desc.radius = type.physicalSize;
desc.position = position;
desc.name = type.name;
desc.delayedCreation = true;
desc.flags |= objNoDamage;
Artifact@ obj = Artifact(desc);
obj.ArtifactType = type.id;
if(region !is null)
@obj.region = region;
obj.finalizeCreation();
if(region !is null)
region.enterRegion(obj);
return obj;
}
tidy class ArtifactScript {
const ArtifactType@ type;
StrategicIconNode@ icon;
uint regMask = 0;
double deepSpaceTime = 0;
double expire = -1.0;
void postInit(Artifact& obj) {
@type = getArtifactType(obj.ArtifactType);
if(type is null) {
error("Invalid artifact created...");
printTrace();
obj.destroy();
return;
}
obj.setNeutralAbilities(true);
obj.setAbilityDestroy(type.singleUse);
for(uint i = 0, cnt = type.abilities.length; i < cnt; ++i)
obj.createAbility(type.abilities[i].id);
if(type.orbit && obj.region !is null)
obj.orbitAround(obj.region.position);
makeMesh(obj);
ArtifactCount += 1;
}
void makeMesh(Artifact& obj) {
MeshDesc mesh;
@mesh.model = type.model;
@mesh.material = type.material;
mesh.memorable = true;
bindMesh(obj, mesh);
if(type.strategicIcon.valid) {
@icon = StrategicIconNode();
if(type.strategicIcon.sheet !is null)
icon.establish(obj, type.iconSize, type.strategicIcon.sheet, type.strategicIcon.index);
else if(type.strategicIcon.mat !is null)
icon.establish(obj, type.iconSize, type.strategicIcon.mat);
icon.memorable = true;
if(obj.region !is null) {
if(!obj.region.initialized)
@obj.region = null;
else
obj.region.addStrategicIcon(-1, obj, icon);
if(obj.region !is null) {
Node@ node = obj.getNode();
node.hintParentObject(obj.region, false);
}
}
}
}
void destroy(Artifact& obj) {
if(icon !is null) {
if(obj.region !is null)
obj.region.removeStrategicIcon(-1, icon);
icon.markForDeletion();
@icon = null;
}
leaveRegion(obj);
ArtifactCount -= 1;
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
Empire@ other = getEmpire(i);
if(!other.major)
continue;
if(regMask & other.mask != 0) {
regMask &= ~other.mask;
other.unregisterArtifact(obj);
}
}
}
void setExpire(double time) {
expire = time;
}
bool onOwnerChange(Artifact& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
if(obj.owner !is null && obj.owner.valid)
obj.setNeutralAbilities(false);
else
obj.setNeutralAbilities(true);
return false;
}
double tick(Artifact& obj, double time) {
Region@ prevRegion = obj.region;
if(updateRegion(obj)) {
Region@ newRegion = obj.region;
if(icon !is null) {
if(prevRegion !is null)
prevRegion.removeStrategicIcon(-1, icon);
if(newRegion !is null)
newRegion.addStrategicIcon(-1, obj, icon);
}
@prevRegion = newRegion;
Node@ node = obj.getNode();
if(node !is null)
node.hintParentObject(newRegion, false);
}
if(obj.region is null) {
deepSpaceTime += time;
if(deepSpaceTime > 14.0 * 60.0) {
deepSpaceTime = randomd(-30.0,30.0);
Ship@ ship = createShip(obj.position + random3d(4000.0),
Creeps.getDesign("Gravitar"), Creeps, free = true);
ship.addAbilityOrder(0, obj, obj.radius + ship.radius + 5.0, append=true);
auto@ reg = findNearestRegion(obj.position);
ship.addMoveOrder(reg.position + (obj.position - reg.position).normalize(reg.radius * 0.8) + random3d(10.0), append=true);
ship.addAbilityOrder(0, ship, obj.radius + ship.radius + 5.0, append=true);
ship.addMoveOrder(obj.position + random3d(10.0), append=true);
ship.addStatus(getStatusID("GravitarShip"));
}
}
else if(deepSpaceTime > 0.0) {
deepSpaceTime -= time * 0.5;
}
if(expire > 0) {
expire -= time;
if(expire <= 0.001)
obj.destroy();
}
if(prevRegion is null && isOutsideUniverseExtents(obj.position))
limitToUniverseExtents(obj.position);
Empire@ owner = obj.owner;
if(owner !is null && owner.valid && !type.canOwn)
@obj.owner = defaultEmpire;
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
Empire@ other = getEmpire(i);
if(!other.major)
continue;
bool usable = false;
if(owner !is null && owner.valid && owner !is other) {
usable = false;
}
else if(prevRegion !is null) {
if(prevRegion.PlanetsMask != 0) {
usable = prevRegion.PlanetsMask & other.mask != 0;
}
else {
usable = hasPlanetsAdjacent(other, prevRegion)
&& obj.memoryMask & other.mask != 0;
}
}
if(usable) {
if(regMask & other.mask == 0) {
regMask |= other.mask;
other.registerArtifact(obj);
}
}
else {
if(regMask & other.mask != 0) {
regMask &= ~other.mask;
other.unregisterArtifact(obj);
}
}
}
icon.visible = obj.isVisibleTo(playerEmpire);
obj.abilityTick(time);
obj.orbitTick(time);
return 0.5;
}
vec3d get_strategicIconPosition(Artifact& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
void load(Artifact& obj, SaveFile& file) {
loadObjectStates(obj, file);
@type = getArtifactType(file.readIdentifier(SI_Artifact));
obj.ArtifactType = type.id;
file >> cast<Savable>(obj.Abilities);
file >> cast<Savable>(obj.Orbit);
if(file >= SV_0029)
file >> regMask;
if(file >= SV_0121)
file >> expire;
}
void postLoad(Artifact& obj) {
makeMesh(obj);
}
void save(Artifact& obj, SaveFile& file) {
saveObjectStates(obj, file);
file.writeIdentifier(SI_Artifact, type.id);
file << cast<Savable>(obj.Abilities);
file << cast<Savable>(obj.Orbit);
file << regMask;
file << expire;
}
void syncInitial(const Artifact& obj, Message& msg) {
msg.writeSmall(type.id);
obj.writeOrbit(msg);
obj.writeAbilities(msg);
}
bool syncDelta(const Artifact& obj, Message& msg) {
bool used = false;
if(obj.writeAbilityDelta(msg))
used = true;
else
msg.write0();
if(obj.writeOrbitDelta(msg))
used = true;
else
msg.write0();
return used;
}
void syncDetailed(const Artifact& obj, Message& msg) {
obj.writeOrbit(msg);
obj.writeAbilities(msg);
}
};
+572
View File
@@ -0,0 +1,572 @@
import resources;
import regions.regions;
import saving;
import cargo;
import attributes;
from systems import hasTradeAdjacent;
Asteroid@ createAsteroid(const vec3d& position, Region@ region = null, bool delay = false) {
ObjectDesc desc;
desc.type = OT_Asteroid;
desc.radius = 5.0;
desc.position = position;
desc.name = locale::ASTEROID;
desc.flags |= objNoDamage;
if(region !is null)
desc.delayedCreation = true;
Asteroid@ obj = Asteroid(desc);
if(region !is null) {
@obj.region = region;
obj.finalizeCreation();
region.enterRegion(obj);
}
if(!delay)
obj.initMesh();
return obj;
}
tidy class AsteroidScript {
StrategicIconNode@ icon;
MeshNode@ baseNode;
array<const ResourceType@> available;
array<float> costs;
array<bool> exploited;
array<int> nativeIds;
bool delta = false;
uint resourceLimit = 1;
uint currentResources = 0;
uint limitMod = 0;
void load(Asteroid& obj, SaveFile& file) {
loadObjectStates(obj, file);
if(file >= SV_0122) {
file >> cast<Savable>(obj.Orbit);
file >> cast<Savable>(obj.Cargo);
}
else {
file >> cast<Savable>(obj.Resources);
file >> cast<Savable>(obj.Orbit);
}
if(file >= SV_0125)
file >> cast<Savable>(obj.Resources);
if(file < SV_0122 || file >= SV_0125) {
Object@ origin;
@origin = file.readObject();
uint cnt = 0;
file >> cnt;
available.length = cnt;
costs.length = cnt;
exploited.length = cnt;
nativeIds.length = cnt;
for(uint i = 0; i < cnt; ++i) {
@available[i] = getResource(file.readIdentifier(SI_Resource));
file >> costs[i];
file >> exploited[i];
file >> nativeIds[i];
}
file >> resourceLimit;
file >> limitMod;
file >> currentResources;
}
obj.HasBase = (obj.owner !is null && obj.owner.valid) ? 1.f : 0.f;
}
void postLoad(Asteroid& obj) {
makeMesh(obj);
}
void save(Asteroid& obj, SaveFile& file) {
saveObjectStates(obj, file);
file << cast<Savable>(obj.Orbit);
file << cast<Savable>(obj.Cargo);
file << cast<Savable>(obj.Resources);
file << obj.origin;
uint cnt = available.length;
file << cnt;
for(uint i = 0; i < cnt; ++i) {
file.writeIdentifier(SI_Resource, available[i].id);
file << costs[i];
file << exploited[i];
file << nativeIds[i];
}
file << resourceLimit;
file << limitMod;
file << currentResources;
}
void postInit(Asteroid& obj) {
obj.setImportEnabled(false);
obj.setResourceLevel(4);
obj.sightRange = 0;
obj.modCargoStorage(+INFINITY);
}
void destroy(Asteroid& obj) {
obj.destroyObjResources();
if(obj.region !is null)
obj.region.removeStrategicIcon(-1, icon);
icon.markForDeletion();
@icon = null;
if(baseNode !is null) {
baseNode.markForDeletion();
@baseNode = null;
}
if(obj.owner !is null && obj.owner.valid)
obj.owner.unregisterAsteroid(obj);
leaveRegion(obj);
}
bool onOwnerChange(Asteroid& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
if(prevOwner !is null && prevOwner.valid)
prevOwner.unregisterAsteroid(obj);
if(obj.owner !is null && obj.owner.valid)
obj.owner.registerAsteroid(obj);
obj.changeResourceOwner(prevOwner);
bool hasBase = obj.owner !is null && obj.owner.valid;
obj.HasBase = hasBase ? 1.f : 0.f;
if(hasBase && baseNode is null) {
@baseNode = MeshNode(model::MiningBase, material::GenericPBR_MiningBase);
nodeSyncObject(baseNode, obj);
}
else if(baseNode !is null && !hasBase) {
baseNode.markForDeletion();
@baseNode = null;
}
return false;
}
void initMesh(Asteroid& obj) {
makeMesh(obj);
}
void makeMesh(Asteroid& obj) {
MeshDesc mesh;
switch(obj.id % 4) {
case 0:
@mesh.model = model::Asteroid1; break;
case 1:
@mesh.model = model::Asteroid2; break;
case 2:
@mesh.model = model::Asteroid3; break;
case 3:
@mesh.model = model::Asteroid4; break;
}
switch(obj.id % 3) {
case 0:
@mesh.material = material::AsteroidPegmatite; break;
case 1:
@mesh.material = material::AsteroidMagnetite; break;
case 2:
@mesh.material = material::AsteroidTonalite; break;
}
mesh.memorable = true;
@icon = StrategicIconNode();
if(obj.cargoTypes != 0)
icon.establish(obj, 0.015, spritesheet::OreAsteroidIcon, 0);
else
icon.establish(obj, 0.015, spritesheet::AsteroidIcon, 0);
icon.memorable = true;
bindMesh(obj, mesh);
if(obj.region !is null) {
if(!obj.region.initialized)
@obj.region = null;
else
obj.region.addStrategicIcon(-1, obj, icon);
if(obj.region !is null) {
Node@ node = obj.getNode();
node.hintParentObject(obj.region, false);
}
}
bool hasBase = obj.owner !is null && obj.owner.valid;
if(hasBase && baseNode is null) {
@baseNode = MeshNode(model::MiningBase, material::GenericPBR_MiningBase);
nodeSyncObject(baseNode, obj);
}
}
float timer = 0.f;
void occasional_tick(Asteroid& obj) {
Region@ region = obj.region;
bool engaged = obj.engaged;
obj.inCombat = engaged;
obj.engaged = false;
if(engaged && region !is null)
region.EngagedMask |= obj.owner.mask;
}
double tick(Asteroid& obj, double time) {
Region@ prevRegion = obj.region;
if(updateRegion(obj)) {
Region@ newRegion = obj.region;
if(prevRegion !is null)
prevRegion.removeStrategicIcon(-1, icon);
if(newRegion !is null)
newRegion.addStrategicIcon(-1, obj, icon);
@prevRegion = newRegion;
Node@ node = obj.getNode();
if(node !is null)
node.hintParentObject(newRegion, false);
}
if(prevRegion is null && isOutsideUniverseExtents(obj.position))
limitToUniverseExtents(obj.position);
icon.visible = obj.isVisibleTo(playerEmpire);
obj.orbitTick(time);
obj.resourceTick(time);
//Tick occasional stuff
timer -= float(time);
if(timer <= 0.f) {
occasional_tick(obj);
timer = 1.f;
}
//Asteroids lose ownership if not in an owned or neutral system
if(obj.owner.valid) {
if(prevRegion !is null && prevRegion.PlanetsMask != 0 && prevRegion.PlanetsMask & obj.owner.mask == 0) {
if(!hasTradeAdjacent(obj.owner, prevRegion)) {
clearSetup(obj);
return 0.2;
}
}
}
//Asteroids are destroyed when they run out of cargo or resources
if(obj.cargoTypes == 0 && obj.nativeResourceCount == 0)
obj.destroy();
return 0.2;
}
vec3d get_strategicIconPosition(Asteroid& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
bool canDevelop(Asteroid& obj, Empire@ emp) {
return (!obj.owner.valid || obj.owner is emp) && currentResources < resourceLimit;
}
bool canGainLimit(Asteroid& obj, Empire@ emp) {
if(!obj.owner.valid || obj.owner !is emp)
return false;
return resourceLimit < available.length;
}
void addAvailable(Asteroid& obj, uint resource, double cost) {
const ResourceType@ type = getResource(resource);
if(type is null)
return;
available.insertLast(type);
costs.insertLast(cost);
exploited.insertLast(false);
int id = obj.addResource(type.id);
obj.setResourceDisabled(id, true);
nativeIds.insertLast(id);
delta = true;
}
uint getAvailableCount() {
if(currentResources >= resourceLimit)
return 0;
return available.length;
}
uint getAvailable(uint index) {
if(index >= available.length)
return uint(-1);
if(exploited[index])
return uint(-1);
return available[index].id;
}
double getAvailableCost(uint index) {
if(index >= costs.length)
return -1.0;
if(exploited[index])
return -1.0;
return costs[index];
}
double getAvailableCostFor(uint resId) {
const ResourceType@ type = getResource(resId);
if(type is null)
return -1.0;
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(exploited[i])
continue;
if(available[i] is type)
return costs[i];
}
return -1.0;
}
void setup(Asteroid& obj, Object@ origin, Empire@ emp, uint resource) {
if(obj.owner.valid && emp !is obj.owner)
return;
if(currentResources >= resourceLimit)
return;
const ResourceType@ type = getResource(resource);
if(type is null)
return;
bool found = false;
uint foundIndex = 0;
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(exploited[i])
continue;
if(available[i] is type) {
found = true;
foundIndex = i;
break;
}
}
if(!found)
return;
Object@ queued;
exploited[foundIndex] = true;
obj.setResourceDisabled(nativeIds[foundIndex], false);
if(!obj.owner.valid) {
@obj.owner = emp;
@obj.origin = origin;
obj.name = type.name+" "+locale::ASTEROID;
emp.modAttribute(EA_MiningBasesBuilt, AC_Add, 1);
}
//Remove all the fake resources and remember the queue
currentResources += 1;
if(currentResources >= resourceLimit) {
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(exploited[i])
continue;
obj.removeResource(nativeIds[i]);
nativeIds[i] = -1;
}
}
delta = true;
}
void clearSetup(Asteroid& obj) {
if(currentResources == 0)
return;
delta = true;
currentResources = 0;
Object@ queued = obj.nativeResourceDestination[0];
uint qtype = obj.nativeResourceType[0];
for(uint i = 0, cnt = obj.nativeResourceCount; i < cnt; ++i)
obj.removeResource(obj.nativeResourceId[i]);
Empire@ prevOwner = obj.owner;
@obj.owner = defaultEmpire;
@obj.origin = null;
obj.name = locale::ASTEROID;
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
exploited[i] = false;
if(costs[i] <= 0)
costs[i] = available[i].asteroidCost;
int id = obj.addResource(available[i].id);
obj.setResourceDisabled(id, true);
nativeIds[i] = id;
if(queued !is null && qtype == available[i].id && prevOwner.valid)
obj.exportResource(prevOwner, id, queued);
}
}
void checkLimit(Asteroid& obj, uint prevLimit) {
bool wasLimited = currentResources >= prevLimit;
bool nowLimited = currentResources >= resourceLimit;
if(wasLimited) {
if(!nowLimited) {
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(exploited[i]) {
obj.setResourceDisabled(nativeIds[i], false);
}
else {
nativeIds[i] = obj.addResource(available[i].id);
obj.setResourceDisabled(nativeIds[i], true);
}
}
}
}
else {
if(nowLimited) {
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(exploited[i])
continue;
obj.removeResource(nativeIds[i]);
nativeIds[i] = -1;
}
}
}
if(nowLimited) {
uint requireDisable = currentResources - resourceLimit;
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(!exploited[i])
continue;
Object@ dest = obj.getNativeResourceDestinationByID(obj.owner, nativeIds[i]);
if(dest !is null) {
obj.setResourceDisabled(nativeIds[i], false);
continue;
}
if(requireDisable > 0) {
obj.setResourceDisabled(nativeIds[i], true);
requireDisable -= 1;
}
else {
obj.setResourceDisabled(nativeIds[i], false);
}
}
for(uint i = 0, cnt = available.length; i < cnt && requireDisable > 0; ++i) {
if(!exploited[i])
continue;
Object@ dest = obj.getNativeResourceDestinationByID(obj.owner, nativeIds[i]);
if(dest is null)
continue;
obj.setResourceDisabled(nativeIds[i], true);
requireDisable -= 1;
}
}
}
void morphTo(Asteroid& obj, uint resId, double cost) {
auto@ resource = getResource(resId);
if(resource is null)
return;
for(uint i = 0, cnt = available.length; i < cnt; ++i) {
if(nativeIds[i] != -1)
obj.removeResource(nativeIds[i]);
nativeIds[i] = -1;
}
currentResources = 0;
available.length = 0;
exploited.length = 0;
nativeIds.length = 0;
costs.length = 0;
addAvailable(obj, resource.id, cost);
if(obj.owner !is null && obj.owner.valid)
setup(obj, obj.origin, obj.owner, resource.id);
}
void setResourceLimit(Asteroid& obj, uint newLimit) {
if(newLimit + limitMod == resourceLimit)
return;
uint prevLimit = resourceLimit;
resourceLimit = newLimit + limitMod;
checkLimit(obj, prevLimit);
delta = true;
}
void modResourceLimitMod(Asteroid& obj, int mod) {
limitMod += mod;
uint prevLimit = resourceLimit;
resourceLimit += mod;
checkLimit(obj, prevLimit);
delta = true;
}
void _writeAsteroid(const Asteroid& obj, Message& msg) {
msg << obj.origin;
uint cnt = available.length;
msg.writeSmall(cnt);
for(uint i = 0; i < cnt; ++i) {
msg.writeLimited(available[i].id, getResourceCount()-1);
msg << costs[i];
msg << exploited[i];
}
msg.writeSmall(resourceLimit);
msg.writeSmall(currentResources);
}
void syncInitial(const Asteroid& obj, Message& msg) {
_writeAsteroid(obj, msg);
obj.writeResources(msg);
obj.writeCargo(msg);
obj.writeOrbit(msg);
}
bool syncDelta(const Asteroid& obj, Message& msg) {
bool used = false;
if(delta) {
used = true;
delta = false;
msg.write1();
_writeAsteroid(obj, msg);
}
else
msg.write0();
if(obj.writeResourceDelta(msg))
used = true;
else
msg.write0();
if(obj.writeCargoDelta(msg))
used = true;
else
msg.write0();
if(obj.writeOrbitDelta(msg))
used = true;
else
msg.write0();
return used;
}
void syncDetailed(const Asteroid& obj, Message& msg) {
_writeAsteroid(obj, msg);
obj.writeResources(msg);
obj.writeCargo(msg);
obj.writeOrbit(msg);
}
};
+558
View File
@@ -0,0 +1,558 @@
import regions.regions;
import saving;
import systems;
import resources;
import civilians;
import statuses;
const double ACC_SYSTEM = 2.0;
const double ACC_INTERSYSTEM = 65.0;
const int GOODS_WORTH = 8;
const double CIV_HEALTH = 25.0;
const double CIV_REPAIR = 1.0;
const double BLOCKADE_TIMER = 3.0 * 60.0;
const double DEST_RANGE = 4.0;
tidy class CivilianScript {
uint type = 0;
Object@ origin;
Object@ pathTarget;
Object@ intermediate;
Region@ prevRegion;
Region@ nextRegion;
int moveId = -1;
bool leavingRegion = false, awaitingIntermediate = false;
bool pickedUp = false;
double Health = CIV_HEALTH;
int stepCount = 0;
int income = 0;
bool delta = false;
uint cargoType = CT_Goods;
const ResourceType@ cargoResource;
int cargoWorth = 0;
double get_health() {
return Health;
}
double get_maxHealth(const Civilian& obj) {
return CIV_HEALTH * obj.radius * obj.owner.ModHP.value;
}
void load(Civilian& obj, SaveFile& msg) {
loadObjectStates(obj, msg);
if(msg.readBit()) {
obj.activateMover();
msg >> cast<Savable>(obj.Mover);
}
msg >> type;
msg >> origin;
msg >> pathTarget;
msg >> intermediate;
msg >> prevRegion;
msg >> nextRegion;
msg >> moveId;
msg >> leavingRegion;
msg >> pickedUp;
msg >> Health;
msg >> stepCount;
msg >> income;
msg >> cargoType;
if(msg.readBit())
@cargoResource = getResource(msg.readIdentifier(SI_Resource));
msg >> cargoWorth;
makeMesh(obj);
}
void save(Civilian& obj, SaveFile& msg) {
saveObjectStates(obj, msg);
if(obj.hasMover) {
msg.write1();
msg << cast<Savable>(obj.Mover);
}
else {
msg.write0();
}
msg << type;
msg << origin;
msg << pathTarget;
msg << intermediate;
msg << prevRegion;
msg << nextRegion;
msg << moveId;
msg << leavingRegion;
msg << pickedUp;
msg << Health;
msg << stepCount;
msg << income;
msg << cargoType;
if(cargoResource is null) {
msg.write0();
}
else {
msg.write1();
msg.writeIdentifier(SI_Resource, cargoResource.id);
}
msg << cargoWorth;
}
uint getCargoType() {
if(cargoType == CT_Resource && !pickedUp)
return CT_Goods;
return cargoType;
}
uint getCargoResource() {
if(cargoResource is null)
return uint(-1);
return cargoResource.id;
}
int getCargoWorth() {
return cargoWorth;
}
void setCargoType(Civilian& obj, uint type) {
cargoType = type;
@cargoResource = null;
if(type == CT_Goods)
cargoWorth = GOODS_WORTH * obj.radius * CIV_RADIUS_WORTH;
delta = true;
}
void setCargoResource(Civilian& obj, uint id) {
cargoType = CT_Resource;
@cargoResource = getResource(id);
if(pickedUp)
cargoWorth = cargoResource.cargoWorth * obj.radius * CIV_RADIUS_WORTH;
else
cargoWorth = GOODS_WORTH * obj.radius * CIV_RADIUS_WORTH;
delta = true;
}
void modCargoWorth(int diff) {
cargoWorth += diff;
delta = true;
}
int getStepCount() {
return stepCount;
}
void modStepCount(int mod) {
stepCount += mod;
}
void resetStepCount() {
stepCount = 0;
}
void init(Civilian& obj) {
obj.sightRange = 0;
}
uint getCivilianType() {
return type;
}
void setCivilianType(uint type) {
this.type = type;
}
void modIncome(Civilian& obj, int mod) {
if(obj.owner !is null && obj.owner.valid)
obj.owner.modTotalBudget(+mod, MoT_Trade);
income += mod;
}
void postInit(Civilian& obj) {
if(type == CiT_Freighter && obj.owner !is null)
obj.owner.CivilianTradeShips += 1;
if(type == CiT_Freighter) {
obj.activateMover();
obj.maxAcceleration = ACC_SYSTEM;
obj.rotationSpeed = 1.0;
}
makeMesh(obj);
Health = get_maxHealth(obj);
delta = true;
}
void makeMesh(Civilian& obj) {
MeshDesc mesh;
@mesh.model = getCivilianModel(obj.owner, type, obj.radius);
@mesh.material = getCivilianMaterial(obj.owner, type, obj.radius);
@mesh.iconSheet = getCivilianIcon(obj.owner, type, obj.radius).sheet;
mesh.iconIndex = getCivilianIcon(obj.owner, type, obj.radius).index;
bindMesh(obj, mesh);
}
bool onOwnerChange(Civilian& obj, Empire@ prevOwner) {
if(income != 0 && prevOwner !is null && prevOwner.valid)
prevOwner.modTotalBudget(-income, MoT_Trade);
if(type == CiT_Freighter && prevOwner !is null)
prevOwner.CivilianTradeShips -= 1;
regionOwnerChange(obj, prevOwner);
if(type == CiT_Freighter && obj.owner !is null)
obj.owner.CivilianTradeShips += 1;
if(income != 0 && prevOwner !is null && obj.owner.valid)
obj.owner.modTotalBudget(-income, MoT_Trade);
return false;
}
void destroy(Civilian& obj) {
if((obj.inCombat || obj.engaged) && !game_ending) {
playParticleSystem("ShipExplosion", obj.position, obj.rotation, obj.radius, obj.visibleMask);
}
else {
if(cargoResource !is null) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDestroy(obj, origin, pathTarget, null);
}
}
if(origin !is null && origin.hasResources)
origin.setAssignedCivilian(null);
if(pathTarget !is null && pathTarget.isPlanet && pathTarget.owner is obj.owner) {
auto@ status = getStatusType("Blockaded");
if(status !is null)
pathTarget.addStatus(status.id, timer=BLOCKADE_TIMER);
}
leaveRegion(obj);
if(obj.owner !is null && obj.owner.valid) {
if(type == CiT_Freighter)
obj.owner.CivilianTradeShips -= 1;
if(income != 0)
obj.owner.modTotalBudget(-income, MoT_Trade);
}
}
void freeCivilian(Civilian& obj) {
if(origin !is null && origin.hasResources)
origin.setAssignedCivilian(null);
Region@ region = obj.region;
if(region !is null) {
@origin = null;
@pathTarget = null;
@prevRegion = null;
@nextRegion = null;
region.freeUpCivilian(obj);
}
else {
@origin = null;
@pathTarget = null;
@prevRegion = null;
@nextRegion = null;
obj.destroy();
}
}
float timer = 0.f;
void occasional_tick(Civilian& obj) {
//Update in combat flags
bool engaged = obj.engaged;
obj.inCombat = engaged;
obj.engaged = false;
if(engaged && obj.region !is null)
obj.region.EngagedMask |= obj.owner.mask;
}
void gotoTradeStation(Civilian@ station) {
if(!awaitingIntermediate)
return;
awaitingIntermediate = false;
@intermediate = station;
}
void gotoTradePlanet(Planet@ planet) {
if(!awaitingIntermediate)
return;
awaitingIntermediate = false;
@intermediate = planet;
}
double tick(Civilian& obj, double time) {
//Update normal stuff
updateRegion(obj);
if(obj.hasMover)
obj.moverTick(time);
//Tick occasional stuff
timer -= float(time);
if(timer <= 0.f) {
occasional_tick(obj);
timer = 1.f;
}
//Do repair
double maxHP = get_maxHealth(obj);
if(!obj.inCombat && Health < maxHP) {
Health = min(Health + (CIV_REPAIR * time * obj.radius), maxHP);
delta = true;
}
if(awaitingIntermediate)
return 0.25;
//Update pathing
Region@ curRegion = obj.region;
if(pathTarget !is null) {
if(origin !is null && !pickedUp) {
if(obj.moveTo(origin, moveId, distance=10.0, enterOrbit=false)) {
pickedUp = true;
if(cargoResource !is null)
cargoWorth = cargoResource.cargoWorth * obj.radius * CIV_RADIUS_WORTH;
delta = true;
moveId = -1;
return 0.5;
}
else {
return 0.2;
}
}
Region@ destRegion;
if(pathTarget.isRegion)
@destRegion = cast<Region>(pathTarget);
else
@destRegion = pathTarget.region;
if(nextRegion is null) {
if(curRegion is null)
@nextRegion = findNearestRegion(obj.position);
else
@nextRegion = curRegion;
}
if(nextRegion is null || destRegion is null) {
freeCivilian(obj);
return 0.4;
}
if(leavingRegion) {
vec3d enterDest;
if(nextRegion !is destRegion || destRegion is pathTarget || getSystem(prevRegion).isSpatialAdjacent(getSystem(nextRegion))) {
enterDest = nextRegion.position + (prevRegion.position - nextRegion.position).normalized(nextRegion.radius * 0.85);
enterDest += random3d(0.0, DEST_RANGE);
}
else {
enterDest = pathTarget.position + vec3d(0,0,pathTarget.radius+10.0);
}
obj.maxAcceleration = ACC_INTERSYSTEM;
if(!obj.moveTo(enterDest, moveId, enterOrbit=false))
return 0.2;
if(cargoType == CT_Resource)
prevRegion.bumpTradeCounter(obj.owner);
moveId = -1;
leavingRegion = false;
}
if(curRegion is null || (nextRegion !is null && nextRegion is curRegion)) {
if(nextRegion is destRegion) {
//Move to destination
obj.maxAcceleration = ACC_SYSTEM;
if(curRegion is pathTarget || obj.moveTo(pathTarget, moveId, distance=10.0, enterOrbit=false)) {
moveId = -1;
if(cargoType == CT_Resource)
destRegion.bumpTradeCounter(obj.owner);
if(cargoResource !is null && !pathTarget.isRegion) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDeliver(obj, origin, pathTarget);
}
freeCivilian(obj);
return 0.4;
}
else {
return 0.2;
}
}
else if(curRegion is null) {
//Move to closest region
vec3d pos = nextRegion.position + (nextRegion.position - obj.position).normalized(nextRegion.radius * 0.85);
obj.maxAcceleration = ACC_INTERSYSTEM;
if(obj.moveTo(pos, moveId, enterOrbit=false)) {
moveId = -1;
return 0.4;
}
else {
return 0.2;
}
}
else {
//Find the next region to path to
TradePath path(obj.owner);
path.generate(getSystem(curRegion), getSystem(destRegion));
if(path.pathSize < 2 || !path.valid) {
freeCivilian(obj);
return 0.4;
}
else {
@prevRegion = curRegion;
@nextRegion = path.pathNode[1].object;
awaitingIntermediate = true;
@intermediate = null;
if(curRegion.hasTradeStation(obj.owner))
curRegion.getTradeStation(obj, obj.owner, obj.position);
else if(cargoType == CT_Goods)
curRegion.getTradePlanet(obj, obj.owner);
else
awaitingIntermediate = false;
leavingRegion = false;
}
}
}
if(!leavingRegion) {
if(intermediate !is null) {
if(obj.moveTo(intermediate, moveId, distance=10.0, enterOrbit=false)) {
moveId = -1;
@intermediate = null;
return 0.4;
}
else {
return 0.2;
}
}
else {
if(moveId == -1 && !getSystem(prevRegion).isSpatialAdjacent(getSystem(nextRegion))) {
leavingRegion = true;
return 0.5;
}
vec3d leaveDest;
if(prevRegion is null)
leaveDest = obj.position;
else
leaveDest = prevRegion.position + (nextRegion.position - prevRegion.position).normalized(prevRegion.radius * 0.85) + random3d(0, DEST_RANGE);
obj.maxAcceleration = ACC_SYSTEM;
if(obj.moveTo(leaveDest, moveId, enterOrbit=false)) {
moveId = -1;
leavingRegion = true;
return 0.5;
}
}
}
}
return 0.2;
}
void setOrigin(Object@ origin) {
@this.origin = origin;
delta = true;
}
void pathTo(Civilian& obj, Object@ origin, Object@ target, Object@ stopAt = null) {
@pathTarget = target;
@prevRegion = null;
@nextRegion = null;
@intermediate = stopAt;
@this.origin = origin;
pickedUp = false;
delta = true;
leavingRegion = false;
}
void pathTo(Civilian& obj, Object@ target) {
@pathTarget = target;
@prevRegion = null;
@nextRegion = null;
@origin = null;
@intermediate = null;
pickedUp = true;
delta = true;
leavingRegion = false;
}
void damage(Civilian& obj, DamageEvent& evt, double position, const vec2d& direction) {
if(!obj.valid || obj.destroying)
return;
obj.engaged = true;
Health = max(0.0, Health - evt.damage);
delta = true;
if(Health <= 0.0) {
if(cargoWorth > 0) {
Empire@ other = evt.obj.owner;
if(other !is null && other.major) {
other.addBonusBudget(cargoWorth);
cargoWorth = 0;
}
}
if(cargoResource !is null) {
for(uint i = 0, cnt = cargoResource.hooks.length; i < cnt; ++i)
cargoResource.hooks[i].onTradeDestroy(obj, origin, pathTarget, evt.obj);
}
obj.destroy();
}
}
void _writeDelta(const Civilian& obj, Message& msg) {
msg.writeSmall(cargoType);
msg.writeSmall(cargoWorth);
msg.writeBit(pickedUp);
msg.writeFixed(obj.health/obj.maxHealth);
if(cargoResource !is null) {
msg.write1();
msg.writeLimited(cargoResource.id, getResourceCount()-1);
}
else {
msg.write0();
}
}
void syncInitial(const Civilian& obj, Message& msg) {
if(obj.hasMover) {
msg.write1();
obj.writeMover(msg);
}
else {
msg.write0();
}
msg << type;
_writeDelta(obj, msg);
}
void syncDetailed(const Civilian& obj, Message& msg) {
if(obj.hasMover) {
msg.write1();
obj.writeMover(msg);
}
else {
msg.write0();
}
_writeDelta(obj, msg);
}
bool syncDelta(const Civilian& obj, Message& msg) {
bool used = false;
if(obj.hasMover && obj.writeMoverDelta(msg))
used = true;
else
msg.write0();
if(delta) {
used = true;
delta = false;
msg.write1();
_writeDelta(obj, msg);
}
else {
msg.write0();
}
return used;
}
};
void dumpPlanetWaitTimes() {
uint cnt = playerEmpire.planetCount;
double avg = 0.0, maxTime = 0.0;
for(uint i = 0; i < cnt; ++i) {
Planet@ pl = playerEmpire.planetList[i];
if(pl !is null && pl.getNativeResourceDestination(playerEmpire, 0) !is null) {
double timer = pl.getCivilianTimer();
print(pl.name+" -- "+timer);
avg += timer;
if(timer > maxTime)
maxTime = timer;
}
}
avg /= double(cnt);
print(" AVERAGE: "+avg);
print(" MAX: "+maxTime);
}
+152
View File
@@ -0,0 +1,152 @@
from resources import MoneyType;
import regions.regions;
import saving;
tidy class ColonyShipScript {
int moveId;
ColonyShipScript() {
moveId = -1;
}
void load(ColonyShip& obj, SaveFile& msg) {
loadObjectStates(obj, msg);
msg >> cast<Savable>(obj.Mover);
@obj.Origin = msg.readObject();
@obj.Target = msg.readObject();
msg >> obj.CarriedPopulation;
if(msg < SV_0033 && obj.Target !is null)
obj.Target.modIncomingPop(obj.CarriedPopulation);
msg >> moveId;
}
void makeMesh(ColonyShip& obj) {
MeshDesc shipMesh;
const Shipset@ ss = obj.owner.shipset;
const ShipSkin@ skin;
if(ss !is null)
@skin = ss.getSkin("Colonizer");
if(obj.owner.ColonizerModel.length != 0) {
@shipMesh.model = getModel(obj.owner.ColonizerModel);
@shipMesh.material = getMaterial(obj.owner.ColonizerMaterial);
}
else if(skin !is null) {
@shipMesh.model = skin.model;
@shipMesh.material = skin.material;
}
else {
@shipMesh.model = model::ColonyShip;
@shipMesh.material = material::VolkurGenericPBR;
}
@shipMesh.iconSheet = spritesheet::HullIcons;
shipMesh.iconIndex = 0;
bindMesh(obj, shipMesh);
}
void postLoad(ColonyShip& obj) {
//Create the graphics
makeMesh(obj);
}
void save(ColonyShip& obj, SaveFile& msg) {
saveObjectStates(obj, msg);
msg << cast<Savable>(obj.Mover);
msg << obj.Origin;
msg << obj.Target;
msg << obj.CarriedPopulation;
msg << moveId;
}
void init(ColonyShip& ship) {
//Create the graphics
ship.sightRange = 0;
makeMesh(ship);
}
void postInit(ColonyShip& ship) {
if(ship.Target !is null)
ship.Target.modIncomingPop(ship.CarriedPopulation);
if(ship.owner !is null && ship.owner.valid)
ship.owner.modMaintenance(round(80.0 * ship.CarriedPopulation * ship.owner.ColonizerMaintFactor), MoT_Colonizers);
}
bool onOwnerChange(ColonyShip& ship, Empire@ prevOwner) {
if(prevOwner !is null && prevOwner.valid)
prevOwner.modMaintenance(-round(80.0 * ship.CarriedPopulation * prevOwner.ColonizerMaintFactor), MoT_Colonizers);
if(ship.owner !is null && ship.owner.valid)
ship.owner.modMaintenance(round(80.0 * ship.CarriedPopulation * ship.owner.ColonizerMaintFactor), MoT_Colonizers);
regionOwnerChange(ship, prevOwner);
return false;
}
void destroy(ColonyShip& ship) {
if(ship.owner !is null && ship.owner.valid && ship.CarriedPopulation > 0)
ship.owner.modMaintenance(-round(80.0 * ship.CarriedPopulation * ship.owner.ColonizerMaintFactor), MoT_Colonizers);
if(ship.CarriedPopulation > 0 && ship.Origin !is null && ship.Target !is null) {
if(ship.Origin.hasSurfaceComponent)
ship.Origin.reducePopInTransit(ship.Target, ship.CarriedPopulation);
ship.Target.modIncomingPop(-ship.CarriedPopulation);
}
leaveRegion(ship);
}
double tick(ColonyShip& ship, double time) {
Object@ target = ship.Target;
if(moveId == -1 && target is null)
return 0.2;
ship.moverTick(time);
updateRegion(ship);
if(target is null) {
ship.destroy();
}
else if(ship.position.distanceTo(target.position) <= (ship.radius + target.radius + 0.1) || ship.moveTo(target, moveId, enterOrbit=false)) {
ship.destroy();
if(target.isPlanet) {
target.colonyShipArrival(ship.owner, ship.CarriedPopulation);
if(ship.Origin !is null && ship.Origin.hasSurfaceComponent)
ship.Origin.reducePopInTransit(ship.Target, ship.CarriedPopulation);
if(ship.owner !is null && ship.owner.valid)
ship.owner.modMaintenance(-round(80.0 * ship.CarriedPopulation * ship.owner.ColonizerMaintFactor), MoT_Colonizers);
ship.CarriedPopulation = 0;
}
}
else if(target.isPlanet && ship.owner !is null && ship.owner.major && !target.isEmpireColonizing(ship.owner)) {
if(ship.Origin !is null && ship.Origin.hasSurfaceComponent && ship.position.distanceTo(ship.Origin.position) < 3000.0)
ship.destroy();
}
return 0.2;
}
void damage(ColonyShip& ship, DamageEvent& evt, double position, const vec2d& direction) {
ship.Health -= evt.damage;
if(ship.Health <= 0)
ship.destroy();
}
void syncInitial(const ColonyShip& ship, Message& msg) {
ship.writeMover(msg);
}
void syncDetailed(const ColonyShip& ship, Message& msg) {
ship.writeMover(msg);
}
bool syncDelta(const ColonyShip& ship, Message& msg) {
bool used = false;
if(ship.writeMoverDelta(msg))
used = true;
else
msg.write0();
return used;
}
};
+170
View File
@@ -0,0 +1,170 @@
from resources import MoneyType;
import regions.regions;
import saving;
import statuses;
import systems;
const int COLONY_SHIP_MAINTENANCE = 5;
tidy class FreighterScript {
int moveId = -1;
double idleTimer = 180.0;
FreighterScript() {
}
void makeMesh(Freighter& obj) {
MeshDesc shipMesh;
const Shipset@ ss = obj.owner.shipset;
const ShipSkin@ skin;
if(ss !is null)
@skin = ss.getSkin(obj.skin);
if(skin !is null) {
@shipMesh.model = skin.model;
@shipMesh.material = skin.material;
}
else {
@shipMesh.model = model::Fighter;
@shipMesh.material = material::Ship10;
}
@shipMesh.iconSheet = spritesheet::HullIcons;
shipMesh.iconIndex = 0;
bindMesh(obj, shipMesh);
}
void load(Freighter& obj, SaveFile& msg) {
loadObjectStates(obj, msg);
msg >> cast<Savable>(obj.Mover);
@obj.Origin = msg.readObject();
@obj.Target = msg.readObject();
msg >> moveId;
if(msg >= SV_0109) {
msg >> obj.StatusId;
msg >> obj.StatusDuration;
msg >> obj.SetOrigin;
msg >> obj.MinLevel;
}
if(msg >= SV_0110)
msg >> obj.VisitHostile;
if(msg >= SV_0145)
msg >> obj.skin;
//Create the graphics
makeMesh(obj);
}
void save(Freighter& obj, SaveFile& msg) {
saveObjectStates(obj, msg);
msg << cast<Savable>(obj.Mover);
msg << obj.Origin;
msg << obj.Target;
msg << moveId;
msg << obj.StatusId;
msg << obj.StatusDuration;
msg << obj.SetOrigin;
msg << obj.MinLevel;
msg << obj.VisitHostile;
msg << obj.skin;
}
void init(Freighter& ship) {
ship.maxAcceleration = 2.5;
}
void postInit(Freighter& ship) {
makeMesh(ship);
}
bool onOwnerChange(Freighter& ship, Empire@ prevOwner) {
regionOwnerChange(ship, prevOwner);
return false;
}
void destroy(Freighter& ship) {
leaveRegion(ship);
}
double tick(Freighter& ship, double time) {
Object@ target = ship.Target;
ship.moverTick(time);
updateRegion(ship);
if(target is null) {
idleTimer -= time;
if(ship.region !is null && idleTimer > 0.0) {
Region@ lookIn = ship.region;
bool local = randomi(0,1) == 0;
if(!local) {
SystemDesc@ desc = getSystem(lookIn.SystemId);
if(desc.adjacent.length > 0)
@lookIn = getSystem(desc.adjacent[randomi(0, desc.adjacent.length-1)]).object;
}
uint plCount = lookIn.planetCount;
if(plCount > 0) {
Planet@ targ = lookIn.planets[randomi(0, plCount-1)];
if(targ !is null && targ.owner.valid && (ship.VisitHostile || !ship.owner.isHostile(targ.owner)) && targ.level >= uint(ship.MinLevel)) {
@target = targ;
@ship.Target = targ;
ship.moveTo(target, moveId, enterOrbit=false);
}
}
return 0.2;
}
else {
ship.destroy();
return 0.2;
}
}
if(!target.owner.valid || (!ship.VisitHostile && ship.owner.isHostile(target.owner))) {
@target = null;
@ship.Target = null;
}
else if(ship.position.distanceTo(target.position) <= (ship.radius + target.radius + 0.1) || ship.moveTo(target, moveId, enterOrbit=false)) {
if(target.isPlanet) {
int status = ship.StatusId;
if(status == -1)
status = getStatusID("Happy");
Empire@ originEmp;
if(ship.SetOrigin)
@originEmp = ship.owner;
target.addStatus(status, ship.StatusDuration, originEmpire=originEmp);
}
ship.destroy();
}
return 0.2;
}
void damage(Freighter& ship, DamageEvent& evt, double position, const vec2d& direction) {
ship.Health -= evt.damage;
if(ship.Health <= 0)
ship.destroy();
}
void syncInitial(const Freighter& ship, Message& msg) {
msg << ship.skin;
ship.writeMover(msg);
}
void syncDetailed(const Freighter& ship, Message& msg) {
ship.writeMover(msg);
}
bool syncDelta(const Freighter& ship, Message& msg) {
bool used = false;
if(ship.writeMoverDelta(msg))
used = true;
else
msg.write0();
return used;
}
};
+355
View File
@@ -0,0 +1,355 @@
import regions.regions;
import saving;
import oddity_navigation;
import systems;
import oddities;
void createSlipstream(const vec3d& from, const vec3d& to, double timer = -1.0, Empire@ revealEmp = null) {
ObjectDesc desc;
desc.type = OT_Oddity;
desc.radius = 15.0;
desc.position = from;
desc.flags |= objNoPhysics;
desc.flags |= objNoDamage;
if(revealEmp !is null)
@desc.owner = revealEmp;
desc.name = locale::SLIPSTREAM_TEAR;
auto@ input = Oddity(desc);
input.noCollide = true;
desc.position = to;
auto@ output = Oddity(desc);
output.noCollide = true;
input.linkTo(output);
output.linkTo(input);
input.setGate(true);
output.setGate(true);
input.setTimer(timer);
output.setTimer(timer);
input.makeVisuals(Odd_Slipstream);
output.makeVisuals(Odd_Slipstream);
input.linkVision(true);
output.linkVision(true);
}
void createWormhole(SystemDesc@ from, SystemDesc@ to) {
vec3d fromPos = from.position;
vec2d offset = random2d(100.0, from.radius - 100.0);
fromPos.x += offset.x;
fromPos.z += offset.y;
vec3d toPos = to.position;
offset = random2d(100.0, to.radius - 100.0);
toPos.x += offset.x;
toPos.z += offset.y;
ObjectDesc desc;
desc.type = OT_Oddity;
desc.radius = 25.0;
desc.position = fromPos;
desc.flags |= objNoPhysics;
desc.flags |= objNoDamage;
desc.name = locale::WORMHOLE;
auto@ input = Oddity(desc);
input.noCollide = true;
desc.position = toPos;
auto@ output = Oddity(desc);
output.noCollide = true;
input.linkTo(output);
output.linkTo(input);
input.setGate(true);
output.setGate(true);
input.makeVisuals(Odd_Wormhole);
output.makeVisuals(Odd_Wormhole);
input.linkVision(true);
output.linkVision(true);
input.setSuperior(true);
output.setSuperior(true);
}
Oddity@ createNebula(const vec3d& position, double radius, uint color = 0xf0c87040, Region@ region = null) {
ObjectDesc desc;
desc.type = OT_Oddity;
desc.radius = radius;
desc.position = position;
desc.flags |= objNoPhysics;
desc.flags |= objNoDamage;
auto@ nebula = Oddity(desc);
if(region !is null) {
@nebula.region = region;
region.enterRegion(nebula);
}
nebula.noCollide = true;
nebula.makeVisuals(Odd_Nebula, color=color);
return nebula;
}
tidy class OddityScript {
StrategicIconNode@ icon;
bool gate = false;
bool superior = false;
double timer = -1.0;
uint visualType = uint(-1);
uint visualColor = 0xffffffff;
Object@ link;
bool visionLinked = false;
Region@ visionRegion;
uint visionMask = 0;
uint prevMemory = 0;
bool delta = false;
void save(Oddity& obj, SaveFile& file) {
saveObjectStates(obj, file);
file << gate;
file << timer;
file << visualType;
file << link;
file << visionLinked;
file << visionRegion;
file << visionMask;
file << superior;
file << prevMemory;
file << visualColor;
}
void load(Oddity& obj, SaveFile& file) {
loadObjectStates(obj, file);
file >> gate;
file >> timer;
file >> visualType;
file >> link;
file >> visionLinked;
file >> visionRegion;
file >> visionMask;
if(file >= SV_0020)
file >> superior;
if(file >= SV_0067)
file >> prevMemory;
if(file >= SV_0099)
file >> visualColor;
makeVisuals(obj, visualType, fromCreation=false, color=visualColor);
if(gate)
addOddityGate(obj);
}
uint getVisualType() {
return visualType;
}
uint getVisualColor() {
return visualColor;
}
bool isGate() {
return gate;
}
vec3d getGateDest() {
if(link !is null)
return link.position;
return vec3d();
}
Object@ getLink() {
return link;
}
double getTimer() {
return timer;
}
void setGate(Oddity& obj, bool value) {
if(value == gate)
return;
gate = value;
if(gate)
addOddityGate(obj);
else
removeOddityGate(obj);
delta = true;
}
void linkVision(bool value) {
visionLinked = value;
delta = true;
}
void linkTo(Oddity& obj, Object@ other) {
@link = other;
obj.rotation = quaterniond_fromVecToVec(vec3d_front(), (other.position - obj.position).normalized(), vec3d_up());
delta = true;
}
void setTimer(double value) {
timer = value;
delta = true;
}
void setSuperior(bool value) {
superior = value;
}
vec3d get_strategicIconPosition(Oddity& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
void makeVisuals(Oddity& obj, uint type, bool fromCreation = true, uint color = 0xffffffff) {
visualType = type;
visualColor = color;
@icon = makeOddityVisuals(obj, type, fromCreation, color=color);
}
void destroy(Oddity& obj) {
if(obj.region !is null)
obj.region.removeStrategicIcon(-1, icon);
if(icon !is null)
icon.markForDeletion();
leaveRegion(obj);
updateVision(visionRegion, visionMask, 0);
if(gate)
removeOddityGate(obj);
removeAmbientSource(CURRENT_PLAYER, obj.id);
}
void updateVision(Region@ onRegion, uint fromMask, uint toMask) {
if(onRegion is null)
return;
if(fromMask == toMask)
return;
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
Empire@ emp = getEmpire(i);
if(fromMask & emp.mask != 0) {
if(toMask & emp.mask == 0)
onRegion.revokeVision(emp);
}
else {
if(toMask & emp.mask != 0)
onRegion.grantVision(emp);
}
}
}
double tick(Oddity& obj, double time) {
//Handle region changes
Region@ prevRegion = obj.region;
if(updateRegion(obj)) {
Region@ newRegion = obj.region;
if(icon !is null) {
if(prevRegion !is null)
prevRegion.removeStrategicIcon(-1, icon);
if(newRegion !is null)
newRegion.addStrategicIcon(-1, obj, icon);
}
updateVision(visionRegion, visionMask, 0);
visionMask = 0;
@visionRegion = null;
@prevRegion = newRegion;
}
if(icon !is null)
icon.visible = obj.isVisibleTo(playerEmpire);
if(link !is null && !link.valid)
@link = null;
//Handle vision sharing
Region@ newRegion;
uint newMask = 0;
if(visionLinked && link !is null) {
@newRegion = link.region;
if(prevRegion is null)
obj.donatedVision |= link.visibleMask;
}
if(prevRegion !is null)
newMask = prevRegion.BasicVisionMask;
if(newRegion !is visionRegion) {
updateVision(visionRegion, visionMask, 0);
updateVision(newRegion, 0, newMask);
@visionRegion = newRegion;
visionMask = newMask;
}
else {
updateVision(visionRegion, visionMask, newMask);
visionMask = newMask;
}
//Handle memories
if(obj.memoryMask != prevMemory) {
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
auto@ emp = getEmpire(i);
if((prevMemory & emp.mask) != (obj.memoryMask & emp.mask))
emp.PathId += 1;
}
prevMemory = obj.memoryMask;
}
//Kill the oddity gate if the region blocks ftl at all
if(gate && !superior && prevRegion !is null) {
if(prevRegion.BlockFTLMask.value & obj.owner.mask != 0) {
if(link !is null)
link.destroy();
obj.destroy();
}
}
//Handle timer
if(timer >= 0.0) {
timer -= time;
if(timer <= 0.0)
obj.destroy();
}
return 0.25;
}
void _write(const Oddity& obj, Message& msg) {
msg << gate;
msg << timer;
msg << visualType;
msg << visualColor;
msg << link;
}
void syncInitial(const Oddity& obj, Message& msg) {
_write(obj, msg);
}
bool syncDelta(const Oddity& obj, Message& msg) {
bool used = false;
if(delta) {
used = true;
delta = false;
msg.write1();
_write(obj, msg);
}
else
msg.write0();
return used;
}
void syncDetailed(const Oddity& obj, Message& msg) {
_write(obj, msg);
}
};
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
import pickups;
import camps;
import saving;
import regions.regions;
import attributes;
tidy class PickupScript {
void postInit(Pickup& obj) {
obj.initPickup();
}
void syncInitial(const Pickup& obj, Message& msg) {
msg << obj.PickupType;
obj.writePickup(msg);
}
bool syncDelta(const Pickup& obj, Message& msg) {
if(obj.claimPickupDelta()) {
obj.writePickup(msg);
return true;
}
return false;
}
void destroy(Pickup& obj) {
leaveRegion(obj);
}
void load(Pickup& obj, SaveFile& msg) {
cast<PickupControl>(obj.PickupControl).load(obj, msg);
}
void postLoad(Pickup& obj) {
cast<PickupControl>(obj.PickupControl).postLoad(obj);
}
void save(Pickup& obj, SaveFile& msg) {
cast<PickupControl>(obj.PickupControl).save(obj, msg);
}
double tick(Pickup& obj, double time) {
updateRegion(obj);
obj.tickPickup(time);
return 0.5;
}
};
tidy class PickupControl : Component_PickupControl {
const PickupType@ type;
Object@[] protectors;
vec3d offset;
bool pickupDelta = false;
const CampType@ campType;
PickupControl() {
}
void generateMesh(Object& obj) {
MeshDesc mesh;
@mesh.model = type.model;
@mesh.material = type.material;
@mesh.iconSheet = type.iconSheet;
mesh.iconIndex = type.iconIndex;
mesh.memorable = true;
bindMesh(obj, mesh);
Node@ node = obj.getNode();
node.customColor = true;
node.color = Color(0x998888ff);
}
void setCampType(uint id) {
@campType = getCreepCamp(id);
}
void load(Pickup& obj, SaveFile& msg) {
uint tid = msg.readIdentifier(SI_Pickup);
@type = getPickupType(tid);
uint cnt = 0;
msg >> cnt;
protectors.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> protectors[i];
obj.PickupType = type.id;
generateMesh(obj);
if(msg >= SV_0095) {
if(msg.readBit())
@campType = getCreepCamp(msg.readIdentifier(SI_CreepCamp));
}
}
void postLoad(Pickup& obj) {
if(protectors.length != 0)
offset = protectors[0].position - obj.position;
}
void save(Pickup& obj, SaveFile& msg) {
msg.writeIdentifier(SI_Pickup, type.id);
uint cnt = protectors.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << protectors[i];
msg.writeBit(campType !is null);
if(campType !is null)
msg.writeIdentifier(SI_CreepCamp, campType.id);
}
void initPickup(Object& obj) {
Pickup@ pickup = cast<Pickup>(obj);
@type = getPickupType(pickup.PickupType);
pickup.name = type.getName(pickup);
pickup.radius = type.physicalSize;
generateMesh(obj);
}
bool claimPickupDelta() {
if(pickupDelta) {
pickupDelta = false;
return true;
}
return false;
}
bool get_isPickupProtected() {
return protectors.length != 0;
}
void addPickupProtector(Object& obj, Object& protector) {
protectors.insertLast(protector);
if(protectors.length == 1)
offset = protectors[0].position - obj.position;
}
Object@ getProtector() {
if(protectors.length == 0)
return null;
return protectors[0];
}
void tickPickup(Object& pickup, double time) {
bool wasProtected = protectors.length != 0;
if(protectors.length != 0) {
auto@ prot = protectors[0];
pickup.position = prot.position + offset;
pickup.velocity = prot.velocity;
pickup.acceleration = prot.acceleration;
}
else {
pickup.velocity = vec3d();
pickup.acceleration = vec3d();
}
Object@ lastHit;
for(uint i = 0, cnt = protectors.length; i < cnt; ++i) {
Object@ prot = protectors[i];
if(!prot.valid || prot.owner.major) {
protectors.removeAt(i);
pickupDelta = true;
--i; --cnt;
if(cnt == 0) {
if(prot.isShip)
@lastHit = getObjectByID(cast<Ship>(prot).lastHit);
}
}
}
if(wasProtected && protectors.length == 0) {
if(gameTime <= 5.0) {
pickup.destroy();
return;
}
if(lastHit !is null) {
Empire@ hitEmp = lastHit.owner;
if(hitEmp !is null)
hitEmp.modAttribute(EA_RemnantsCleared, AC_Add, 1.0);
if(lastHit.hasSupportAI)
@lastHit = cast<Ship>(lastHit).Leader;
if(lastHit !is null)
type.onClear(cast<Pickup>(pickup), lastHit);
}
Region@ reg = pickup.region;
if(campType !is null && reg !is null) {
for(uint i = 0, cnt = campType.region_statuses.length; i < cnt; ++i)
reg.removeRegionStatus(null, campType.region_statuses[i].id);
}
}
}
void pickupPickup(Object& obj, Object& by) {
Pickup@ pickup = cast<Pickup>(obj);
if(!type.canPickup(pickup, by))
return;
if(isPickupProtected)
return;
type.onPickup(pickup, by);
obj.destroy();
}
void writePickup(Message& msg) {
uint cnt = protectors.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << protectors[i];
}
};
+567
View File
@@ -0,0 +1,567 @@
import object_creation;
import regions.regions;
import planet_types;
import saving;
import util.target_search;
import cargo;
from objects.Asteroid import createAsteroid;
tidy class MoonData {
uint style = 0;
float size = 0.f;
};
tidy class PlanetScript {
float timer = 0.f;
float bonusSupply = 0.0;
float bonusPop = 0.0;
bool hpDelta = false;
uint ringStyle = 0;
array<MoonData@>@ moons;
void init(Planet& planet) {
timer = -float(uint8(planet.id)) / 255.0;
planet.owner.recordStatDelta(stat::Planets, 1);
planet.canBuildShips = true;
planet.canBuildOrbitals = true;
planet.leaderInit();
}
void postInit(Planet& planet) {
planet.modCargoStorage(+INFINITY);
if(planet.owner !is null) {
planet.owner.TotalPlanets += 1;
planet.owner.registerPlanet(planet);
}
}
void save(Planet& planet, SaveFile& file) {
saveObjectStates(planet, file);
file << cast<Savable>(planet.Orbit);
file << cast<Savable>(planet.Construction);
file << cast<Savable>(planet.SurfaceComponent);
file << cast<Savable>(planet.Resources);
file << cast<Savable>(planet.LeaderAI);
file << cast<Savable>(planet.Statuses);
file << cast<Savable>(planet.Cargo);
file << planet.ResearchRate;
file << planet.OrbitSize;
file << planet.Population;
file << planet.renamed;
file << bonusSupply << bonusPop;
file << planet.Health << planet.MaxHealth;
file << ringStyle;
file.writeIdentifier(SI_PlanetType, planet.PlanetType);
file << planet.hasAbilities;
if(planet.hasAbilities)
file << cast<Savable>(planet.Abilities);
if(moons !is null && moons.length != 0) {
file.write1();
file << moons.length;
for(uint i = 0, cnt = moons.length; i < cnt; ++i) {
file << moons[i].size;
file << moons[i].style;
}
}
else {
file.write0();
}
if(planet.hasMover) {
file.write1();
file << cast<Savable>(planet.Mover);
}
else {
file.write0();
}
}
void load(Planet& planet, SaveFile& file) {
timer = -float(uint8(planet.id)) / 255.0;
loadObjectStates(planet, file);
file >> cast<Savable>(planet.Orbit);
file >> cast<Savable>(planet.Construction);
file >> cast<Savable>(planet.SurfaceComponent);
file >> cast<Savable>(planet.Resources);
file >> cast<Savable>(planet.LeaderAI);
file >> cast<Savable>(planet.Statuses);
if(file >= SV_0122)
file >> cast<Savable>(planet.Cargo);
else
planet.modCargoStorage(+INFINITY);
file >> planet.ResearchRate;
file >> planet.OrbitSize;
file >> planet.Population;
file >> planet.renamed;
file >> bonusSupply >> bonusPop;
if(file >= SV_0083) {
if(file >= SV_0102) {
file >> planet.Health >> planet.MaxHealth;
}
else {
float hp = 0, maxhp = 0;
file >> hp >> maxhp;
planet.Health = hp;
planet.MaxHealth = maxhp;
}
}
if(file >= SV_0086)
file >> ringStyle;
planet.PlanetType = file.readIdentifier(SI_PlanetType);
if(planet.PlanetType == -1)
planet.PlanetType = 0;
bool hasAbl = false;
file >> hasAbl;
if(hasAbl) {
planet.activateAbilities();
file >> cast<Savable>(planet.Abilities);
}
PlanetNode@ plNode = cast<PlanetNode>(bindNode(planet, "PlanetNode"));
plNode.establish(planet);
plNode.planetType = planet.PlanetType;
plNode.colonized = planet.owner.valid;
plNode.flags = planet.planetGraphicsFlags;
if(ringStyle != 0)
plNode.addRing(ringStyle);
if(file >= SV_0110) {
if(file.readBit()) {
if(moons is null)
@moons = array<MoonData@>();
uint cnt = 0;
file >> cnt;
moons.length = cnt;
for(uint i = 0; i < cnt; ++i) {
MoonData dat;
file >> dat.size;
file >> dat.style;
@moons[i] = dat;
if(plNode !is null)
plNode.addMoon(dat.size, dat.style);
}
}
}
if(file >= SV_0133) {
if(file.readBit()) {
planet.activateMover();
file >> cast<Savable>(planet.Mover);
}
}
}
void postLoad(Planet& planet) {
planet.resourcesPostLoad();
planet.surfacePostLoad();
planet.leaderPostLoad();
Node@ node = planet.getNode();
if(node !is null)
node.hintParentObject(planet.region, false);
}
bool quietDestruction = false;
void destroyQuiet(Planet& planet) {
quietDestruction = true;
planet.destroy();
}
void destroy(Planet& planet) {
if(!game_ending && !quietDestruction) {
playParticleSystem("PlanetExplosion", planet.position, planet.rotation, planet.radius, planet.visibleMask);
double totChance = config::ASTEROID_OCCURANCE + config::RESOURCE_ASTEROID_OCCURANCE;
double resChance = config::RESOURCE_ASTEROID_OCCURANCE;
if(totChance > 0) {
for(uint i = 0, cnt = randomi(0,4); i < cnt; ++i) {
vec3d pos = planet.position;
pos += random3d(80 + planet.radius);
Asteroid@ roid = createAsteroid(pos);
Region@ reg = planet.region;
if(reg !is null) {
roid.orbitAround(reg.position);
roid.orbitSpin(randomd(20.0, 60.0));
}
double roll = randomd(0, totChance);
if(roll >= resChance) {
auto@ cargo = getCargoType("Ore");
if(cargo !is null)
roid.addCargo(cargo.id, randomd(500, 5000));
}
else {
do {
const ResourceType@ type = getDistributedAsteroidResource();
roid.addAvailable(type.id, type.asteroidCost);
}
while(randomd() < 0.4);
}
}
}
}
planet.destroyConstruction();
planet.destroyObjResources();
planet.destroySurface();
planet.leaderDestroy();
planet.destroyStatus();
leaveRegion(planet);
if(planet.owner !is null) {
planet.owner.recordStatDelta(stat::Planets, -1);
planet.owner.TotalPlanets -= 1;
planet.owner.unregisterPlanet(planet);
}
}
bool onOwnerChange(Planet& planet, Empire@ prevOwner) {
if(prevOwner !is null) {
prevOwner.recordStatDelta(stat::Planets, -1);
prevOwner.TotalPlanets -= 1;
prevOwner.unregisterPlanet(planet);
}
if(planet.owner !is null) {
planet.owner.recordStatDelta(stat::Planets, 1);
planet.owner.TotalPlanets += 1;
planet.owner.registerPlanet(planet);
}
planet.clearRally();
if(planet.hasAbilities)
planet.abilityOwnerChange(prevOwner, planet.owner);
planet.changeSurfaceOwner(prevOwner);
planet.changeResourceOwner(prevOwner);
planet.changeStatusOwner(prevOwner, planet.owner);
regionOwnerChange(planet, prevOwner);
planet.leaderChangeOwner(prevOwner, planet.owner);
if(!planet.hasMover && planet.owner.hasFlingBeacons) {
planet.activateMover();
planet.maxAcceleration = 0;
}
auto@ node = cast<PlanetNode>(planet.getNode());
if(node !is null && planet.owner !is null)
node.colonized = planet.owner.valid;
return false;
}
void occasional_tick(Planet& obj) {
Region@ region = obj.region;
bool engaged = obj.engaged;
obj.inCombat = engaged;
obj.engaged = false;
if(engaged && region !is null)
region.EngagedMask |= obj.owner.mask;
float newSupply = obj.owner.PlanetSupplyMod + obj.owner.PlanetLevelSupport * float(obj.level);
if(newSupply != bonusSupply) {
obj.modSupplyCapacity(newSupply - bonusSupply);
bonusSupply = newSupply;
}
float newPop = 0.f;
uint lev = obj.level;
if(lev == 2)
newPop += obj.owner.PopulationLevel2Mod;
else if(lev >= 3)
newPop += obj.owner.PopulationLevel3Mod;
if(int(newPop) != int(bonusPop)) {
obj.modMaxPopulation(int(newPop) - int(bonusPop));
bonusPop = newPop;
}
if(!obj.hasMover && obj.owner.hasFlingBeacons) {
obj.activateMover();
obj.maxAcceleration = 0;
}
//Order support ships to attack
if(engaged) {
if(obj.supportCount > 0) {
Object@ target = findEnemy(obj, obj, obj.owner, obj.position, 700.0);
if(target !is null) {
//Always target the fleet as a whole
{
Ship@ othership = cast<Ship>(target);
if(othership !is null) {
Object@ leader = othership.Leader;
if(leader !is null)
@target = leader;
}
}
//Order a random support to assist
uint cnt = obj.supportCount;
if(cnt > 0) {
uint attackWith = max(1, cnt / 8);
for(uint i = 0, off = randomi(0,cnt-1); i < attackWith; ++i) {
Object@ sup = obj.supportShip[(i+off) % cnt];
if(sup !is null)
sup.supportAttack(target);
}
}
}
}
}
obj.updateFleetStrength();
}
double tick(Planet& planet, double time) {
//Update region
Region@ prevRegion = planet.region;
if(updateRegion(planet)) {
Node@ node = planet.getNode();
if(node !is null)
node.hintParentObject(planet.region, false);
planet.changeResourceRegion(prevRegion, planet.region);
planet.changeSurfaceRegion(prevRegion, planet.region);
planet.changeStatusRegion(prevRegion, planet.region);
@prevRegion = planet.region;
}
//Take vision from region
if(prevRegion !is null)
planet.donatedVision |= prevRegion.DonateVisionMask;
else
planet.donatedVision |= ~0;
//Tick components
if(planet.hasMover) {
planet.moverTick(time);
if(prevRegion is null && isOutsideUniverseExtents(planet.position))
limitToUniverseExtents(planet.position);
}
else
planet.orbitTick(time);
planet.leaderTick(time);
planet.orderTick(time);
if(planet.hasAbilities)
planet.abilityTick(time);
//Tick occasional stuff
timer += float(time);
if(timer >= 0.9f) {
occasional_tick(planet);
planet.surfaceTick(timer);
planet.resourceTick(timer);
planet.statusTick(time);
timer = 0.f;
}
//Update biome population
planet.Population = planet.population;
Empire@ owner = planet.owner;
if(owner !is null && owner.valid) {
planet.constructionTick(time);
if(planet.hasConstructionUnder(0.2))
return 0.0;
else
return 0.2;
}
else {
return 0.2;
}
}
void syncInitial(const Planet& planet, Message& msg) {
msg << float(planet.Health);
msg << float(planet.MaxHealth);
msg.writeSmall(planet.PlanetType);
msg << planet.renamed;
msg << planet.OrbitSize;
planet.writeResources(msg);
planet.writeSurface(msg);
planet.writeOrbit(msg);
planet.writeLeaderAI(msg);
planet.writeStatuses(msg);
planet.writeCargo(msg);
msg.writeBit(planet.hasAbilities);
if(planet.hasAbilities)
planet.writeAbilities(msg);
msg.writeBit(ringStyle != 0);
if(ringStyle != 0)
msg << ringStyle;
if(planet.hasMover) {
msg.write1();
planet.writeMover(msg);
}
else {
msg.write0();
}
if(moons !is null && moons.length != 0) {
msg.write1();
msg.writeSmall(moons.length);
for(uint i = 0, cnt = moons.length; i < cnt; ++i) {
msg << moons[i].size;
msg << moons[i].style;
}
}
else {
msg.write0();
}
}
bool syncDelta(const Planet& planet, Message& msg) {
bool used = false;
if(planet.writeResourceDelta(msg))
used = true;
else
msg.write0();
if(planet.writeSurfaceDelta(msg))
used = true;
else
msg.write0();
if(planet.writeConstructionDelta(msg))
used = true;
else
msg.write0();
if(planet.writeLeaderAIDelta(msg))
used = true;
else
msg.write0();
if(planet.hasAbilities && planet.writeAbilityDelta(msg))
used = true;
else
msg.write0();
if(planet.writeStatusDelta(msg))
used = true;
else
msg.write0();
if(planet.writeCargoDelta(msg))
used = true;
else
msg.write0();
if(hpDelta) {
used = true;
hpDelta = false;
msg.write1();
msg << float(planet.Health);
msg << float(planet.MaxHealth);
}
else
msg.write0();
if(planet.writeMoverDelta(msg))
used = true;
else
msg.write0();
if(planet.writeOrbitDelta(msg))
used = true;
else
msg.write0();
return used;
}
void syncDetailed(const Planet& planet, Message& msg) {
msg << float(planet.Health);
msg << float(planet.MaxHealth);
planet.writeResources(msg);
planet.writeSurface(msg);
planet.writeConstruction(msg);
planet.writeStatuses(msg);
planet.writeCargo(msg);
msg.writeBit(planet.hasAbilities);
if(planet.hasAbilities)
planet.writeAbilities(msg);
if(planet.hasMover) {
msg.write1();
planet.writeMover(msg);
}
else {
msg.write0();
}
}
void dealPlanetDamage(Planet& planet, double amount) {
double curPop = planet.population;
double hpDmg = 0;
if(curPop > 1.0)
hpDmg = amount * pow(0.4, curPop/5.0);
else
hpDmg = amount;
double popDmg = amount - hpDmg;
if(hpDmg > 0) {
hpDelta = true;
planet.Health -= hpDmg;
if(planet.Health <= 0) {
planet.Health = 0;
planet.destroy();
return;
}
}
if(popDmg > 0) {
double popLost = popDmg / planet.MaxHealth * 3.0 * planet.maxPopulation;
planet.removePopulation(popLost, 1.0);
}
}
void giveHistoricMemory(Planet& planet, Empire@ emp) {
if(planet.memoryMask & emp.mask != 0)
return;
planet.memoryMask |= emp.mask;
planet.giveBasicIconVision(emp);
//TODO: Make this force a vision update on the client
}
void setRing(uint ring) {
ringStyle = ring;
}
void addMoon(Planet& planet, float size = 0, uint style = 0) {
if(moons is null)
@moons = array<MoonData@>();
if(style == 0)
style = randomi();
if(size == 0)
size = randomd(planet.radius * 0.15, planet.radius * 0.3);
MoonData dat;
dat.size = size;
dat.style = style;
moons.insertLast(dat);
PlanetNode@ plNode = cast<PlanetNode>(planet.getNode());
if(plNode !is null)
plNode.addMoon(size, style);
}
uint get_moonCount() {
if(moons is null)
return 0;
return moons.length;
}
};
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
import regions.regions;
import saving;
import systems;
LightDesc lightDesc;
tidy class StarScript {
bool hpDelta = false;
void syncInitial(const Star& star, Message& msg) {
msg << float(star.temperature);
star.writeOrbit(msg);
}
void save(Star& star, SaveFile& file) {
saveObjectStates(star, file);
file << star.temperature;
file << cast<Savable>(star.Orbit);
file << star.Health;
file << star.MaxHealth;
}
void load(Star& star, SaveFile& file) {
loadObjectStates(star, file);
file >> star.temperature;
if(star.owner is null)
@star.owner = defaultEmpire;
lightDesc.att_quadratic = 1.f/(2000.f*2000.f);
double temp = star.temperature;
Node@ node;
double soundRadius = star.radius;
if(temp > 0.0) {
@node = bindNode(star, "StarNode");
node.color = blackBody(temp, max((temp + 15000.0) / 40000.0, 1.0));
}
else {
@node = bindNode(star, "BlackholeNode");
node.color = blackBody(16000.0, max((16000.0 + 15000.0) / 40000.0, 1.0));
cast<BlackholeNode>(node).establish(star);
soundRadius *= 10.0;
}
addAmbientSource(CURRENT_PLAYER, "star_rumble", star.id, star.position, soundRadius);
if(file >= SV_0028)
file >> cast<Savable>(star.Orbit);
if(file >= SV_0102) {
file >> star.Health;
file >> star.MaxHealth;
}
lightDesc.position = vec3f(star.position);
lightDesc.radius = star.radius;
lightDesc.diffuse = node.color * 1.0f;
if(temp <= 0)
lightDesc.diffuse.a = 0.f;
lightDesc.specular = lightDesc.diffuse;
if(star.inOrbit)
makeLight(lightDesc, node);
else
makeLight(lightDesc);
}
void syncDetailed(const Star& star, Message& msg) {
msg << float(star.Health);
msg << float(star.MaxHealth);
}
bool syncDelta(const Star& star, Message& msg) {
if(!hpDelta)
return false;
msg << float(star.Health);
msg << float(star.MaxHealth);
hpDelta = false;
return true;
}
void postLoad(Star& star) {
Node@ node = star.getNode();
if(node !is null)
node.hintParentObject(star.region, false);
}
void postInit(Star& star) {
double soundRadius = star.radius;
//Blackholes need a 'bigger' sound
if(star.temperature == 0.0)
soundRadius *= 10.0;
addAmbientSource(CURRENT_PLAYER, "star_rumble", star.id, star.position, soundRadius);
}
void dealStarDamage(Star& star, double amount) {
hpDelta = true;
star.Health -= amount;
if(star.Health <= 0) {
star.Health = 0;
star.destroy();
}
}
void destroy(Star& star) {
if(!game_ending) {
double explRad = star.radius;
if(star.temperature == 0.0) {
explRad *= 20.0;
for(uint i = 0, cnt = systemCount; i < cnt; ++i) {
auto@ sys = getSystem(i);
double dist = star.position.distanceTo(sys.position);
if(dist < 100000.0) {
double factor = sqr(1.0 - (dist / 100000));
sys.object.addStarDPS(factor * star.MaxHealth * 0.08);
}
}
}
playParticleSystem("StarExplosion", star.position, star.rotation, explRad);
//auto@ node = createNode("NovaNode");
//if(node !is null)
// node.position = star.position;
removeAmbientSource(CURRENT_PLAYER, star.id);
if(star.region !is null)
star.region.addSystemDPS(star.MaxHealth * 0.12);
}
leaveRegion(star);
}
/*void damage(Star& star, DamageEvent& evt, double position, const vec2d& direction) {
evt.damage -= 100.0;
if(evt.damage > 0.0)
star.HP -= evt.damage;
}*/
double tick(Star& obj, double time) {
updateRegion(obj);
obj.orbitTick(time);
Region@ reg = obj.region;
uint mask = ~0;
if(reg !is null && obj.temperature > 0)
mask = reg.ExploredMask.value;
obj.donatedVision = mask;
return 1.0;
}
};
+237
View File
@@ -0,0 +1,237 @@
import systems;
tidy class TerritoryScript {
TerritoryNode@ node;
set_int inner;
set_int edges;
bool regionDelta = false;
array<Region@> regions;
array<Region@> visionPending;
array<bool> visionPendingOp;
void postInit(Territory& obj) {
obj.sightRange = 0.0;
@node = TerritoryNode();
node.setOwner(obj.owner);
}
void destroy(Territory& obj) {
node.markForDeletion();
@node = null;
}
void save(Territory& obj, SaveFile& file) {
uint cnt = regions.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << regions[i];
cnt = visionPending.length;
file << cnt;
for(uint i = 0; i < cnt; ++i) {
file << visionPending[i];
file << visionPendingOp[i];
}
}
void load(Territory& obj, SaveFile& file) {
uint cnt = 0;
file >> cnt;
regions.length = cnt;
for(uint i = 0; i < cnt; ++i)
@regions[i] = cast<Region>(file.readObject());
file >> cnt;
visionPending.length = cnt;
visionPendingOp.length = cnt;
for(uint i = 0; i < cnt; ++i) {
@visionPending[i] = cast<Region>(file.readObject());
file >> visionPendingOp[i];
}
}
void postLoad(Territory& obj) {
@node = TerritoryNode();
node.setOwner(obj.owner);
for(uint i = 0, cnt = regions.length; i < cnt; ++i) {
Region@ region = regions[i];
node.addInner(region.id, region.position, region.radius);
inner.insert(region.id);
if(edges.contains(region.id)) {
node.removeEdge(region.id);
edges.erase(region.id);
}
//Add edges from this region
SystemDesc@ desc = getSystem(region.SystemId);
for(uint i = 0, cnt = desc.adjacent.length; i < cnt; ++i) {
uint adj = desc.adjacent[i];
SystemDesc@ other = getSystem(adj);
if(inner.contains(other.object.id))
continue;
if(edges.contains(other.object.id))
continue;
edges.insert(other.object.id);
node.addEdge(other.object.id, other.position, other.radius);
}
}
//Reverse pending vision to get to old state
for(int i = visionPending.length - 1; i >= 0; --i) {
Region@ region = visionPending[i];
if(visionPendingOp[i])
node.removeInner(region.id);
else
node.addInner(region.id, region.position, region.radius);
}
}
double tick(Territory& obj, double time) {
for(uint i = 0, cnt = visionPending.length; i < cnt; ++i) {
Region@ region = visionPending[i];
if(obj.owner is playerEmpire || region.VisionMask & playerEmpire.visionMask != 0) {
if(visionPendingOp[i])
node.addInner(region.id, region.position, region.radius);
else
node.removeInner(region.id);
visionPending.removeAt(i);
visionPendingOp.removeAt(i);
--i; --cnt;
}
}
return 1.0;
}
bool canTradeTo(Region@ region) const {
return inner.contains(region.id) || edges.contains(region.id);
}
uint getRegionCount() const {
return regions.length;
}
Region@ getRegion(uint i) const {
if(i >= regions.length)
return null;
return regions[i];
}
void add(Territory& obj, Region@ region) {
if(obj.owner is playerEmpire || region.VisionMask & playerEmpire.visionMask != 0) {
node.addInner(region.id, region.position, region.radius);
}
else {
visionPending.insertLast(region);
visionPendingOp.insertLast(true);
}
inner.insert(region.id);
regions.insertLast(region);
regionDelta = true;
if(edges.contains(region.id)) {
node.removeEdge(region.id);
edges.erase(region.id);
}
//Add edges from this region
SystemDesc@ desc = getSystem(region.SystemId);
for(uint i = 0, cnt = desc.adjacent.length; i < cnt; ++i) {
uint adj = desc.adjacent[i];
SystemDesc@ other = getSystem(adj);
if(inner.contains(other.object.id))
continue;
if(edges.contains(other.object.id))
continue;
edges.insert(other.object.id);
node.addEdge(other.object.id, other.position, other.radius);
}
}
void remove(Territory& obj, Region@ region) {
if(obj.owner is playerEmpire || region.VisionMask & playerEmpire.visionMask != 0) {
node.removeInner(region.id);
}
else {
visionPending.insertLast(region);
visionPendingOp.insertLast(false);
}
inner.erase(region.id);
regions.remove(region);
regionDelta = true;
//Remove edges from this region
SystemDesc@ desc = getSystem(region.SystemId);
bool isEdge = false;
for(uint i = 0, cnt = desc.adjacent.length; i < cnt; ++i) {
uint adj = desc.adjacent[i];
SystemDesc@ other = getSystem(adj);
if(inner.contains(other.object.id))
isEdge = true;
if(!edges.contains(other.object.id))
continue;
bool found = false;
for(uint j = 0, jcnt = other.adjacent.length; j < jcnt; ++j) {
SystemDesc@ chk = getSystem(other.adjacent[j]);
if(inner.contains(chk.object.id)) {
found = true;
break;
}
}
if(!found) {
edges.erase(other.object.id);
node.removeEdge(other.object.id);
}
}
//Check if this should be added back as an edge
if(isEdge) {
edges.insert(region.id);
node.addEdge(region.id, region.position, region.radius);
}
}
void _writeRegions(const Territory& obj, Message& msg) {
uint cnt = regions.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << regions[i];
}
void syncInitial(const Territory& obj, Message& msg) {
_writeRegions(obj, msg);
}
void syncDetailed(const Territory& obj, Message& msg) {
_writeRegions(obj, msg);
}
bool syncDelta(const Territory& obj, Message& msg) {
if(!regionDelta)
return false;
if(regionDelta) {
msg.write1();
_writeRegions(obj, msg);
regionDelta = false;
}
else {
msg.write0();
}
return true;
}
};