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
+6
View File
@@ -0,0 +1,6 @@
import bool getCheatsEverOn() from "cheats";
void clientAchive(string id) {
if(!getCheatsEverOn())
unlockAchievement(id);
}
+20
View File
@@ -0,0 +1,20 @@
bool CHEATS_ENABLED_THIS_GAME = false;
bool CHEATS_ENABLED = false;
bool getCheatsEnabled() {
return CHEATS_ENABLED;
}
bool getCheatsEverOn() {
return CHEATS_ENABLED_THIS_GAME;
}
void serverCheatsEnabled(bool enabled) {
CHEATS_ENABLED = enabled;
if(enabled)
CHEATS_ENABLED_THIS_GAME = true;
}
void syncInitial(Message& msg) {
msg >> CHEATS_ENABLED;
msg >> CHEATS_ENABLED_THIS_GAME;
}
+66
View File
@@ -0,0 +1,66 @@
import abilities;
import saving;
tidy class Abilities : Component_Abilities {
array<Ability> abilities;
Ability@ getAbility(int id) {
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(abilities[i].id == id)
return abilities[i];
}
return null;
}
uint get_abilityCount() const {
return abilities.length;
}
void abilityTick(Object& obj, double time) {
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(!abilities[i].disabled)
abilities[i].cooldown = max(0.0, abilities[i].cooldown - time);
}
}
void getAbilities() const {
for(uint i = 0, cnt = abilities.length; i < cnt; ++i)
yield(abilities[i]);
}
uint get_abilityTypes(int id) {
Ability@ abl = getAbility(id);
if(abl is null)
return uint(-1);
return abl.type.id;
}
void readAbilities(Message& msg) {
uint cnt = msg.read_uint();
abilities.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> abilities[i];
}
void readAbilityDelta(Message& msg) {
readAbilities(msg);
}
Ability@ getAbilityOfType(int type) {
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
auto@ abl = abilities[i];
if(abl.type.id == uint(type))
return abl;
}
return null;
}
int findAbilityOfType(int type) const {
auto@ abl = getAbilityOfType(type);
if(abl is null)
return -1;
else
return abl.id;
}
};
+27
View File
@@ -0,0 +1,27 @@
import attributes;
tidy class Attributes : Component_Attributes {
array<double> attributes(getEmpAttributeCount());
double getAttribute(Empire& emp, uint id) {
if(id < EA_COUNT)
return emp.attributes[id];
return attributes[id];
}
void readAttributes(Empire& emp, Message& msg) {
msg.readAlign();
uint cnt = 0;
msg >> cnt;
for(uint i = 0; i < cnt; ++i) {
uint index = msg.readLimited(attributes.length-1);
double value = 1.0;
msg >> value;
attributes[index] = value;
if(index < EA_COUNT)
emp.attributes[index] = value;
}
}
};
+44
View File
@@ -0,0 +1,44 @@
import cargo;
tidy class Cargo : CargoStorage, Component_Cargo {
void getCargo() {
yield(this);
}
double get_cargoCapacity() {
return capacity;
}
double get_cargoStored() {
return filled;
}
double getCargoStored(uint typeId) {
auto@ type = getCargoType(typeId);
if(type is null)
return -1.0;
return get(type);
}
uint get_cargoTypes() {
if(types is null)
return 0;
return types.length;
}
uint get_cargoType(uint index) {
if(types is null)
return uint(-1);
if(index >= types.length)
return uint(-1);
return types[index].id;
}
void readCargo(Message& msg) {
msg >> this;
}
void readCargoDelta(Message& msg) {
readCargo(msg);
}
};
@@ -0,0 +1,74 @@
import abilities;
tidy class EnergyManager : Component_EnergyManager {
Mutex ablMutex;
array<Ability> abilities;
Ability@ getAbility(int id) {
Lock lck(ablMutex);
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(abilities[i].id == id)
return abilities[i];
}
return null;
}
void getAbility(int id) const {
Lock lck(ablMutex);
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(abilities[i].id == id) {
yield(abilities[i]);
return;
}
}
}
void getAbilityOfType(uint id) const {
Lock lck(ablMutex);
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(abilities[i].type.id == id) {
yield(abilities[i]);
return;
}
}
}
uint get_abilityCount() const {
return abilities.length;
}
void abilityTick(Object& obj, double time) {
Lock lck(ablMutex);
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
if(!abilities[i].disabled)
abilities[i].cooldown = max(0.0, abilities[i].cooldown - time);
}
}
void getAbilities() const {
Lock lck(ablMutex);
for(uint i = 0, cnt = abilities.length; i < cnt; ++i)
yield(abilities[i]);
}
uint get_abilityTypes(int id) {
Lock lck(ablMutex);
Ability@ abl = getAbility(id);
if(abl is null)
return uint(-1);
return abl.type.id;
}
void readAbilities(Message& msg) {
Lock lck(ablMutex);
uint cnt = msg.read_uint();
abilities.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> abilities[i];
}
void readAbilityDelta(Message& msg) {
Lock lck(ablMutex);
readAbilities(msg);
}
}
@@ -0,0 +1 @@
#include "server/components/FleetManager.as"
@@ -0,0 +1,123 @@
import influence;
import double getInfluenceIncome(int stock, int stored, double factor) from "influence_global";
import double getInfluenceEfficiency(int stock, int stored) from "influence_global";
import double getInfluencePercentage(Empire& emp) from "influence_global";
import double getInfluenceStorage(int stock) from "influence_global";
tidy class InfluenceManager : Component_InfluenceManager {
Mutex inflMtx;
Mutex cardMtx;
int influence = 0;
int influenceIncome = 0;
double inflFactor = 1.0;
array<InfluenceCard@> cards;
DiplomacyEdict edict;
int get_Influence() {
return influence;
}
int getInfluenceStock() {
return max(influenceIncome, 0);
}
double get_InfluenceIncome() {
return getInfluenceIncome(max(influenceIncome,0), influence, inflFactor);
}
double get_InfluenceEfficiency() {
return getInfluenceEfficiency(max(influenceIncome,0), influence);
}
double get_InfluencePercentage(Empire& emp) {
return getInfluencePercentage(emp);
}
double get_InfluenceCap() {
return getInfluenceStorage(max(influenceIncome,0));
}
double get_InfluenceFactor() {
return inflFactor;
}
uint getEdictType() {
return edict.type;
}
Empire@ getEdictEmpire() {
return edict.empTarget;
}
Object@ getEdictObject() {
return edict.objTarget;
}
uint getInfluenceCardType(int id) {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id)
return cards[i].type.id;
}
return uint(-1);
}
int getInfluenceCardUses(int id) {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id)
return cards[i].uses;
}
return 0;
}
int getInfluenceCardQuality(int id) {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id)
return cards[i].quality;
}
return 0;
}
void getInfluenceCard(int id) {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id)
yield(cards[i]);
}
}
void influenceTick(Empire& emp, double time) {
}
uint getInfluenceCardCount() {
return cards.length;
}
void getInfluenceCards() {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i)
yield(cards[i]);
}
void readInfluenceManager(Message& msg) {
if(msg.readBit()) {
Lock lock(cardMtx);
uint cnt = msg.readSmall();
cards.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(cards[i] is null)
@cards[i] = InfluenceCard();
msg >> cards[i];
}
}
msg >> inflFactor;
influence = msg.readSignedSmall();
influenceIncome = msg.readSignedSmall();
msg >> edict;
}
}
+1
View File
@@ -0,0 +1 @@
#include "server/components/Mover.as"
@@ -0,0 +1,54 @@
import notifications;
tidy class Notifications : Component_Notifications {
Mutex mtx;
array<Notification@> list;
uint get_notificationCount() const {
return list.length;
}
void getNotifications(uint limit, int beforeId = -1, bool reverse = true) {
Lock lock(mtx);
if(reverse) {
if(beforeId == -1 || beforeId > int(list.length))
beforeId = list.length;
if(beforeId == 0)
return;
for(int i = beforeId - 1; i >= 0; --i) {
Notification@ n = list[i];
yieldNotification(n);
if(--limit == 0)
break;
}
}
else {
int cnt = list.length;
if(beforeId == -1 || beforeId > cnt)
beforeId = 0;
if(beforeId >= cnt)
return;
for(int i = beforeId; i < cnt; ++i) {
Notification@ n = list[i];
yieldNotification(n);
if(--limit == 0)
break;
}
}
}
void readNotifications(Message& msg, bool delta) {
Lock lock(mtx);
uint cnt = 0;
msg >> cnt;
if(!delta)
list.length = 0;
list.reserve(list.length + cnt);
for(uint i = 0; i < cnt; ++i) {
uint type = msg.read_uint();
Notification@ n = createNotification(type);
msg >> n;
list.insertLast(n);
}
}
};
+356
View File
@@ -0,0 +1,356 @@
import ftl;
import resources;
tidy class ColonizationEvent : Serializable {
Object@ from;
Object@ to;
void write(Message& msg) {
msg << from;
msg << to;
}
void read(Message& msg) {
msg >> from;
msg >> to;
}
};
tidy class ObjectManager : Component_ObjectManager {
ReadWriteMutex plMutex;
Planet@[] planets;
Asteroid@[] asteroids;
Orbital@[] Orbitals;
Mutex flingMutex;
Object@[] flingBeacons;
Mutex gateMutex;
Object@[] gates;
Mutex artifMutex;
Artifact@[] artifacts;
ColonizationEvent@[] colonizations;
ColonizationEvent@[] queuedAutoColonizations;
AutoImportDesc[] autoImports;
ReadWriteMutex defenseMtx;
array<Object@> defenseObjects;
set_int defenseSet;
double defenseRate = 0;
double defenseStorage = 0;
double defenseStored = 0;
double localDefenseRate = 0;
void getPlanets() {
ReadLock lock(plMutex);
for(uint i = 0, cnt = planets.length; i < cnt; ++i)
yield(planets[i]);
}
void getAutoImports() {
ReadLock lock(plMutex);
for(uint i = 0, cnt = autoImports.length; i < cnt; ++i) {
if(!autoImports[i].handled)
yield(autoImports[i]);
}
}
uint get_planetCount() {
return planets.length;
}
Planet@ get_planetList(uint index) {
ReadLock lock(plMutex);
if(index >= planets.length)
return null;
return planets[index];
}
uint get_orbitalCount() {
return Orbitals.length;
}
Orbital@ get_orbitals(uint index) {
ReadLock lock(plMutex);
if(index >= Orbitals.length)
return null;
return Orbitals[index];
}
Orbital@ getClosestOrbital(uint type, const vec3d& position) {
ReadLock lock(plMutex);
Orbital@ closest;
double closestDist = INFINITY;
for(uint i = 0, cnt = Orbitals.length; i < cnt; ++i) {
Orbital@ orb = Orbitals[i];
if(orb.coreModule == type) {
double d = orb.position.distanceToSQ(position);
if(d < closestDist) {
closestDist = d;
@closest = orb;
}
}
}
return closest;
}
bool isFlingBeacon(Object@ obj) {
Lock lock(flingMutex);
for(uint i = 0, cnt = flingBeacons.length; i < cnt; ++i)
if(flingBeacons[i] is obj)
return true;
return false;
}
void getAsteroids() {
ReadLock lock(plMutex);
for(uint i = 0, cnt = asteroids.length; i < cnt; ++i)
yield(asteroids[i]);
}
void getFlingBeacons() {
Lock lock(flingMutex);
for(uint i = 0, cnt = flingBeacons.length; i < cnt; ++i)
yield(flingBeacons[i]);
}
void getStargates() {
Lock lock(gateMutex);
for(uint i = 0, cnt = gates.length; i < cnt; ++i)
yield(gates[i]);
}
void getArtifacts() {
Lock lock(artifMutex);
for(uint i = 0, cnt = artifacts.length; i < cnt; ++i)
yield(artifacts[i]);
}
void getOrbitals() {
ReadLock lock(plMutex);
for(uint i = 0, cnt = Orbitals.length; i < cnt; ++i)
yield(Orbitals[i]);
}
void getQueuedColonizations(Empire& emp) {
ReadLock lock(plMutex);
for(uint i = 0, cnt = queuedAutoColonizations.length; i < cnt; ++i) {
auto@ q = queuedAutoColonizations[i];
if(q.to.owner !is emp && q.from is null)
yield(q.to);
}
for(uint i = 0, cnt = colonizations.length; i < cnt; ++i) {
if(colonizations[i].to.owner !is emp)
yield(colonizations[i].to);
}
}
bool get_hasFlingBeacons() {
return flingBeacons.length != 0;
}
Object@ getFlingBeacon(vec3d position) {
Lock lock(flingMutex);
for(uint i = 0, cnt = flingBeacons.length; i < cnt; ++i) {
if(flingBeacons[i].position.distanceToSQ(position) < FLING_BEACON_RANGE_SQ)
return flingBeacons[i];
}
return null;
}
Object@ getClosestFlingBeacon(vec3d position) {
Lock lock(flingMutex);
Object@ nearest;
double dist = 0;
for(uint i = 0, cnt = flingBeacons.length; i < cnt; ++i) {
Object@ beacon = flingBeacons[i];
double d = beacon.position.distanceToSQ(position);
if(nearest is null || d < dist) {
@nearest = beacon;
dist = d;
}
}
return nearest;
}
Object@ getClosestFlingBeacon(Object& obj) {
Lock lock(flingMutex);
Object@ nearest;
double dist = 0;
for(uint i = 0, cnt = flingBeacons.length; i < cnt; ++i) {
Object@ beacon = flingBeacons[i];
if(beacon is obj)
continue;
double d = beacon.position.distanceToSQ(obj.position);
if(nearest is null || d < dist) {
@nearest = beacon;
dist = d;
}
}
return nearest;
}
bool hasStargates() {
return gates.length != 0;
}
Object@ getStargate(vec3d position) {
Lock lock(gateMutex);
Object@ best;
double bestDist = INFINITY;
for(uint i = 0, cnt = gates.length; i < cnt; ++i) {
Object@ gate = gates[i];
double d = gate.position.distanceToSQ(position);
if(d < bestDist) {
bestDist = d;
@best = gate;
}
}
return best;
}
bool isDefending(Object@ obj) {
if(obj is null)
return false;
ReadLock lck(defenseMtx);
return defenseSet.contains(obj.id);
}
bool get_hasDefending() {
return defenseObjects.length > 0;
}
void getDefending() {
ReadLock lck(defenseMtx);
for(uint i = 0, cnt = defenseObjects.length; i < cnt; ++i)
yield(defenseObjects[i]);
}
void setDefending(Object@ obj, bool value) {
if(obj is null)
return;
WriteLock lck(defenseMtx);
if(value) {
if(defenseSet.contains(obj.id))
return;
defenseSet.insert(obj.id);
defenseObjects.insertLast(obj);
}
else {
if(!defenseSet.contains(obj.id))
return;
defenseSet.erase(obj.id);
defenseObjects.remove(obj);
}
}
double get_globalDefenseRate() {
return defenseRate + localDefenseRate;
}
double get_globalDefenseStorage() {
return defenseStorage;
}
double get_globalDefenseStored() {
return defenseStored;
}
void readObjects(Message& msg) {
WriteLock wlock(plMutex);
if(msg.readBit()) {
msg >> defenseRate;
msg >> localDefenseRate;
msg >> defenseStorage;
msg >> defenseStored;
}
if(msg.readBit()) {
uint cnt = 0;
msg >> cnt;
planets.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> planets[i];
msg >> cnt;
asteroids.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> asteroids[i];
msg >> cnt;
Orbitals.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> Orbitals[i];
{
Lock lock(flingMutex);
msg >> cnt;
flingBeacons.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> flingBeacons[i];
}
{
Lock lock(gateMutex);
msg >> cnt;
gates.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> gates[i];
}
{
Lock lock(artifMutex);
msg >> cnt;
artifacts.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> artifacts[i];
}
{
ReadLock lock(defenseMtx);
msg >> cnt;
defenseObjects.length = cnt;
defenseSet.clear();
for(uint i = 0; i < cnt; ++i) {
msg >> defenseObjects[i];
if(defenseObjects[i] !is null)
defenseSet.insert(defenseObjects[i].id);
}
}
}
if(msg.readBit()) {
uint cnt = 0;
msg >> cnt;
colonizations.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(colonizations[i] is null)
@colonizations[i] = ColonizationEvent();
msg >> colonizations[i];
}
msg >> cnt;
queuedAutoColonizations.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(queuedAutoColonizations[i] is null)
@queuedAutoColonizations[i] = ColonizationEvent();
msg >> queuedAutoColonizations[i];
}
}
if(msg.readBit()) {
uint cnt = 0;
msg >> cnt;
autoImports.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> autoImports[i];
}
}
};
+1
View File
@@ -0,0 +1 @@
#include "server/components/Orbit.as"
+64
View File
@@ -0,0 +1,64 @@
import random_events;
tidy class RandomEvents : Component_RandomEvents {
Mutex mtx;
array<CurrentEvent> events;
CurrentEvent@ getEventByID(int id) {
for(uint i = 0, cnt = events.length; i < cnt; ++i) {
if(events[i].id == id)
return events[i];
}
return null;
}
int get_currentEventID() {
if(events.length == 0)
return -1;
Lock lck(mtx);
if(events.length == 0)
return -1;
return events[0].id;
}
bool hasCurrentEvents() {
return events.length != 0;
}
void getCurrentEvents() {
Lock lck(mtx);
for(uint i = 0, cnt = events.length; i < cnt; ++i)
yield(events[i]);
}
void getEvent(int id) {
Lock lck(mtx);
auto@ evt = getEventByID(id);
if(evt !is null)
yield(evt);
}
void chooseEventOption(Empire& emp, int evtId, uint optId) {
Lock lck(mtx);
for(uint i = 0, cnt = events.length; i < cnt; ++i) {
if(events[i].id == evtId) {
events.removeAt(i);
break;
}
}
}
void readEvents(Message& msg) {
if(!msg.readBit()) {
if(events.length != 0) {
Lock lck(mtx);
events.length = 0;
}
return;
}
Lock lck(mtx);
events.length = msg.readSmall();
for(uint i = 0, cnt = events.length; i < cnt; ++i)
msg >> events[i];
}
};
+151
View File
@@ -0,0 +1,151 @@
import research;
tidy class ResearchGrid : Component_ResearchGrid {
ReadWriteMutex mtx;
TechnologyGrid grid;
array<bool>@ tagUnlocks;
double researchRate = 0;
double points = 0;
double totalGenerated = 0;
double get_ResearchRate(Empire& emp) {
return researchRate * ResearchEfficiency * emp.ResearchGenerationFactor;
}
double get_ResearchEfficiency() {
return 2000.0 / (2000.0 + totalGenerated);
}
double get_ResearchPoints() {
return points;
}
void getTechnologyNodes() {
ReadLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i)
yield(grid.nodes[i]);
}
void getTechnologyNode(int id) {
ReadLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
if(id == grid.nodes[i].id) {
yield(grid.nodes[i]);
return;
}
}
}
TechnologyNode@ getNode(int id) {
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
if(grid.nodes[i].id == id)
return grid.nodes[i];
}
return null;
}
void setResearchQueued(Empire& emp, int id, bool queued) {
//PREDICTIVE
WriteLock lock(mtx);
auto@ node = getNode(id);
if(node is null)
return;
if(node.bought)
return;
node.queued = queued;
}
void research(Empire& emp, int id, bool secondary = false, bool queue = false) {
//PREDICTIVE
WriteLock lock(mtx);
auto@ node = getNode(id);
if(node is null)
return;
if(node.bought)
return;
if(queue) {
if(node.canUnlock(emp))
node.queued = true;
}
}
void getResearchingNodes() {
ReadLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ node = grid.nodes[i];
if(!node.bought)
continue;
if(node.unlocked)
continue;
if(!node.unlockable)
continue;
yield(grid.nodes[i]);
}
}
void getTechnologyNode(vec2i pos) {
ReadLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
if(pos == grid.nodes[i].position) {
yield(grid.nodes[i]);
return;
}
}
}
bool isTagUnlocked(int id) {
if(tagUnlocks is null)
return false;
if(id < 0 || uint(id) >= tagUnlocks.length)
return false;
return tagUnlocks[id];
}
void readResearch(Message& msg) {
WriteLock lock(mtx);
msg >> researchRate;
msg >> points;
msg >> totalGenerated;
bool delta = msg.readBit();
bool gridDelta = msg.readBit();
if(msg.readBit()) {
uint cnt = msg.readSmall();
if(tagUnlocks is null)
@tagUnlocks = array<bool>(cnt, false);
for(uint i = 0; i < cnt; ++i) {
bool unlocked = msg.readBit();
if(i < tagUnlocks.length)
tagUnlocks[i] = unlocked;
}
}
if(delta || gridDelta) {
if(gridDelta) {
msg >> grid.minPos;
msg >> grid.maxPos;
uint cnt = 0;
msg >> cnt;
grid.nodes.length = cnt;
}
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
if(grid.nodes[i] is null)
@grid.nodes[i] = TechnologyNode();
if(gridDelta)
grid.nodes[i].read(msg);
else
grid.nodes[i].readStatus(msg);
}
if(gridDelta)
grid.regenGrid();
}
}
};
@@ -0,0 +1,232 @@
import resources;
tidy class ResourceManager : Component_ResourceManager {
Mutex budgetMutex;
Mutex ftlMutex;
Mutex energyMutex;
double Population = 0;
double FTL_Capacity = 0;
double FTL_Stored = 0;
double FTL_Income = 0;
double FTL_Use = 0;
double Energy_Stored = 0;
double Energy_Income = 0;
double Energy_Use = 0;
double Energy_Allocated = 0;
uint welfareMode = WM_Influence;
int Budget_Total = 0;
int Maintenance = 0;
int PrevBudget = 0;
int PrevMaintenance = 0;
int Budget_Remaining = 0;
int Budget_Forward = 0;
int Budget_CycleId = 0;
int Budget_Bonus = 0;
double Budget_Cycle = 3.0 * 60.0;
double Budget_Tick = Budget_Cycle;
double Borrow_Rate = 1.5;
array<int> moneyTypes = array<int>(MoT_COUNT, 0);
//Population
double get_EstTotalPopulation() const {
return max(round(Population / 10.0), 1.0) * 10.0;
}
double get_TotalPopulation() const {
return Population;
}
//FTL
double get_FTLIncome() {
return FTL_Income;
}
double get_FTLStored() {
return FTL_Stored;
}
double get_FTLUse(const Empire& emp) {
return FTL_Use * emp.FTLCostFactor;
}
double get_FTLCapacity() {
return FTL_Capacity;
}
bool get_FTLShortage(const Empire& emp) const {
return FTL_Stored <= 0.0001 && (FTL_Use * emp.FTLCostFactor) > FTL_Income + 0.0001;
}
bool isFTLShortage(const Empire& emp, double amt) const {
if(FTL_Use + amt <= FTL_Income + 0.0001)
return false;
//Only not a shortage if we can run it for at least a minute
double cons = (FTL_Use + amt) * emp.FTLCostFactor * 60.0;
double have = FTL_Stored + FTL_Income * 60.0;
return cons >= have;
}
//Energy
double get_EnergyIncome() {
return Energy_Income;
}
double get_EnergyStored() {
return Energy_Stored;
}
double get_EnergyUse() {
return Energy_Use;
}
double get_EnergyEfficiency(Empire& emp) {
return pow(0.5, max(Energy_Stored + Energy_Allocated - emp.FreeEnergyStorage, 0.0) / config::ENERGY_EFFICIENCY_STEP);
}
bool get_EnergyShortage() {
return Energy_Stored <= 0.0001 && Energy_Use > EnergyIncome + 0.0001;
}
bool isEnergyShortage(double amt) {
if(Energy_Use + amt <= EnergyIncome + 0.0001)
return false;
//Only not a shortage if we can run it for at least a minute
double cons = (Energy_Use + amt) * 60.0;
double have = Energy_Stored + EnergyIncome * 60.0;
return cons >= have;
}
bool consumeEnergyUse(double amt) {
Lock lock(energyMutex);
if(Energy_Use + amt <= EnergyIncome + 0.0001) {
Energy_Use += amt;
return true;
}
//Only not a shortage if we can run it for at least a minute
double cons = (Energy_Use + amt) * 60.0;
double have = Energy_Stored + EnergyIncome * 60.0;
if(cons >= have)
return false;
Energy_Use += amt;
return true;
}
//Budget
int getMoneyFromType(uint type) {
if(type < MoT_COUNT)
return moneyTypes[type];
return 0;
}
int get_TotalBudget() {
return Budget_Total;
}
int get_MaintenanceBudget() {
return Maintenance;
}
int get_RemainingBudget() {
return Budget_Remaining;
}
int get_ForwardBudget() {
return Budget_Forward;
}
int get_BonusBudget() {
return Budget_Bonus;
}
double get_BorrowRate() {
return Borrow_Rate;
}
double get_BudgetCycle() {
return Budget_Cycle;
}
double get_BudgetTimer() {
return Budget_Tick;
}
float get_DebtFactor() {
if(Budget_Remaining >= 0)
return 0.f;
if(Budget_Total < 100)
return float(-Budget_Remaining) / 100.f;
return float(-Budget_Remaining) / float(Budget_Total);
}
int get_EstNextBudget() const {
int budget = Budget_Total - Maintenance + Budget_Forward + Budget_Bonus;
budget += min(Budget_Remaining - min(PrevBudget - PrevMaintenance, 0), 0);
return budget;
}
int getEstBudgetConsuming(int amount) const {
int budget = Budget_Total - Maintenance + Budget_Forward + max(Budget_Bonus - amount, 0);
budget += min(Budget_Remaining - amount - min(PrevBudget - PrevMaintenance, 0), 0);
return budget;
}
int get_BudgetCycleId() {
return Budget_CycleId;
}
uint get_WelfareMode() const {
return welfareMode;
}
bool canBorrow(int amount) const {
amount = ceil(double(amount) * Borrow_Rate);
return EstNextBudget >= amount;
}
bool canPay(int amount) const {
if(amount <= Budget_Remaining)
return true;
return canBorrow(amount - Budget_Remaining);
}
//Networking
void readResources(Empire& emp, Message& msg) {
Population = msg.read_float();
FTL_Capacity = msg.read_float();
FTL_Stored = msg.read_float();
FTL_Income = msg.read_float();
FTL_Use = msg.read_float();
Energy_Stored = msg.read_float();
Energy_Income = msg.read_float();
Energy_Use = msg.read_float();
Energy_Allocated = msg.read_float();
msg >> Budget_Total;
msg >> Maintenance;
msg >> PrevMaintenance;
msg >> PrevBudget;
msg >> Budget_Remaining;
msg >> Budget_Forward;
msg >> Budget_Bonus;
msg >> Budget_CycleId;
Budget_Cycle = msg.read_float();
Budget_Tick = msg.read_float();
Borrow_Rate = msg.read_float();
for(uint i = 0; i < MoT_COUNT; ++i)
moneyTypes[i] = msg.readSignedSmall();
welfareMode = msg.readLimited(WM_COUNT-1);
}
};
+600
View File
@@ -0,0 +1,600 @@
import resources;
import systems;
import planet_levels;
from resources import _tempResource;
tidy class ObjectResources : Component_Resources {
TradePath@[] resourcePaths;
Object@[] pathsActive;
Resource[] nativeResources;
Resource primaryResource;
Resource[] resources;
QueuedImport[] queuedImports;
Resources availableResources;
array<QueuedResource@> queuedExports;
int ExportDisabled = 0;
int ImportDisabled = 0;
uint ResourceModId = 0;
bool terraforming = false;
double resVanishBonus = 0.0;
ObjectResources() {
}
bool isTerraforming() {
return terraforming;
}
void getNativeResources(Player& pl, const Object& obj) {
Empire@ plEmp = pl.emp;
if(plEmp is obj.owner || pl == SERVER_PLAYER) {
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i)
yield(nativeResources[i]);
}
else {
Resource@ res = _tempResource();
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i) {
res = nativeResources[i];
@res.exportedTo = null;
if(queuedExports !is null) {
for(uint n = 0, ncnt = queuedExports.length; n < ncnt; ++n) {
QueuedResource@ q = queuedExports[n];
if(q.forEmpire is plEmp && res.id == q.id)
@res.exportedTo = q.to;
}
}
yield(res);
}
}
}
uint get_nativeResourceCount() {
return nativeResources.length;
}
uint get_nativeResourceType(uint i) {
if(i >= nativeResources.length)
return uint(-1);
return nativeResources[i].type.id;
}
int get_nativeResourceId(uint i) {
if(i >= nativeResources.length)
return -1;
return nativeResources[i].id;
}
uint get_nativeResourceTotalLevel() const {
uint level = 0;
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i)
level += nativeResources[i].type.level;
return level;
}
bool get_nativeResourceUsable(uint i) {
if(i >= nativeResources.length)
return false;
return nativeResources[i].usable;
}
uint get_primaryResourceType() const {
if(primaryResource.type is null)
return uint(-1);
return primaryResource.type.id;
}
uint get_primaryResourceLevel() const {
if(primaryResource.type is null)
return 0;
return primaryResource.type.level;
}
uint get_primaryResourceLimitLevel(const Object& obj) const {
if(primaryResource.type is null)
return 0;
if(primaryResource.type.limitlessLevel)
return getMaxPlanetLevel(obj.levelChain);
return primaryResource.type.level;
}
int get_primaryResourceId() const {
return primaryResource.id;
}
bool get_primaryResourceUsable() const {
return primaryResource.usable;
}
bool get_primaryResourceLocked() const {
return primaryResource.locked;
}
bool get_primaryResourceExported() const {
return primaryResource.exportedTo !is null;
}
bool get_nativeResourceLocked(Player& pl, Object& obj, uint i) {
Empire@ forEmp = pl.emp;
if(pl == SERVER_PLAYER)
@forEmp = obj.owner;
if(i >= nativeResources.length)
return false;
auto@ r = nativeResources[i];
if(forEmp is obj.owner)
return r.locked;
for(uint i = 0, cnt = queuedExports.length; i < cnt; ++i) {
QueuedResource@ q = queuedExports[i];
if(q.forEmpire is forEmp && r.id == q.id)
return q.locked;
}
return false;
}
float get_resourceVanishRate() const {
return 1.f / (1.f + resVanishBonus);
}
uint getTradedResourceCount() const {
uint tradedNative = 0;
uint usableNative = 0;
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i) {
if(!nativeResources[i].usable)
continue;
++usableNative;
if(nativeResources[i].exportedTo !is null)
++tradedNative;
}
return (resources.length - (usableNative - tradedNative)) + tradedNative;
}
Object@ get_nativeResourceDestination(Player& pl, const Object& obj, uint i) {
if(i >= nativeResources.length)
return null;
Empire@ emp = pl.emp;
Resource@ r = nativeResources[i];
//Find the current export
if(r.exportedTo !is null && (emp is obj.owner || pl == SERVER_PLAYER))
return r.exportedTo;
//Try to find a queued export
for(uint i = 0, cnt = queuedExports.length; i < cnt; ++i) {
QueuedResource@ q = queuedExports[i];
if(q.forEmpire is emp && r.id == q.id)
return q.to;
}
return null;
}
Object@ getNativeResourceDestination(const Object& obj, Empire@ emp, uint i) {
if(i >= nativeResources.length)
return null;
Resource@ r = nativeResources[i];
//Find the current export
if(r.exportedTo !is null && emp is obj.owner)
return nativeResources[i].exportedTo;
//Try to find a queued export
for(uint i = 0, cnt = queuedExports.length; i < cnt; ++i) {
QueuedResource@ q = queuedExports[i];
if(q.forEmpire is emp && r.id == q.id)
return q.to;
}
return null;
}
uint getNativeIndex(int id) {
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i) {
if(nativeResources[i].id == id)
return i;
}
return uint(-1);
}
string getDisabledReason(Object& obj, int id) {
uint index = getNativeIndex(id);
if(index >= nativeResources.length)
return "Not present";
auto@ r = nativeResources[index];
if(!r.type.exportable)
return "";
auto@ to = r.exportedTo;
if(ExportDisabled != 0)
return locale::EXPBLOCK_DISABLED;
if((to !is null && to.region is null) || obj.region is null)
return locale::EXPBLOCK_DEEPSPACE;
if(!obj.owner.valid) {
if(obj.isAsteroid)
return locale::EXPBLOCK_UNMINED;
else if(obj.isPlanet)
return locale::EXPBLOCK_UNCOLONIZED;
else
return locale::EXPBLOCK_UNOWNED;
}
if(to !is null && obj.owner !is to.owner)
return locale::EXPBLOCK_UNOWNED;
if(obj.hasSurfaceComponent) {
if(obj.population < 1.0)
return format(locale::EXPBLOCK_POP, uint(1));
else if(obj.resourceLevel < r.type.level)
return format(locale::EXPBLOCK_LOWLEVEL, r.type.level);
else if(obj.population < getPlanetLevelRequiredPop(obj, r.type.level))
return format(locale::EXPBLOCK_POP, uint(getPlanetLevelRequiredPop(obj, r.type.level)));
}
if(to !is null) {
//NOTE: Approximation of trade rules
auto@ src = obj.region;
auto@ dst = to.region;
if(src !is null && dst !is null && src !is dst &&
src.getTerritory(obj.owner) !is dst.getTerritory(obj.owner))
return locale::EXPBLOCK_DISCONNECTED;
}
if(!r.usable)
return locale::EXPBLOCK_UNUSABLE;
return "";
}
void getAvailableResources() {
for(uint i = 0, cnt = resources.length; i < cnt; ++i)
yield(resources[i]);
}
Object@ get_availableResourceOrigin(uint index) const {
if(index >= resources.length)
return null;
return resources[index].origin;
}
void getImportedResources(const Object& obj) {
for(uint i = 0, cnt = resources.length; i < cnt; ++i)
if(resources[i].origin is null || obj !is resources[i].origin)
yield(resources[i]);
}
bool get_hasAutoImports(Player& pl, Object& obj) {
for(uint i = 0, cnt = queuedImports.length; i < cnt; ++i)
if(queuedImports[i].origin is null && pl.emp is queuedImports[i].forEmpire)
return true;
return false;
}
void getAllResources(Player& pl, const Object& obj) {
Empire@ emp = pl.emp;
if(emp is obj.owner || pl == SERVER_PLAYER) {
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i)
yield(nativeResources[i]);
for(uint i = 0, cnt = resources.length; i < cnt; ++i) {
if(obj !is resources[i].origin)
yield(resources[i]);
}
}
else {
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i)
yield(nativeResources[i]);
}
for(uint i = 0, cnt = queuedImports.length; i < cnt; ++i) {
if(queuedImports[i].forEmpire is emp)
yield(queuedImports[i]);
}
}
void getQueuedImports(Player& pl, const Object& obj) {
Empire@ emp = pl.emp;
if(queuedImports !is null) {
for(uint i = 0, cnt = queuedImports.length; i < cnt; ++i) {
if(queuedImports[i].forEmpire is emp)
yield(queuedImports[i]);
}
}
}
uint get_queuedImportCount() {
return queuedImports.length;
}
uint get_queuedImportType(Player& pl, Object& obj, uint i) {
if(pl.emp !is queuedImports[i].forEmpire)
return uint(-1);
return queuedImports[i].type.id;
}
Object@ get_queuedImportOrigin(Player& pl, Object& obj, uint i) {
if(pl.emp !is queuedImports[i].forEmpire)
return null;
return queuedImports[i].origin;
}
void getResourceAmounts() {
yield(availableResources);
}
bool hasImportedResources(const Object& obj) const {
if(resources.length == 0)
return false;
for(uint i = 0, cnt = resources.length; i < cnt; ++i) {
if(obj !is resources[i].origin)
return true;
}
return false;
}
uint getImportsOfClass(Player& pl, const Object& obj, uint clsId) const {
const ResourceClass@ cls = getResourceClass(clsId);
if(cls is null)
return 0;
uint count = 0;
Empire@ emp = pl.emp;
if(emp is obj.owner) {
for(uint i = 0, cnt = resources.length; i < cnt; ++i) {
if(resources[i].type.cls is cls)
count += 1;
}
}
for(uint i = 0, cnt = queuedImports.length; i < cnt; ++i) {
const QueuedImport@ imp = queuedImports[i];
if(imp.forEmpire is emp && imp.type.cls is cls)
count += 1;
}
return count;
}
bool get_hasAutoImports(Player& pl, const Object& obj) {
for(uint i = 0, cnt = queuedImports.length; i < cnt; ++i)
if(queuedImports[i].origin is null && pl.emp is queuedImports[i].forEmpire)
return true;
return false;
}
uint get_availableResourceCount() const {
return resources.length;
}
uint get_usableResourceCount() const {
uint amt = 0;
for(uint i = 0, cnt = resources.length; i < cnt; ++i) {
if(resources[i].usable)
amt += 1;
}
return amt;
}
uint get_availableResourceType(uint index) const {
if(index >= resources.length)
return uint(-1);
return resources[index].type.id;
}
bool get_availableResourceUsable(uint index) const {
if(index >= resources.length)
return false;
return resources[index].usable;
}
bool isResourceAvailable(uint id) const {
return availableResources.getAmount(getResource(id)) != 0;
}
uint getAvailableResourceAmount(uint id) const {
return availableResources.getAmount(getResource(id));
}
Resource@ resourceFrom(Object@ from, int id) {
for(uint i = 0, cnt = resources.length; i < cnt; ++i) {
Resource@ r = resources[i];
if(r.origin is from && r.id == id)
return r;
}
return null;
}
void setAvailableResourceVanish(Object& obj, Object@ from, int id, double vanishTime) {
Resource@ r = resourceFrom(from, id);
if(r !is null)
r.vanishTime = vanishTime;
}
bool get_exportEnabled() {
return ExportDisabled == 0;
}
bool get_importEnabled() {
return ImportDisabled == 0;
}
void clearLines(Resource@ res, TradePath@ path, Object@ from, Object@ to) {
uint cnt = path.pathSize;
if(cnt == 1) {
path.origin.object.removeTradePathing(-1, from, res.id);
}
else {
for(uint i = 0; i < cnt-1; ++i) {
SystemDesc@ node = path.pathNode[i];
SystemDesc@ next = path.pathNode[i+1];;
node.object.removeTradePathing(next.index, from, res.id);
}
}
}
void updateLines(Resource@ res, TradePath@ path, Object@ from, Object@ to) {
uint cnt = path.pathSize;
if(cnt == 1) {
path.origin.object.addTradePathing(-1, from, to, res.id, res.type.id);
}
else {
for(uint i = 0; i < cnt-1; ++i) {
SystemDesc@ node = path.pathNode[i];
SystemDesc@ next = path.pathNode[i+1];;
node.object.addTradePathing(next.index, from, to, res.id, res.type.id);
}
}
}
void resourceTick(Object& obj, double time) {
//Vanish any native resources
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i) {
Resource@ r = nativeResources[i];
if(!r.usable || obj.owner is null || !obj.owner.valid)
continue;
switch(r.type.vanishMode) {
case VM_WhenExported:
if(r.exportedTo is null)
continue;
break;
case VM_ExportedInCombat:
if(r.exportedTo is null) {
if(r.origin is null || !r.origin.inCombat)
continue;
}
else {
if(!r.exportedTo.inCombat)
continue;
}
break;
case VM_Always:
break;
case VM_Custom:
if(!r.type.shouldVanish(obj, r))
continue;
break;
default:
continue;
}
if(r.exportedTo !is null) {
float rate = r.exportedTo.resourceVanishRate;
r.vanishTime += time * rate;
r.exportedTo.setAvailableResourceVanish(obj, r.id, r.vanishTime);
}
else {
r.vanishTime += time * get_resourceVanishRate();
setAvailableResourceVanish(obj, obj, r.id, r.vanishTime);
}
}
}
void destroyObjResources(Object& obj) {
for(uint i = 0, cnt = nativeResources.length; i < cnt; ++i) {
Resource@ r = nativeResources[i];
TradePath@ path = resourcePaths[i];
if(pathsActive[i] !is null)
clearLines(r, path, r.origin, pathsActive[i]);
}
}
uint get_resourceModID() {
return ResourceModId;
}
void _readRes(Object& obj, Message& msg) {
msg >> terraforming;
resVanishBonus = msg.read_float();
availableResources.read(msg);
{
uint cnt = msg.readSmall();
nativeResources.length = cnt;
for(uint i = 0; i < cnt; ++i) {
Resource@ r = nativeResources[i];
r.read(msg);
if(r.type.vanishMode != VM_Never) {
if(r.exportedTo !is null)
r.exportedTo.setAvailableResourceVanish(obj, r.id, r.vanishTime);
else
setAvailableResourceVanish(obj, obj, r.id, r.vanishTime);
}
}
}
{
uint cnt = msg.readSmall();
resources.length = cnt;
for(uint i = 0; i < cnt; ++i)
resources[i].read(msg);
}
{
uint cnt = msg.readSmall();
queuedImports.length = cnt;
for(uint i = 0; i < cnt; ++i)
queuedImports[i].readQueued(msg);
}
{
uint cnt = msg.readSmall();
queuedExports.length = cnt;
for(uint i = 0; i < cnt; ++i) {
@queuedExports[i] = QueuedResource();
queuedExports[i].read(msg);
}
}
if(nativeResources.length != 0)
primaryResource.descFrom(nativeResources[0]);
else
primaryResource.descFrom(null);
++ResourceModId;
}
void _readPath(Object& obj, Message& msg) {
uint cnt = msg.readSmall();
if(cnt != resourcePaths.length) {
uint prev = resourcePaths.length;
resourcePaths.length = cnt;
pathsActive.length = cnt;
for(uint i = prev; i < cnt; ++i) {
@resourcePaths[i] = TradePath(obj.owner);
@pathsActive[i] = null;
}
}
for(uint i = 0; i < cnt; ++i) {
TradePath@ path = resourcePaths[i];
Resource@ r = nativeResources[i];
if(pathsActive[i] !is null)
clearLines(r, path, r.origin, pathsActive[i]);
@path.forEmpire = obj.owner;
path.read(msg);
if(path.isUsablePath) {
@pathsActive[i] = r.exportedTo;
updateLines(r, path, r.origin, r.exportedTo);
}
else {
@pathsActive[i] = null;
}
}
}
void readResourceDelta(Object& obj, Message& msg) {
if(msg.readBit())
_readRes(obj, msg);
if(msg.readBit())
_readPath(obj, msg);
}
void readResources(Object& obj, Message& msg) {
_readRes(obj, msg);
_readPath(obj, msg);
if(msg.readBit())
msg >> ImportDisabled;
else
ImportDisabled = 0;
if(msg.readBit())
msg >> ExportDisabled;
else
ExportDisabled = 0;
}
}
+82
View File
@@ -0,0 +1,82 @@
import statuses;
tidy class Statuses : Component_Statuses {
array<Status@> statuses;
void getStatusEffects(Player& pl, Object& obj) {
Empire@ plEmp = pl.emp;
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
if(!statuses[i].isVisibleTo(obj, plEmp))
continue;
yield(statuses[i]);
}
}
uint get_statusEffectCount() {
return statuses.length;
}
uint get_statusEffectType(uint index) {
if(index >= statuses.length)
return uint(-1);
return statuses[index].type.id;
}
uint get_statusEffectStacks(uint index) {
if(index >= statuses.length)
return 0;
return statuses[index].stacks;
}
Object@ get_statusEffectOriginObject(uint index) {
if(index >= statuses.length)
return null;
return statuses[index].originObject;
}
Empire@ get_statusEffectOriginEmpire(uint index) {
if(index >= statuses.length)
return null;
return statuses[index].originEmpire;
}
uint getStatusStackCountAny(uint typeId) {
uint count = 0;
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
if(statuses[i].type.id == typeId)
count += statuses[i].stacks;
}
return count;
}
uint getStatusStackCount(uint typeId, Object@ originObject = null, Empire@ originEmpire = null) {
uint count = 0;
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
if(statuses[i].type.id == typeId && statuses[i].originObject is originObject && statuses[i].originEmpire is originEmpire)
count += statuses[i].stacks;
}
return count;
}
bool hasStatusEffect(uint typeId) {
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
if(statuses[i].type.id == typeId)
return true;
}
return false;
}
void readStatuses(Message& msg) {
uint cnt = msg.readSmall();
statuses.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(statuses[i] is null)
@statuses[i] = Status();
msg >> statuses[i];
}
}
void readStatusDelta(Message& msg) {
readStatuses(msg);
}
};
+81
View File
@@ -0,0 +1,81 @@
import traits;
import attitudes;
tidy class Traits : Component_Traits {
array<const Trait@> traits;
array<bool> hasTraits(getTraitCount(), false);
array<Attitude> attitudes;
ReadWriteMutex attMtx;
bool hasTrait(uint id) {
if(id >= hasTraits.length)
return false;
return hasTraits[id];
}
uint get_traitCount() const {
return traits.length;
}
uint getTraitType(uint index) const {
if(index >= traits.length)
return uint(-1);
return traits[index].id;
}
uint getAttitudeLevel(uint id) const {
ReadLock lck(attMtx);
Attitude@ att;
for(uint i = 0, cnt = attitudes.length; i < cnt; ++i) {
if(attitudes[i].type.id == id)
return attitudes[i].level;
}
return 0;
}
void readTraits(Message& msg) {
uint cnt = msg.readSmall();
traits.length = cnt;
for(uint i = 0; i < cnt; ++i) {
int id = msg.readSmall();
@traits[i] = getTrait(id);
hasTraits[id] = true;
}
}
uint get_attitudeCount() {
return attitudes.length;
}
void getAttitudes() {
ReadLock lck(attMtx);
for(uint i = 0, cnt = attitudes.length; i < cnt; ++i)
yield(attitudes[i]);
}
bool hasAttitude(uint id) {
ReadLock lck(attMtx);
for(uint i = 0, cnt = attitudes.length; i < cnt; ++i)
if(attitudes[i].type.id == id)
return true;
return false;
}
int getNextAttitudeCost(Empire& emp) {
if(emp.FreeAttitudes > 0)
return 0;
return config::ATTITUDE_BASE_COST + config::ATTITUDE_INC_COST * max(int(attitudes.length)-1, 0);
}
void readAttitudes(Message& msg, bool initial) {
uint cnt = msg.readSmall();
attitudes.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(msg.readBit())
msg >> attitudes[i];
}
}
};
+326
View File
@@ -0,0 +1,326 @@
import resources;
import constructible;
import bool getCheatsEverOn() from "cheats";
#include "include/resource_constants.as"
enum ConstructionCapability {
CC_Ship = 0x1,
CC_Orbital = 0x2,
CC_Asteroid = 0x4,
CC_Terraform = 0x8,
CC_Supports = 0x10,
};
tidy class Construction : Component_Construction {
Constructible[] queue;
uint capabilities = 0;
bool buildingSupport = false;
double LaborIncome = 0;
double LaborFactor = 0;
double DistributedLabor = 0;
double laborStorage = 0;
double storedLabor = 0;
bool canExport = true;
bool canImport = false;
int nextID = 0;
int supportSpeed = 100;
int shipCost = 100;
int orbitalCost = 100;
double orbitalMaint = 1.0;
double terraformCost = 1.0;
double constructionCost = 1.0;
bool rally = false;
bool repeating = false;
Object@ rallyObj;
vec3d rallyPoint;
double get_constructionCostMod() const {
return constructionCost;
}
bool get_canBuildSupports() {
return capabilities & CC_Supports != 0;
}
bool get_canBuildShips() {
return capabilities & CC_Ship != 0;
}
bool get_canBuildOrbitals() {
return capabilities & CC_Orbital != 0;
}
bool get_canBuildAsteroids() {
return capabilities & CC_Asteroid != 0;
}
bool get_canTerraform() {
return capabilities & CC_Terraform != 0;
}
bool get_canExportLabor() {
return canExport;
}
bool get_canImportLabor() {
return canImport;
}
double get_terraformCostMod() const {
return terraformCost;
}
uint get_constructionCount() const {
return queue.length;
}
bool get_constructingSupport() const {
return buildingSupport;
}
double get_laborIncome() const {
return LaborIncome * LaborFactor;
}
bool get_isRepeating() const {
return repeating;
}
int constructibleIndex(int id) {
for(uint i = 0, cnt = queue.length; i < cnt; ++i)
if(queue[i].id == id)
return i;
return -1;
}
string get_constructionName(uint num) {
if(num >= queue.length)
return "(null)";
return queue[num].name;
}
float get_constructionProgress() const {
if(queue.length == 0)
return -1.f;
if(queue[0].totalLabor <= 0)
return 1.f;
return queue[0].curLabor / queue[0].totalLabor;
}
int get_supportBuildSpeed(const Object& obj) const {
int cost = 100;
cost = max(10, cost + (supportSpeed - 100));
return cost;
}
int get_shipBuildCost(const Object& obj) const {
int cost = 100;
cost = max(10, cost + (shipCost - 100));
return cost;
}
int get_orbitalBuildCost(const Object& obj) const {
int cost = 100;
cost = max(10, cost + (orbitalCost - 100));
return cost;
}
double get_orbitalMaintenanceMod(const Object& obj) const {
double cost = 1.0;
cost *= clamp(orbitalMaint, 0.01f, 1.f);
return cost;
}
void getConstructionQueue() {
for(uint i = 0, cnt = queue.length; i < cnt; ++i)
yield(queue[i]);
}
void getConstructionQueue(uint limit) {
for(uint i = 0, cnt = min(queue.length, limit); i < cnt; ++i)
yield(queue[i]);
}
const Design@ get_constructionDesign() const {
if(queue.length == 0)
return null;
const Constructible@ top = queue[0];
if(top.dsg !is null)
return top.dsg;
return null;
}
void cancelConstruction(Object& obj, int id) {
int index = constructibleIndex(id);
if(index == -1)
return;
queue.removeAt(index);
}
void queueConstructible(Object& obj, Constructible@ cons) {
cons.id = nextID++;
queue.insertLast(cons);
if(queue.length == 1)
cons.started = true;
}
void moveConstruction(Object& obj, int id, int beforeId = -1) {
int myIndex = constructibleIndex(id);
if(myIndex == -1)
return;
int dropIndex = constructibleIndex(beforeId);
if(dropIndex == -1) {
queue.insertLast(queue[myIndex]);
queue.removeAt(myIndex);
}
else {
Constructible@ copy = queue[myIndex];
queue.removeAt(myIndex);
if(myIndex < dropIndex)
--dropIndex;
queue.insertAt(dropIndex, copy);
}
}
void buildOrbital(Object& obj, int type) {
/*if(capabilities & CC_Orbital == 0)*/
/* return;*/
/*const OrbitalDef@ def = getOrbitalDef(type);*/
/*Constructible cons;*/
/*cons.type = CT_Orbital;*/
/*@cons.orbital = def;*/
/*cons.buildCost = def.buildCost;*/
/*cons.totalLabor = def.laborCost;*/
/*cons.buildCost = ceil(double(cons.buildCost) * double(get_orbitalBuildCost(obj)) / 100.0);*/
/*if(def !is null)*/
/* queueConstructible(obj, cons);*/
}
void buildFlagship(Object& obj, const Design@ design) {
if(capabilities & CC_Ship == 0)
return;
if(design is null || design.hasTag(ST_IsSupport)) {
error("Invalid design for ship construction at " + obj.name);
return;
}
Constructible@ cons = Constructible(design);
getBuildCost(design, cons.buildCost, cons.maintainCost, cons.totalLabor, 1);
cons.buildCost = ceil(double(cons.buildCost) * double(get_shipBuildCost(obj)) / 100.0);
queueConstructible(obj, cons);
}
void clearRally() {
rally = false;
@rallyObj = null;
}
void rallyTo(Object& obj, Object@ dest) {
if(dest is null || !dest.valid || !dest.isVisibleTo(obj.owner))
clearRally();
rally = true;
@rallyObj = dest;
rallyPoint = dest.position;
}
void rallyTo(vec3d position) {
rally = true;
rallyPoint = position;
}
bool get_isRallying() {
return rally;
}
vec3d get_rallyPosition() {
return rallyPoint;
}
Object@ get_rallyObject() {
return rallyObj;
}
double get_laborStorageCapacity() const {
return laborStorage;
}
double get_currentLaborStored() const {
return storedLabor;
}
void constructionTick(Object& obj, double time) {
if(rally && rallyObj !is null && rallyObj.isVisibleTo(obj.owner))
rallyPoint = rallyObj.position;
if(laborIncome >= LABOR_ACHIEVE_THRESH && obj.owner is playerEmpire && !getCheatsEverOn())
unlockAchievement("ACH_LABOR200");
}
void readConstructionDelta(Message& msg) {
if(msg.readBit()) {
readConstruction(msg);
}
else {
if(msg.readBit()) {
laborStorage = msg.read_float();
storedLabor = msg.read_float();
if(msg.readBit())
return;
}
if(queue.length == 0)
queue.length = 1;
queue[0].read(msg);
}
}
void readCommon(Message& msg) {
}
void readConstruction(Message& msg) {
uint cnt = msg.readSmall();
queue.length = cnt;
for(uint i = 0; i < cnt; ++i)
queue[i].read(msg);
if(cnt > 0)
nextID = queue[cnt-1].id + 1;
msg >> rally;
if(rally) {
if(msg.readBit()) {
msg >> rallyObj;
rallyPoint = rallyObj.position;
}
else {
rallyPoint = msg.readMedVec3();
}
}
msg >> capabilities;
msg >> repeating;
msg >> buildingSupport;
LaborIncome = msg.read_float();
LaborFactor = msg.read_float();
DistributedLabor = msg.read_float();
constructionCost = msg.read_float();
msg >> canExport;
msg >> canImport;
msg >> supportSpeed;
msg >> shipCost;
msg >> orbitalCost;
terraformCost = msg.read_float();
orbitalMaint = msg.read_float();
laborStorage = msg.read_float();
storedLabor = msg.read_float();
}
}
+99
View File
@@ -0,0 +1,99 @@
import ftl;
import empire_data;
uint majorEmpireCount = 0;
uint getMajorEmpireCount() {
return majorEmpireCount;
}
void recvPeriodic(Empire& emp, Message& msg) {
if(!msg.readBit())
return;
msg >> emp.visionMask >> emp.hostileMask;
emp.cacheVision();
msg >> emp.GlobalLoyalty.value;
emp.Victory = msg.readSignedSmall();
emp.readResources(msg);
if(msg.readBit())
emp.readAbilityDelta(msg);
emp.readNotifications(msg, true);
emp.readResearch(msg);
emp.readInfluenceManager(msg);
emp.readAttributes(msg);
emp.readObjects(msg);
emp.readSyncedStates(msg);
emp.readEvents(msg);
emp.readDelta(msg);
emp.readAttitudes(msg, false);
}
void syncInitial(Empire& emp, Message& msg) {
emp.readNotifications(msg, false);
emp.readInfluenceManager(msg);
emp.readAttributes(msg);
emp.readObjects(msg);
emp.readResearch(msg);
emp.readSyncedStates(msg);
emp.readAbilities(msg);
emp.readTraits(msg);
emp.readAttitudes(msg, true);
emp.readEvents(msg);
msg >> emp.major;
msg >> emp.backgroundDef;
msg >> emp.portraitDef;
msg >> emp.flagDef;
msg >> emp.flagID;
msg >> emp.RaceName;
msg >> emp.ColonizerModel;
msg >> emp.ColonizerMaterial;
@emp.background = getMaterial(emp.backgroundDef);
auto@ flag = getEmpireFlag(emp.flagDef);
if(flag is null)
@flag = getEmpireFlag(emp.id % getEmpireFlagCount());
@emp.flag = flag.flag;
@emp.portrait = getMaterial(emp.portraitDef);
if(emp.portrait is material::error)
@emp.portrait = getEmpirePortrait(randomi(0, getEmpirePortraitCount()-1)).portrait;
}
void recvPeriodic(Message& msg) {
uint cnt = getEmpireCount();
for(uint i = 0; i < cnt; ++i)
recvPeriodic(getEmpire(i), msg);
}
void init() {
spectatorEmpire.visionMask = 0;
spectatorEmpire.ContactMask.value = int(~0);
}
void tick(double time) {
uint cnt = getEmpireCount();
for(uint i = 0; i < cnt; ++i) {
Empire@ emp = getEmpire(i);
emp.influenceTick(time);
}
}
void syncInitial(Message& msg) {
uint cnt = getEmpireCount();
for(uint i = 0; i < cnt; ++i) {
Empire@ emp = getEmpire(i);
syncInitial(emp, msg);
if(emp.major)
++majorEmpireCount;
}
}
void allowPlayEmpire(Empire@ emp) {
if(emp is spectatorEmpire)
spectatorEmpire.visionMask = ~0;
@playerEmpire = emp;
CURRENT_PLAYER.linkEmpire(emp);
playingEmpire(emp);
}
+17
View File
@@ -0,0 +1,17 @@
tidy class EmpireAI : Component_EmpireAI {
vec3d get_aiFocus() {
return vec3d();
}
bool get_isAI(Empire& emp) {
return false;
}
string getRelation(Player& pl, Empire& emp) {
return "";
}
int getRelationState(Player& pl, Empire& emp) {
return 0;
}
};
+132
View File
@@ -0,0 +1,132 @@
import settings.map_lib;
import maps;
import regions.regions;
SystemDesc[] systems;
Map@[] galaxies;
void getSystems() {
uint cnt = systems.length;
for(uint i = 0; i < cnt; ++i)
yield(systems[i]);
}
uint get_systemCount() {
return systems.length;
}
SystemDesc@ getSystem(uint index) {
if(index >= systems.length)
return null;
return systems[index];
}
SystemDesc@ getSystem(Region@ region) {
if(region.SystemId == -1 || region.SystemId >= int(systems.length))
return null;
return systems[region.SystemId];
}
SystemDesc@ getSystem(const string& name) {
//TODO: Use dictionary
uint cnt = systemCount;
for(uint i = 0; i < cnt; ++i) {
if(getSystem(i).name == name)
return getSystem(i);
}
return null;
}
void syncInitial(Message& msg) {
//Read systems
uint cnt = 0;
msg >> cnt;
systems.length = cnt;
for(uint i = 0; i < cnt; ++i)
systems[i].read(msg);
//Set player empire
if(playerEmpire is null) {
CURRENT_PLAYER.linkEmpire(spectatorEmpire);
@playerEmpire = spectatorEmpire;
}
//Read maps
msg >> cnt;
galaxies.length = cnt;
for(uint i = 0; i < cnt; ++i) {
string ident;
msg >> ident;
@galaxies[i] = getMap(ident).create();
}
//Generate gas nodes & sprites
msg >> cnt;
for(uint i = 0; i < cnt; ++i) {
GalaxyGas@ gas = GalaxyGas();
gas.position = msg.readSmallVec3();
gas.scale = msg.read_float();
if(msg.readBit()) {
vec3d origin = msg.readSmallVec3();
double radius = msg.read_float();
Node@ parent = createCullingNode(origin, radius);
gas.reparent(parent);
}
gas.rebuildTransform();
vec3d pos;
float size = 0;
uint col = 0;
uint spriteCount = msg.readSmall();
for(uint s = 0; s < spriteCount; ++s) {
pos = msg.readSmallVec3();
size = msg.read_float();
msg >> col;
bool structured = msg.readBit();
gas.addSprite(pos, size, col, structured);
}
}
}
void recvPeriodic(Message& msg) {
SystemUpdate upd;
uint cnt = 0;
msg >> cnt;
upd.systems.length = cnt;
for(uint i = 0; i < cnt; ++i)
upd.systems[i].read(msg);
isolate_run(upd);
}
class SystemUpdate : IsolateHook {
SystemDesc[] systems;
void call() {
uint oldCnt = ::systems.length;
::systems.length = systems.length;
for(uint i = 0, cnt = systems.length; i < cnt; ++i)
::systems[i] = systems[i];
for(uint i = oldCnt, cnt = systems.length; i < cnt; ++i)
addRegion(::systems[i].object);
calcGalaxyExtents();
regenerateRegionGroups();
refreshClientSystems();
}
}
void init() {
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i)
galaxies[i].initDefs();
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i)
galaxies[i].init();
}
void tick(double time) {
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i)
galaxies[i].tick(time);
}
+330
View File
@@ -0,0 +1,330 @@
from empire import majorEmpireCount;
import influence;
// {{{ Galactic Influence
int galacticInfluence = 0;
Empire@ SenateLeader;
Empire@ getSenateLeader() {
return SenateLeader;
}
double getInfluenceIncome(int stock, int stored, double factor) {
int total = max(galacticInfluence, 0);
if(stock == 0 || total == 0)
return 0;
double pct = double(stock) / double(total);
//Per budget cycle, distribute 6 points per empire in the game, but don't distribute more than 1 per influence generation
return min(double(stock) * config::INFLUENCE_STAKE_MAX + ceil(sqrt(double(stock))),
double(majorEmpireCount) * config::INFLUENCE_PER_EMPIRE * pct) / 180.0
* getInfluenceEfficiency(stock, stored) * factor;
}
double getInfluenceEfficiency(int stock, int stored) {
double storage = getInfluenceStorage(stock);
if(stored > storage)
return storage / double(stored);
return 1.0;
}
double getInfluenceStorage(int stock) {
return double(stock + ceil(sqrt(double(stock)))) * config::INFLUENCE_STAKE_STORE;
}
double getInfluencePercentage(int amt) {
int total = max(galacticInfluence, 0);
if(total == 0)
return 0.0;
return double(amt) / double(total);
}
double getInfluencePercentage(Empire& emp) {
double totalGen = 0.0, myGen = 0.0;
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
Empire@ other = getEmpire(i);
if(!other.major)
continue;
double gen = getInfluenceIncome(other.getInfluenceStock(), other.Influence, other.InfluenceFactor);
totalGen += gen;
if(emp is other)
myGen = gen;
}
if(totalGen == 0)
return 0.0;
return double(myGen) / double(totalGen);
}
void objectRenamed(Object@ obj, string name, bool setNamed = true) {
obj.name = name;
if(setNamed)
obj.named = true;
}
// }}}
// {{{ Cards
Mutex stackMtx;
array<StackInfluenceCard@> cardStack;
double drawInterval;
double drawTimer = 0.0;
double getInfluenceDrawInterval() {
return drawInterval;
}
double getInfluenceDrawTimer() {
return drawTimer;
}
void readStack(Message& msg, bool initial = false) {
Lock lock(stackMtx);
if(initial)
msg >> drawInterval;
msg >> drawTimer;
if(msg.readBit()) {
uint cnt = 0;
msg >> cnt;
cardStack.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(cardStack[i] is null)
@cardStack[i] = StackInfluenceCard(msg);
else
msg >> cardStack[i];
}
}
}
void getInfluenceCardStack() {
Lock lock(stackMtx);
for(uint i = 0; i < cardStack.length; ++i)
yield(cardStack[i]);
}
// }}}
// {{{ Votes
Mutex voteMtx;
array<InfluenceVote@> voteList;
array<InfluenceVote@> activeVotes;
void readVotes(Message& msg, bool initial = false) {
Lock lock(voteMtx);
msg.readAlign();
uint deltas = 0;
msg >> deltas;
for(uint i = 0; i < deltas; ++i) {
uint index = 0;
msg >> index;
if(index >= voteList.length)
voteList.length = index+1;
if(voteList[index] is null)
@voteList[index] = InfluenceVote(msg);
else
msg >> voteList[index];
}
if(msg.readBit()) {
uint cnt = 0;
msg >> cnt;
activeVotes.length = cnt;
for(uint i = 0; i < cnt; ++i) {
uint id = 0;
msg >> id;
@activeVotes[i] = voteList[id];
}
}
}
void tickVotes(double time) {
Lock lock(voteMtx);
for(uint i = 0, cnt = activeVotes.length; i < cnt; ++i) {
if(activeVotes[i] !is null && activeVotes[i].active)
activeVotes[i].tick(time);
}
}
InfluenceVote@ getInfluenceVoteByID(uint id) {
Lock lock(voteMtx);
if(id >= voteList.length)
return null;
return voteList[id];
}
void getInfluenceVoteByID_client(Player& pl, uint id) {
Empire@ plEmp = pl.emp;
if(plEmp is null)
return;
if(plEmp is spectatorEmpire)
@plEmp = null;
Lock lock(voteMtx);
if(id >= voteList.length)
return;
if(voteList[id] is null)
return;
voteList[id].write(startYield(), plEmp);
finishYield();
}
void getActiveInfluenceVotes_client(Player& pl) {
Empire@ plEmp = pl.emp;
if(plEmp is null)
return;
if(plEmp is spectatorEmpire)
@plEmp = null;
Lock lock(voteMtx);
for(uint i = 0, cnt = activeVotes.length; i < cnt; ++i) {
auto@ vote = activeVotes[i];
if(vote !is null && (plEmp is null || vote.isPresent(plEmp))) {
activeVotes[i].write(startYield(), plEmp);
finishYield();
}
}
}
void getInfluenceVoteHistory_client(Player& player, uint limit, int beforeId = -1, bool reverse = true) {
Empire@ plEmp = player.emp;
if(plEmp is null)
return;
Lock lock(voteMtx);
if(reverse) {
if(beforeId == -1 || beforeId > int(voteList.length))
beforeId = voteList.length;
if(beforeId == 0)
return;
for(int i = beforeId - 1; i >= 0; --i) {
InfluenceVote@ vote = voteList[i];
if(vote.active)
continue;
vote.write(startYield(), plEmp);
finishYield();
if(--limit == 0)
break;
}
}
else {
int cnt = voteList.length;
if(beforeId == -1 || beforeId > cnt)
beforeId = 0;
if(beforeId >= cnt - 1)
return;
for(int i = beforeId + 1; i < cnt; ++i) {
InfluenceVote@ vote = voteList[i];
if(vote.active)
continue;
vote.write(startYield(), plEmp);
finishYield();
if(--limit == 0)
break;
}
}
}
// }}}
// {{{ Effects
Mutex effectMtx;
array<InfluenceEffect@> activeEffects;
void readEffects(Message& msg, bool initial = false) {
Lock lock(effectMtx);
uint cnt = 0;
msg >> cnt;
activeEffects.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(activeEffects[i] is null)
@activeEffects[i] = InfluenceEffect();
if(msg.readBit())
msg >> activeEffects[i];
}
}
void tickEffects(double time) {
Lock lock(effectMtx);
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i) {
if(activeEffects[i].active)
activeEffects[i].tick(time);
}
}
void getActiveInfluenceEffects_client() {
Lock lock(effectMtx);
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i)
yield(activeEffects[i]);
}
Empire@ getInfluenceEffectOwner(int id) {
Lock lock(effectMtx);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.id == id)
return effect.owner;
}
return null;
}
bool canDismissInfluenceEffect(int id, Empire@ emp = null) {
Lock lock(effectMtx);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.id == id) {
if(emp is null)
@emp = effect.owner;
return effect.canDismiss(emp);
}
}
return false;
}
// }}}
// {{{ Treaties
Mutex treatyMtx;
array<Treaty@> activeTreaties;
void readTreaties(Message& msg, bool initial = false) {
Lock lock(treatyMtx);
uint cnt = 0;
msg >> cnt;
activeTreaties.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(activeTreaties[i] is null)
@activeTreaties[i] = Treaty();
if(msg.readBit())
msg >> activeTreaties[i];
}
}
void getActiveInfluenceTreaties_client(Player& pl) {
Empire@ plEmp = pl.emp;
Lock lock(treatyMtx);
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
if(activeTreaties[i].isVisibleTo(plEmp))
yield(activeTreaties[i]);
}
}
// }}}
void syncInitial(Message& msg) {
msg >> galacticInfluence;
msg >> SenateLeader;
readStack(msg, true);
readVotes(msg, true);
readEffects(msg, true);
readTreaties(msg, true);
}
void recvPeriodic(Message& msg) {
msg >> galacticInfluence;
msg >> SenateLeader;
readStack(msg);
readVotes(msg);
readEffects(msg);
readTreaties(msg);
}
void tick(double time) {
tickVotes(time);
tickEffects(time);
}
+146
View File
@@ -0,0 +1,146 @@
import resources;
import regions.regions;
import saving;
import anomalies;
tidy class AnomalyScript {
const AnomalyType@ type;
const AnomalyState@ state;
array<const AnomalyOption@> options;
array<float> progresses(getEmpireCount(), 0);
StrategicIconNode@ icon;
float get_progress(Player& player, const Anomaly& obj) {
if(player.emp is null || !player.emp.valid)
return 0.0;
else
return progresses[player.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(get_progress(player, obj) < 1.f || type is null)
return 0;
else
return type.id;
}
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;
}
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 postInit(Anomaly& 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);
}
double tick(Anomaly& 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;
}
icon.visible = obj.isVisibleTo(playerEmpire);
return 0.2;
}
void readProgress(Message& msg) {
for(uint i = 0; i < progresses.length; ++i) {
if(msg.readBit())
progresses[i] = msg.readFixed(0.0, 1.0, 7);
else
progresses[i] = 0.0;
}
}
void readChoices(Message& msg) {
@type = getAnomalyType(msg.readSmall());
@state = type.states[msg.readSmall()];
options.length = msg.readSmall();
for(uint i = 0; i < options.length; ++i)
@options[i] = type.options[msg.readLimited(type.options.length)];
}
void syncInitial(Anomaly& obj, Message& msg) {
readChoices(msg);
readProgress(msg);
makeMesh(obj);
}
void syncDelta(Anomaly& obj, Message& msg, double tDiff) {
readProgress(msg);
if(msg.readBit())
readChoices(msg);
}
void syncDetailed(Anomaly& obj, Message& msg, double tDiff) {
readProgress(msg);
readChoices(msg);
}
};
+86
View File
@@ -0,0 +1,86 @@
import artifacts;
import regions.regions;
tidy class ArtifactScript {
const ArtifactType@ type;
StrategicIconNode@ icon;
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)
obj.region.addStrategicIcon(-1, obj, icon);
}
}
void destroy(Artifact& obj) {
if(icon !is null) {
if(obj.region !is null)
obj.region.removeStrategicIcon(-1, icon);
icon.markForDeletion();
@icon = null;
}
leaveRegion(obj);
}
bool onOwnerChange(Artifact& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
return false;
}
double tick(Artifact& 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;
}
icon.visible = obj.isVisibleTo(playerEmpire);
obj.orbitTick(time);
obj.abilityTick(time);
return 0.2;
}
vec3d get_strategicIconPosition(Artifact& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
void syncInitial(Artifact& obj, Message& msg) {
@type = getArtifactType(msg.readSmall());
obj.ArtifactType = type.id;
obj.readOrbit(msg);
obj.readAbilities(msg);
makeMesh(obj);
}
void syncDelta(Artifact& obj, Message& msg, double tDiff) {
if(msg.readBit())
obj.readAbilityDelta(msg);
if(msg.readBit())
obj.readOrbitDelta(msg);
}
void syncDetailed(Artifact& obj, Message& msg, double tDiff) {
obj.readOrbit(msg);
obj.readAbilities(msg);
}
};
+199
View File
@@ -0,0 +1,199 @@
import resources;
import regions.regions;
import saving;
tidy class AsteroidScript {
StrategicIconNode@ icon;
MeshNode@ baseNode;
array<const ResourceType@> available;
array<float> costs;
array<bool> exploited;
uint resourceLimit = 1;
uint currentResources = 0;
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;
bindMesh(obj, mesh);
@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;
if(obj.region !is null)
obj.region.addStrategicIcon(-1, obj, icon);
bool hasBase = obj.owner !is null && obj.owner.valid;
if(hasBase && baseNode is null) {
@baseNode = MeshNode(model::MiningBase, material::GenericPBR_MiningBase);
nodeSyncObject(baseNode, obj);
}
}
void destroy(Asteroid& obj) {
if(obj.region !is null)
obj.region.removeStrategicIcon(-1, icon);
icon.markForDeletion();
@icon = null;
if(baseNode !is null) {
baseNode.markForDeletion();
@baseNode = null;
}
leaveRegion(obj);
obj.destroyObjResources();
}
bool onOwnerChange(Asteroid& obj, Empire@ prevOwner) {
regionOwnerChange(obj, 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;
}
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;
}
icon.visible = obj.isVisibleTo(playerEmpire);
obj.orbitTick(time);
obj.resourceTick(time);
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 currentResources < available.length;
}
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 _readAsteroid(Asteroid& obj, Message& msg) {
@obj.origin = msg.readObject();
uint cnt = msg.readSmall();
available.length = cnt;
costs.length = cnt;
exploited.length = cnt;
for(uint i = 0; i < cnt; ++i) {
@available[i] = getResource(msg.readLimited(getResourceCount()-1));
msg >> costs[i];
msg >> exploited[i];
}
resourceLimit = msg.readSmall();
currentResources = msg.readSmall();
}
void syncInitial(Asteroid& obj, Message& msg) {
_readAsteroid(obj, msg);
obj.readResources(msg);
obj.readCargo(msg);
obj.readOrbit(msg);
makeMesh(obj);
}
void syncDelta(Asteroid& obj, Message& msg, double tDiff) {
if(msg.readBit())
_readAsteroid(obj, msg);
if(msg.readBit())
obj.readResourceDelta(msg);
if(msg.readBit())
obj.readCargoDelta(msg);
if(msg.readBit())
obj.readOrbitDelta(msg);
}
void syncDetailed(Asteroid& obj, Message& msg, double tDiff) {
_readAsteroid(obj, msg);
obj.readResources(msg);
obj.readCargo(msg);
obj.readOrbit(msg);
}
};
+116
View File
@@ -0,0 +1,116 @@
import regions.regions;
import resources;
import civilians;
const double CIV_HEALTH = 25.0;
tidy class CivilianScript {
uint type = 0;
uint cargoType = 0;
const ResourceType@ cargoResource;
int cargoWorth = 0;
bool pickedUp = false;
double Health = 0;
uint getCivilianType() {
return type;
}
double get_health() {
return Health;
}
double get_maxHealth(const Civilian& obj) {
return CIV_HEALTH * obj.radius;
}
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 init(Civilian& obj) {
}
void destroy(Civilian& obj) {
leaveRegion(obj);
}
bool onOwnerChange(Civilian& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
return false;
}
double tick(Civilian& obj, double time) {
updateRegion(obj);
if(obj.hasMover)
obj.moverTick(time);
return 0.2;
}
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);
}
void _readDelta(Civilian& obj, Message& msg) {
cargoType = msg.readSmall();
cargoWorth = msg.readSmall();
pickedUp = msg.readBit();
Health = obj.maxHealth * msg.readFixed();
if(msg.readBit()) {
uint id = msg.readLimited(getResourceCount()-1);
@cargoResource = getResource(id);
}
else {
@cargoResource = null;
}
}
void syncInitial(Civilian& obj, Message& msg) {
if(msg.readBit()) {
if(!obj.hasMover)
obj.activateMover();
obj.readMover(msg);
}
msg >> type;
_readDelta(obj, msg);
makeMesh(obj);
}
void syncDetailed(Civilian& obj, Message& msg, double tDiff) {
if(msg.readBit()) {
if(!obj.hasMover)
obj.activateMover();
obj.readMover(msg);
}
_readDelta(obj, msg);
}
void syncDelta(Civilian& obj, Message& msg, double tDiff) {
if(msg.readBit()) {
if(!obj.hasMover)
obj.activateMover();
obj.readMoverDelta(msg);
}
if(msg.readBit())
_readDelta(obj, msg);
}
};
+66
View File
@@ -0,0 +1,66 @@
import regions.regions;
tidy class ColonyShipScript {
ColonyShipScript() {
}
void init(ColonyShip& ship) {
//Create the graphics
makeMesh(ship);
}
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 destroy(ColonyShip& ship) {
leaveRegion(ship);
}
bool onOwnerChange(ColonyShip& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
return false;
}
double tick(ColonyShip& ship, double time) {
updateRegion(ship);
ship.moverTick(time);
return 0.2;
}
void syncInitial(ColonyShip& ship, Message& msg) {
ship.readMover(msg);
}
void syncDetailed(ColonyShip& ship, Message& msg, double tDiff) {
ship.readMover(msg);
}
void syncDelta(ColonyShip& ship, Message& msg, double tDiff) {
if(msg.readBit())
ship.readMoverDelta(msg);
}
};
+63
View File
@@ -0,0 +1,63 @@
import regions.regions;
tidy class FreighterScript {
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 init(Freighter& ship) {
//Create the graphics
makeMesh(ship);
}
void destroy(Freighter& ship) {
leaveRegion(ship);
}
bool onOwnerChange(Freighter& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
return false;
}
double tick(Freighter& ship, double time) {
updateRegion(ship);
ship.moverTick(time);
return 0.2;
}
void syncInitial(Freighter& ship, Message& msg) {
msg >> ship.skin;
ship.readMover(msg);
}
void syncDetailed(Freighter& ship, Message& msg, double tDiff) {
ship.readMover(msg);
}
void syncDelta(Freighter& ship, Message& msg, double tDiff) {
if(msg.readBit())
ship.readMoverDelta(msg);
}
};
+109
View File
@@ -0,0 +1,109 @@
import regions.regions;
import oddity_navigation;
import oddities;
tidy class OddityScript {
StrategicIconNode@ icon;
bool gate = false;
double timer = -1.0;
uint visualType = uint(-1);
uint visualColor = 0xffffffff;
Object@ link;
bool isGate() {
return gate;
}
vec3d getGateDest() {
if(link !is null)
return link.position;
return vec3d();
}
Object@ getLink() {
return link;
}
double getTimer() {
return timer;
}
vec3d get_strategicIconPosition(Oddity& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
void _read(Oddity& obj, Message& msg) {
uint prevVisual = visualType;
bool prevGate = gate;
msg >> gate;
msg >> timer;
msg >> visualType;
msg >> visualColor;
msg >> link;
if(prevVisual == uint(-1) && visualType != uint(-1)) {
if(link !is null)
obj.rotation = quaterniond_fromVecToVec(vec3d_front(), (link.position - obj.position).normalized(), vec3d_up());
makeVisuals(obj, visualType, color=visualColor);
}
if(gate != prevGate) {
if(gate)
addOddityGate(obj);
else
removeOddityGate(obj);
}
}
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);
}
@prevRegion = newRegion;
}
//Handle timer
if(timer > 0.0)
timer = max(timer - time, 0.0);
return 0.25;
}
void syncInitial(Oddity& obj, Message& msg) {
_read(obj, msg);
}
void syncDelta(Oddity& obj, Message& msg, double tDiff) {
if(msg.readBit())
_read(obj, msg);
}
void syncDetailed(Oddity& obj, Message& msg, double tDiff) {
_read(obj, msg);
}
void makeVisuals(Oddity& obj, uint type, bool fromCreation = true, uint color = 0xffffffff) {
visualType = type;
@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);
if(gate)
removeOddityGate(obj);
removeAmbientSource(obj.id);
}
};
+379
View File
@@ -0,0 +1,379 @@
import regions.regions;
from resources import MoneyType;
import orbitals;
import saving;
const int STRATEGIC_RING = -1;
const double RECOVERY_TIME = 3.0 * 60.0;
tidy class OrbitalScript {
OrbitalNode@ node;
StrategicIconNode@ icon;
OrbitalSection@ core;
array<OrbitalSection@> sections;
int nextSectionId = 1;
int contestion = 0;
bool disabled = false;
Orbital@ master;
double Health = 0;
double MaxHealth = 0;
double Armor = 0;
double MaxArmor = 0;
double DR = 2.5;
double DPS = 0;
Orbital@ getMaster() {
return master;
}
bool hasMaster() {
return master !is null;
}
bool isMaster(Object@ obj) {
return master is obj;
}
double get_health(Orbital& orb) {
double v = Health;
Empire@ owner = orb.owner;
if(owner !is null)
v *= owner.OrbitalHealthMod;
return v;
}
double get_maxHealth(Orbital& orb) {
double v = MaxHealth;
Empire@ owner = orb.owner;
if(owner !is null)
v *= owner.OrbitalHealthMod;
return v;
}
double get_armor(Orbital& orb) {
double v = Armor;
Empire@ owner = orb.owner;
if(owner !is null)
v *= owner.OrbitalArmorMod;
return v;
}
double get_maxArmor(Orbital& orb) {
double v = MaxArmor;
Empire@ owner = orb.owner;
if(owner !is null)
v *= owner.OrbitalArmorMod;
return v;
}
double get_dps() {
return DPS;
}
double get_efficiency() {
return clamp(Health / max(1.0, MaxHealth), 0.0, 1.0);
}
double getValue(Player& pl, Orbital& obj, uint id) {
double value = 0.0;
for(uint i = 0, cnt = sections.length; i < cnt; ++i) {
auto@ sec = sections[i];
for(uint j = 0, jcnt = sec.type.hooks.length; j < jcnt; ++j) {
if(sec.type.hooks[j].getValue(pl, obj, sec.data[j], id, value))
return value;
}
}
return 0.0;
}
const Design@ getDesign(Player& pl, Orbital& obj, uint id) {
const Design@ value;
for(uint i = 0, cnt = sections.length; i < cnt; ++i) {
auto@ sec = sections[i];
for(uint j = 0, jcnt = sec.type.hooks.length; j < jcnt; ++j) {
if(sec.type.hooks[j].getDesign(pl, obj, sec.data[j], id, value))
return value;
}
}
return null;
}
Object@ getObject(Player& pl, Orbital& obj, uint id) {
Object@ value;
for(uint i = 0, cnt = sections.length; i < cnt; ++i) {
auto@ sec = sections[i];
if(!sec.enabled)
continue;
for(uint j = 0, jcnt = sec.type.hooks.length; j < jcnt; ++j) {
if(sec.type.hooks[j].getObject(pl, obj, sec.data[j], id, value))
return value;
}
}
return null;
}
void getSections() {
for(uint i = 0, cnt = sections.length; i < cnt; ++i)
yield(sections[i]);
}
bool hasModule(uint typeId) {
for(uint i = 0, cnt = sections.length; i < cnt; ++i) {
auto@ sec = sections[i];
if(sec.type.id == typeId)
return true;
}
return false;
}
uint get_coreModule() {
auto@ mod = core;
if(mod is null)
return uint(-1);
return mod.type.id;
}
bool get_isStandalone() {
auto@ mod = core;
if(mod is null)
return true;
return mod.type.isStandalone;
}
bool get_isContested() {
return contestion != 0;
}
bool get_isDisabled() {
return disabled || (core !is null && !core.enabled);
}
void destroy(Orbital& obj) {
if(icon !is null) {
if(obj.region !is null)
obj.region.removeStrategicIcon(STRATEGIC_RING, icon);
icon.markForDeletion();
@icon = null;
}
@node = null;
leaveRegion(obj);
obj.destroyObjResources();
if(obj.hasConstruction)
obj.destroyConstruction();
if(obj.hasAbilities)
obj.destroyAbilities();
}
bool onOwnerChange(Orbital& obj, Empire@ prevOwner) {
regionOwnerChange(obj, prevOwner);
obj.changeResourceOwner(prevOwner);
return false;
}
float timer = 0.f;
double prevFleet = 0.0;
void occasional_tick(Orbital& obj) {
Region@ prevRegion = obj.region;
if(updateRegion(obj)) {
Region@ newRegion = obj.region;
if(icon !is null) {
if(prevRegion !is null)
prevRegion.removeStrategicIcon(STRATEGIC_RING, icon);
if(newRegion !is null)
newRegion.addStrategicIcon(STRATEGIC_RING, obj, icon);
}
obj.changeResourceRegion(prevRegion, newRegion);
@prevRegion = newRegion;
}
if(icon !is null)
icon.visible = obj.isVisibleTo(playerEmpire);
if(node !is null) {
double rad = 0.0;
if(obj.hasLeaderAI && obj.SupplyCapacity > 0)
rad = obj.getFormationRadius();
if(rad != prevFleet) {
node.setFleetPlane(rad);
prevFleet = rad;
}
}
if(obj.hasLeaderAI)
obj.updateFleetStrength();
}
vec3d get_strategicIconPosition(const Orbital& obj) {
if(icon is null)
return obj.position;
return icon.position;
}
double tick(Orbital& obj, double time) {
//Tick construction
double delay = 0.2;
if(obj.hasConstruction) {
obj.constructionTick(time);
//if(obj.hasConstructionUnder(0.2))
// delay = 0.0;
}
if(obj.hasAbilities)
obj.abilityTick(time);
//Tick resources
obj.resourceTick(time);
//Tick orbit
obj.moverTick(time);
//Tick occasional stuff
timer -= float(time);
if(timer <= 0.f) {
occasional_tick(obj);
timer = 1.f;
}
return delay;
}
void _read(Orbital& obj, Message& msg) {
uint cnt = msg.readSmall();
sections.length = cnt;
for(uint i = 0; i < cnt; ++i) {
if(sections[i] is null)
@sections[i] = OrbitalSection();
msg >> sections[i];
}
if(core is null && sections.length != 0) {
@core = sections[0];
auto@ type = core.type;
@node = cast<OrbitalNode>(bindNode(obj, "OrbitalNode"));
if(node !is null)
node.establish(obj, type.id);
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);
if(obj.region !is null)
obj.region.addStrategicIcon(STRATEGIC_RING, obj, icon);
}
}
msg >> contestion;
msg >> disabled;
msg >> master;
}
void _readHP(Orbital& obj, Message& msg) {
msg >> Health;
msg >> MaxHealth;
msg >> Armor;
msg >> MaxArmor;
msg >> DR;
msg >> DPS;
}
void syncInitial(Orbital& obj, Message& msg) {
_read(obj, msg);
_readHP(obj, msg);
obj.readResources(msg);
obj.readOrbit(msg);
obj.readStatuses(msg);
obj.readMover(msg);
if(msg.readBit()) {
if(!obj.hasConstruction)
obj.activateConstruction();
obj.readConstruction(msg);
}
if(msg.readBit()) {
if(!obj.hasLeaderAI)
obj.activateLeaderAI();
obj.readLeaderAI(msg);
}
if(msg.readBit()) {
if(!obj.hasAbilities)
obj.activateAbilities();
obj.readAbilities(msg);
}
if(msg.readBit()) {
if(!obj.hasCargo)
obj.activateCargo();
obj.readCargo(msg);
}
}
void syncDelta(Orbital& obj, Message& msg, double tDiff) {
if(msg.readBit())
_read(obj, msg);
if(msg.readBit())
_readHP(obj, msg);
if(msg.readBit())
obj.readOrbit(msg);
if(msg.readBit())
obj.readResourceDelta(msg);
if(msg.readBit()) {
if(!obj.hasConstruction)
obj.activateConstruction();
obj.readConstructionDelta(msg);
}
if(msg.readBit()) {
if(!obj.hasLeaderAI)
obj.activateLeaderAI();
obj.readLeaderAIDelta(msg);
}
if(msg.readBit()) {
if(!obj.hasAbilities)
obj.activateAbilities();
obj.readAbilityDelta(msg);
}
if(msg.readBit()) {
if(!obj.hasCargo)
obj.activateCargo();
obj.readCargoDelta(msg);
}
if(msg.readBit())
obj.readStatusDelta(msg);
if(msg.readBit())
obj.readOrbitDelta(msg);
if(msg.readBit())
obj.readMoverDelta(msg);
}
void syncDetailed(Orbital& obj, Message& msg, double tDiff) {
_read(obj, msg);
_readHP(obj, msg);
obj.readResources(msg);
obj.readOrbit(msg);
obj.readStatuses(msg);
obj.readMover(msg);
if(msg.readBit()) {
if(!obj.hasConstruction)
obj.activateConstruction();
obj.readConstruction(msg);
}
if(msg.readBit()) {
if(!obj.hasLeaderAI)
obj.activateLeaderAI();
obj.readLeaderAI(msg);
}
if(msg.readBit()) {
if(!obj.hasAbilities)
obj.activateAbilities();
obj.readAbilities(msg);
}
if(msg.readBit()) {
if(!obj.hasCargo)
obj.activateCargo();
obj.readCargo(msg);
}
}
};
+83
View File
@@ -0,0 +1,83 @@
import pickups;
import regions.regions;
tidy class PickupScript {
void destroy(Pickup& obj) {
leaveRegion(obj);
}
void syncInitial(Pickup& obj, Message& msg) {
msg >> obj.PickupType;
obj.readPickup(msg);
obj.initPickup();
}
void syncDelta(Pickup& obj, Message& msg, double tDiff) {
obj.readPickup(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;
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 initPickup(Object& obj) {
Pickup@ pickup = cast<Pickup>(obj);
@type = getPickupType(pickup.PickupType);
generateMesh(obj);
}
Object@ getProtector() {
if(protectors.length == 0)
return null;
return protectors[0];
}
bool get_isPickupProtected() {
return protectors.length != 0;
}
void tickPickup(Object& pickup, double time) {
if(protectors.length != 0) {
if(offset.zero)
offset = protectors[0].position - pickup.position;
auto@ prot = protectors[0];
pickup.position = prot.position + offset;
pickup.velocity = prot.velocity;
pickup.acceleration = prot.acceleration;
}
}
void readPickup(Object& obj, Message& msg) {
uint cnt = 0, prevCnt = protectors.length;
msg >> cnt;
protectors.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> protectors[i];
}
};
+176
View File
@@ -0,0 +1,176 @@
import planet_types;
import regions.regions;
tidy class MoonData {
uint style = 0;
float size = 0.f;
};
tidy class PlanetScript {
double tickTimer = randomd(-0.2,0.2);
array<MoonData@>@ moons;
void destroy(Planet& planet) {
planet.destroySurface();
leaveRegion(planet);
planet.destroyObjResources();
}
bool onOwnerChange(Planet& planet, Empire@ prevOwner) {
regionOwnerChange(planet, prevOwner);
return false;
}
double tick(Planet& planet, double time) {
//Update region
Region@ prevRegion = planet.region;
if(updateRegion(planet)) {
planet.changeResourceRegion(prevRegion, planet.region);
planet.changeSurfaceRegion(prevRegion, planet.region);
auto@ node = planet.getNode();
if(node !is null)
node.hintParentObject(planet.region);
}
tickTimer += time;
if(tickTimer >= 1.f) {
tickTimer = 0.f;
planet.updateFleetStrength();
}
if(planet.hasMover)
planet.moverTick(time);
else
planet.orbitTick(time);
planet.resourceTick(time);
planet.surfaceTick(time);
planet.constructionTick(time);
if(planet.hasAbilities)
planet.abilityTick(time);
return 0.2;
}
void syncInitial(Planet& planet, Message& msg) {
//Read planet data
planet.Health = msg.read_float();
planet.MaxHealth = msg.read_float();
planet.PlanetType = msg.readSmall();
msg >> planet.renamed;
msg >> planet.OrbitSize;
planet.readResources(msg);
planet.readSurface(msg);
planet.readOrbit(msg);
planet.Population = planet.population;
//Create graphics
PlanetNode@ plNode = cast<PlanetNode>(bindNode(planet, "PlanetNode"));
plNode.establish(planet);
plNode.flags = planet.planetGraphicsFlags;
planet.readLeaderAI(msg);
planet.readStatuses(msg);
planet.readCargo(msg);
if(msg.readBit()) {
if(!planet.hasAbilities)
planet.activateAbilities();
planet.readAbilities(msg);
}
uint ringStyle = 0;
if(msg.readBit())
msg >> ringStyle;
if(plNode !is null) {
plNode.planetType = planet.PlanetType;
plNode.colonized = planet.owner.valid;
if(ringStyle != 0)
plNode.addRing(ringStyle);
plNode.hintParentObject(planet.region);
}
if(msg.readBit()) {
if(!planet.hasMover)
planet.activateMover();
planet.readMover(msg);
}
if(msg.readBit()) {
if(moons is null)
@moons = array<MoonData@>();
moons.length = msg.readSmall();
for(uint i = 0, cnt = moons.length; i < cnt; ++i) {
MoonData dat;
msg >> dat.size;
msg >> dat.style;
@moons[i] = dat;
if(plNode !is null)
plNode.addMoon(dat.size, dat.style);
}
}
}
void syncDelta(Planet& planet, Message& msg, double tDiff) {
if(msg.readBit())
planet.readResourceDelta(msg);
if(msg.readBit()) {
planet.readSurfaceDelta(msg);
planet.Population = planet.population;
}
if(msg.readBit())
planet.readConstructionDelta(msg);
if(msg.readBit())
planet.readLeaderAIDelta(msg);
if(msg.readBit()) {
if(!planet.hasAbilities)
planet.activateAbilities();
planet.readAbilityDelta(msg);
}
if(msg.readBit())
planet.readStatusDelta(msg);
if(msg.readBit())
planet.readCargoDelta(msg);
if(msg.readBit()) {
planet.Health = msg.read_float();
planet.MaxHealth = msg.read_float();
}
if(msg.readBit()) {
if(!planet.hasMover)
planet.activateMover();
planet.readMoverDelta(msg);
}
if(msg.readBit())
planet.readOrbitDelta(msg);
}
void syncDetailed(Planet& planet, Message& msg, double tDiff) {
planet.Health = msg.read_float();
planet.MaxHealth = msg.read_float();
planet.readResources(msg);
planet.readSurface(msg);
planet.readConstruction(msg);
planet.readStatuses(msg);
planet.readCargo(msg);
if(msg.readBit()) {
if(!planet.hasAbilities)
planet.activateAbilities();
planet.readAbilities(msg);
}
planet.Population = planet.population;
if(msg.readBit()) {
if(!planet.hasMover)
planet.activateMover();
planet.readMover(msg);
}
}
uint get_moonCount() {
if(moons is null)
return 0;
return moons.length;
}
};
+249
View File
@@ -0,0 +1,249 @@
import regions.regions;
from designs import getDesignMesh;
tidy class ShipScript {
float commandUsed = 0.f;
float timer = 1.f;
bool hasGraphics = false;
bool get_isStation(Ship& ship) {
return ship.blueprint.design.hasTag(ST_Station);
}
void occasional_tick(Ship& ship, float time) {
if(ship.hasLeaderAI)
ship.updateFleetStrength();
}
double tick(Ship& ship, double time) {
if(updateRegion(ship)) {
auto@ node = ship.getNode();
if(node !is null)
node.hintParentObject(ship.region);
}
ship.moverTick(time);
if(ship.hasLeaderAI)
ship.leaderTick(time);
timer += float(time);
if(timer >= 1.f) {
occasional_tick(ship, timer);
timer = 0.f;
}
return 0.2;
}
void destroy(Ship& ship) {
if(ship.inCombat) {
auto@ region = ship.region;
if(region !is null) {
uint debris = uint(log(ship.blueprint.design.size) / log(2.0));
if(debris > 0)
region.addShipDebris(ship.position, debris);
}
}
leaveRegion(ship);
if(ship.hasLeaderAI)
ship.leaderDestroy();
}
bool onOwnerChange(Ship& ship, Empire@ prevOwner) {
regionOwnerChange(ship, prevOwner);
if(ship.hasLeaderAI)
ship.leaderChangeOwner(prevOwner, ship.owner);
return false;
}
void createGraphics(Ship& ship, const Design@ dsg) {
if(dsg is null)
return;
MeshDesc shipMesh;
getDesignMesh(ship.owner, ship.blueprint.design, shipMesh);
shipMesh.memorable = ship.memorable;
bindMesh(ship, shipMesh);
hasGraphics = true;
if(ship.hasLeaderAI) {
auto@ node = ship.getNode();
if(node !is null)
node.animInvis = true;
}
}
void syncInitial(Ship& ship, Message& msg) {
//Find hull
uint hullID = msg.readSmall();
const Hull@ hull = getHullDefinition(hullID);
//Sync data
ship.blueprint.recvDetails(ship, msg);
if(msg.readBit()) {
ship.activateLeaderAI();
ship.leaderInit();
ship.readLeaderAI(msg);
auto@ node = ship.getNode();
if(node !is null)
node.animInvis = true;
}
else {
ship.activateSupportAI();
ship.readSupportAI(msg);
}
ship.readMover(msg);
if(msg.readBit()) {
msg >> ship.MaxEnergy;
ship.Energy = msg.readFixed(0.f, ship.MaxEnergy, 16);
}
if(msg.readBit()) {
msg >> ship.MaxSupply;
ship.Supply = msg.readFixed(0.f, ship.MaxSupply, 16);
}
if(msg.readBit()) {
msg >> ship.MaxShield;
ship.Shield = msg.readFixed(0.f, ship.MaxShield, 16);
}
if(msg.readBit()) {
ship.activateAbilities();
ship.readAbilities(msg);
}
if(msg.readBit()) {
ship.activateStatuses();
ship.readStatuses(msg);
}
if(msg.readBit()) {
ship.activateCargo();
ship.readCargo(msg);
}
if(msg.readBit()) {
ship.activateConstruction();
ship.readConstruction(msg);
}
if(msg.readBit()) {
ship.activateOrbit();
ship.readOrbit(msg);
}
createGraphics(ship, ship.blueprint.design);
}
void syncDetailed(Ship& ship, Message& msg, double tDiff) {
ship.readMover(msg);
if(ship.hasLeaderAI)
ship.readLeaderAI(msg);
else
ship.readSupportAI(msg);
ship.blueprint.recvDetails(ship, msg);
updateStats(ship);
msg >> ship.Energy;
msg >> ship.MaxEnergy;
msg >> ship.Supply;
msg >> ship.MaxSupply;
msg >> ship.Shield;
msg >> ship.MaxShield;
ship.isFTLing = msg.readBit();
ship.inCombat = msg.readBit();
if(ship.hasAbilities)
ship.readAbilities(msg);
if(ship.hasStatuses)
ship.readStatuses(msg);
if(msg.readBit()) {
if(!ship.hasCargo)
ship.activateCargo();
ship.readCargo(msg);
}
if(msg.readBit()) {
if(!ship.hasOrbit)
ship.activateOrbit();
ship.readOrbit(msg);
}
if(msg.readBit()) {
if(!ship.hasConstruction)
ship.activateConstruction();
ship.readConstruction(msg);
}
}
void updateStats(Ship& ship) {
const Design@ dsg = ship.blueprint.design;
if(dsg is null)
return;
ship.DPS = ship.blueprint.getEfficiencySum(SV_DPS);
ship.MaxDPS = dsg.total(SV_DPS);
ship.MaxSupply = dsg.total(SV_SupplyCapacity);
ship.MaxShield = dsg.total(SV_ShieldCapacity);
commandUsed = dsg.variable(ShV_REQUIRES_Command);
}
void syncDelta(Ship& ship, Message& msg, double tDiff) {
if(msg.readBit())
ship.readMoverDelta(msg);
if(msg.readBit()) {
ship.blueprint.recvDelta(ship, msg);
if(!hasGraphics)
createGraphics(ship, ship.blueprint.design);
updateStats(ship);
}
if(msg.readBit())
ship.Shield = msg.readFixed(0.f, ship.MaxShield, 16);
if(msg.readBit()) {
if(ship.hasLeaderAI)
ship.readLeaderAIDelta(msg);
else
ship.readSupportAIDelta(msg);
}
if(ship.hasAbilities) {
if(msg.readBit())
ship.readAbilityDelta(msg);
}
if(ship.hasStatuses) {
if(msg.readBit())
ship.readStatusDelta(msg);
}
if(ship.hasLeaderAI) {
if(msg.readBit()) {
if(!ship.hasCargo)
ship.activateCargo();
ship.readCargoDelta(msg);
}
}
if(msg.readBit()) {
if(msg.readBit())
msg >> ship.Energy;
else
ship.Energy = 0;
if(msg.readBit())
msg >> ship.Supply;
else
ship.Supply = 0;
ship.isFTLing = msg.readBit();
ship.inCombat = msg.readBit();
}
if(msg.readBit()) {
if(!ship.hasOrbit)
ship.activateOrbit();
ship.readOrbitDelta(msg);
}
if(ship.hasLeaderAI) {
if(msg.readBit()) {
if(!ship.hasConstruction)
ship.activateConstruction();
ship.readConstructionDelta(msg);
}
}
}
};
+68
View File
@@ -0,0 +1,68 @@
import regions.regions;
LightDesc lightDesc;
tidy class StarScript {
void syncInitial(Star& star, Message& msg) {
star.temperature = msg.read_float();
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;
}
if(node !is null)
node.hintParentObject(star.region);
star.readOrbit(msg);
lightDesc.position = vec3f(star.position);
lightDesc.diffuse = node.color * 1.0f;
lightDesc.specular = lightDesc.diffuse;
lightDesc.radius = star.radius;
if(star.inOrbit)
makeLight(lightDesc, node);
else
makeLight(lightDesc);
addAmbientSource("star_rumble", star.id, star.position, soundRadius);
}
void destroy(Star& obj) {
removeAmbientSource(obj.id);
leaveRegion(obj);
}
void syncDetailed(Star& star, Message& msg, double tDiff) {
star.Health = msg.read_float();
star.MaxHealth = msg.read_float();
}
void syncDelta(Star& star, Message& msg, double tDiff) {
star.Health = msg.read_float();
star.MaxHealth = msg.read_float();
}
double tick(Star& star, double time) {
if(updateRegion(star)) {
auto@ node = star.getNode();
if(node !is null)
node.hintParentObject(star.region);
}
star.orbitTick(time);
return 1.0;
}
};
+202
View File
@@ -0,0 +1,202 @@
import systems;
tidy class TerritoryScript {
TerritoryNode@ node;
set_int inner;
set_int edges;
array<Region@> regions;
array<Region@> visionPending;
array<bool> visionPendingOp;
Empire@ prevEmpire = playerEmpire;
double tick(Territory& obj, double time) {
if(playerEmpire !is prevEmpire) {
@prevEmpire = playerEmpire;
//Apply anything that was waiting
for(uint i = 0, cnt = visionPending.length; i < cnt; ++i) {
Region@ region = visionPending[i];
if(visionPendingOp[i])
node.addInner(region.id, region.position, region.radius);
else
node.removeInner(region.id);
}
visionPending.length = 0;
visionPendingOp.length = 0;
//Remove vision on systems we aren't supposed to see
for(uint i = 0, cnt = regions.length; i < cnt; ++i) {
Region@ region = regions[i];
if(obj.owner is playerEmpire || region.VisionMask & playerEmpire.visionMask == 0) {
node.removeInner(region.id);
visionPending.insertLast(region);
visionPendingOp.insertLast(true);
}
}
return 1.0;
}
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 _readRegions(Territory& obj, Message& msg) {
uint cnt = 0;
msg >> cnt;
set_int newSet;
for(uint i = 0; i < cnt; ++i) {
Region@ region = cast<Region>(msg.readObject());
newSet.insert(region.id);
//Add new regions
if(!inner.contains(region.id))
add(obj, region);
//TODO: Check if this could ever result in
//an out-of-order delta putting a region
//in the wrong territory.
region.setTerritory(obj.owner, obj);
}
//Remove old regions
for(uint i = 0, ocnt = regions.length; i < ocnt; ++i) {
Region@ region = regions[i];
if(!newSet.contains(region.id)) {
remove(obj, region);
region.clearTerritory(obj.owner, obj);
--i; --ocnt;
}
}
}
void destroy(Territory& obj) {
node.markForDeletion();
@node = null;
}
void syncInitial(Territory& obj, Message& msg) {
@node = TerritoryNode();
node.setOwner(obj.owner);
_readRegions(obj, msg);
}
void syncDetailed(Territory& obj, Message& msg, double tDiff) {
_readRegions(obj, msg);
}
void syncDelta(Territory& obj, Message& msg, double tDiff) {
if(msg.readBit())
_readRegions(obj, msg);
}
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);
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);
//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);
}
}
};
+616
View File
@@ -0,0 +1,616 @@
import ship_groups;
import orders;
import resources;
//Factor of new design cost as minimum for retrofit
const double RETROFIT_MIN_PCT = 0.3;
tidy class LeaderAI : Component_LeaderAI {
OrderDesc[] orders;
Object@[] supports;
GroupData@[] groupData;
uint supplyCapacity = 0;
uint supplyUsed = 0;
double ghostHP = 0.0;
double ghostDPS = 0.0;
double orderedHP = 0.0;
double orderedDPS = 0.0;
double fleetHP = 0.0;
double fleetDPS = 0.0;
double fleetMaxHP = 0.0;
double fleetMaxDPS = 0.0;
double bonusDPS = 0.0;
float fleetEffectiveness = 1.f;
float permanentEffectiveness = 0.f;
float needExperience = 0.f;
bool autoFill = false;
bool autoBuy = false;
bool AllowFillFrom = false;
bool allowSatellites = false;
AutoMode autoMode = AM_AreaBound;
EngagementBehaviour engageBehave = EB_CloseIn;
EngagementRange engageType = ER_SupportMin;
FleetPlaneNode@ node;
float getFleetEffectiveness() const {
return fleetEffectiveness * getBaseFleetEffectiveness();
}
float getBaseFleetEffectiveness() const {
if(permanentEffectiveness < 0)
return pow(0.5, -2.0 * double(permanentEffectiveness));
return 1.0 + permanentEffectiveness;
}
void setFleetEffectiveness(float value) {
fleetEffectiveness = value;
}
uint getAutoMode() {
return uint(autoMode);
}
uint getEngageBehave() {
return uint(engageBehave);
}
uint getEngageType() {
return uint(engageType);
}
void leaderInit(Object& obj) {
if(obj.isShip) {
double formationRad = getFormationRadius(obj);
@node = FleetPlaneNode();
node.establish(obj, formationRad);
}
leaderChangeOwner(obj, null, obj.owner);
}
void leaderDestroy(Object& obj) {
if(obj.owner !is null && obj.owner.valid)
obj.owner.unregisterFleet(obj);
if(node !is null) {
cast<Node>(node).markForDeletion();
@node = null;
}
}
void leaderTick(Object& obj, double time) {
//Set plane visibility
if(node !is null) {
node.visible = obj.isVisibleTo(playerEmpire);
if(obj.region !is null)
node.hintParentObject(obj.region);
}
}
void leaderChangeOwner(Object& obj, Empire@ oldOwner, Empire@ newOwner) {
if(oldOwner !is null && oldOwner.valid)
oldOwner.unregisterFleet(obj);
if(newOwner !is null && newOwner.valid)
newOwner.registerFleet(obj);
}
int getRetrofitCost(const Object& obj) const {
int cost = 0;
bool have = false;
const Ship@ ship = cast<const Ship>(obj);
if(ship !is null) {
const Design@ from = ship.blueprint.design;
if(from !is null) {
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
int fromCost = getBuildCost(from);
int toCost = getBuildCost(to);
cost += max(toCost - fromCost, int(ceil(toCost * RETROFIT_MIN_PCT)));
have = true;
}
}
}
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ from = dat.dsg;
if(from is null)
continue;
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
int fromCost = getBuildCost(from);
int toCost = getBuildCost(to);
cost += max(toCost - fromCost, int(ceil(toCost * RETROFIT_MIN_PCT))) * dat.amount;
have = true;
}
}
if(!have)
return -1;
else
return cost;
}
double getRetrofitLabor(const Object& obj) const {
double cost = 0;
bool have = false;
const Ship@ ship = cast<const Ship>(obj);
if(ship !is null) {
const Design@ from = ship.blueprint.design;
if(from !is null) {
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
double fromCost = getLaborCost(from);
double toCost = getLaborCost(to);
cost += max(toCost - fromCost, toCost * RETROFIT_MIN_PCT);
have = true;
}
}
}
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ from = dat.dsg;
if(from is null)
continue;
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
double fromCost = getLaborCost(from);
double toCost = getLaborCost(to);
cost += max(toCost - fromCost, toCost * RETROFIT_MIN_PCT) * dat.amount;
have = true;
}
}
if(!have)
return -1;
else
return cost;
}
double get_GhostHP() const {
return ghostHP;
}
double get_GhostDPS() const {
return ghostDPS;
}
bool get_hasOrders() {
return orders.length != 0;
}
bool hasOrder(uint type, bool checkQueued = false) {
if(orders.length == 0)
return false;
if(!checkQueued)
return orders[0].type == type;
for(int i = orders.length - 1; i >= 0; --i) {
if(orders[i].type == type)
return true;
}
return false;
}
uint get_orderCount() {
return orders.length;
}
string get_orderName(uint num) {
return "(null)"; //TODO
}
uint get_orderType(uint num) {
if(num >= orders.length)
return 0;
return orders[num].type;
}
bool get_orderHasMovement(uint num) {
if(num >= orders.length)
return false;
return orders[num].hasMovement;
}
vec3d get_orderMoveDestination(uint num) {
if(num >= orders.length)
return vec3d();
return orders[num].moveDestination;
}
vec3d get_finalMoveDestination(const Object& obj) {
for(int i = orders.length - 1; i >= 0; --i) {
if(orders[i].hasMovement)
return orders[i].moveDestination;
}
return obj.position;
}
void getSupportGroups() const {
for(uint i = 0, cnt = groupData.length; i < cnt; ++i)
yield(groupData[i]);
}
double getFormationRadius(Object& obj) {
Planet@ pl = cast<Planet>(obj);
if(pl !is null)
return pl.OrbitSize;
return obj.radius * 10.0 + 20.0;
}
uint get_supportCount() {
return supports.length;
}
Object@ get_supportShip(uint index) {
if(index >= supports.length)
return null;
auto@ supp = supports[index];
if(!supp.valid || !supp.initialized)
return null;
return supp;
}
uint get_SupplyUsed() const {
return supplyUsed;
}
uint get_SupplyCapacity() const {
return supplyCapacity;
}
uint get_SupplyAvailable() const {
return supplyCapacity - supplyUsed;
}
void updateFleetStrength(Object& obj) {
double hp = 0.0, dps = 0.0, maxHP = 0.0, maxDPS = 0.0;
if(obj.isShip) {
Ship@ ship = cast<Ship>(obj);
auto@ bp = ship.blueprint;
hp = bp.currentHP * bp.hpFactor + ship.Shield;
dps = ship.DPS * bp.shipEffectiveness;
maxHP = bp.design.totalHP + ship.MaxShield;
maxDPS = ship.MaxDPS;
}
if(obj.isOrbital) {
Orbital@ orb = cast<Orbital>(obj);
hp = orb.health + orb.armor;
maxHP = orb.maxHealth + orb.maxArmor;
maxDPS = orb.dps;
dps = maxDPS * orb.efficiency;
}
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
Ship@ ship = cast<Ship>(supports[i]);
if(ship !is null) {
auto@ bp = ship.blueprint;
const Design@ dsg = bp.design;
if(dsg is null)
continue;
hp += bp.currentHP * bp.hpFactor + ship.Shield;
dps += ship.DPS * bp.shipEffectiveness;
maxHP += dsg.totalHP + ship.MaxShield;
maxDPS += ship.MaxDPS;
}
}
fleetHP = hp;
fleetDPS = dps;
fleetMaxHP = maxHP;
fleetMaxDPS = maxDPS;
}
void transferSupports(Object& obj, const Design@ ofDesign, uint amount, Object@ transferTo) {
if(!transferTo.hasLeaderAI || ofDesign is null || amount == 0)
return;
int ind = getGroupDataIndex(ofDesign, false);
if(ind == -1)
return;
//Don't try to transfer over our supply cap
amount = min(amount, transferTo.SupplyAvailable / uint(ofDesign.size));
if(amount == 0)
return;
ind = getGroupDataIndex(ofDesign, false);
if(ind == -1)
return;
GroupData@ dat = groupData[ind];
//Transfer real ships over first
uint take = min(dat.amount, amount);
if(take != 0) {
amount -= take;
dat.amount -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addFakeSupports(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
//Transfer ordered
if(amount == 0)
return;
take = min(dat.ordered, amount);
if(take != 0) {
amount -= take;
dat.ordered -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addSupportOrdered(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
//Transfer ghosts
if(amount == 0)
return;
take = min(dat.ghost, amount);
if(take != 0) {
dat.ghost -= take;
amount -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addSupportGhosts(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
}
void orderSupports(Object& obj, const Design@ ofDesign, uint amount) {
if(!obj.owner.canPay(getBuildCost(ofDesign, amount)))
return;
int index = getGroupDataIndex(ofDesign, true);
groupData[index].ordered += amount;
}
void addSupportGhosts(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.ghost += amount;
supplyUsed += amount * ofDesign.size;
}
void addSupportOrdered(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.ordered += amount;
supplyUsed += amount * ofDesign.size;
}
void addFakeSupports(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.amount += amount;
supplyUsed += amount * ofDesign.size;
}
double getRemainingExp() const {
return needExperience;
}
double getFleetHP() const {
return fleetHP;
}
double getFleetDPS() const {
return fleetDPS + bonusDPS;
}
double getFleetStrength(const Object& obj) const {
return fleetHP * (fleetDPS + bonusDPS);
}
double getFleetMaxStrength(const Object& obj) const {
return (fleetMaxHP + ghostHP + orderedHP) * (fleetMaxDPS + bonusDPS + ghostDPS + orderedDPS) * getBaseFleetEffectiveness();
}
bool get_canHaveSatellites() const {
return allowSatellites;
}
int getGroupDataIndex(const Design@ dsg, bool create = false) {
@dsg = dsg.mostUpdated();
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ oldDesign = dat.dsg;
const Design@ newDesign = dat.dsg.mostUpdated();
if(newDesign is dsg.mostUpdated()) {
if(oldDesign !is newDesign)
@dat.dsg = newDesign;
return i;
}
}
if(create) {
GroupData dat;
@dat.dsg = dsg.mostUpdated();
groupData.insertLast(dat);
return groupData.length - 1;
}
return -1;
}
uint getGhostCount(const Design@ dsg) const {
int ind = getGroupDataIndex(dsg);
if(ind == -1)
return 0;
return groupData[ind].ghost;
}
void readOrders(Message& msg) {
msg.readAlign();
uint cnt = msg.read_uint();
orders.length = cnt;
for(uint i = 0; i < cnt; ++i)
orders[i].read(msg);
autoMode = AutoMode(msg.readSmall());
engageType = EngagementRange(msg.readSmall());
engageBehave = EngagementBehaviour(msg.readSmall());
msg >> autoFill >> autoBuy >> AllowFillFrom;
}
bool get_autoBuySupports() {
return autoBuy;
}
bool get_autoFillSupports() {
return autoFill;
}
void set_autoBuySupports(bool value) {
autoBuy = value;
}
void set_autoFillSupports(bool value) {
autoFill = value;
}
bool get_allowFillFrom() {
return AllowFillFrom;
}
void set_allowFillFrom(bool value) {
AllowFillFrom = value;
}
void readLeaderData(Message& msg) {
uint cnt = msg.readSmall();
groupData.length = cnt;
double gHP = 0, gDPS = 0;
double oHP = 0, oDPS = 0;
for(uint i = 0; i < cnt; ++i) {
GroupData@ dat = groupData[i];
if(dat is null) {
@dat = GroupData();
@groupData[i] = dat;
}
msg >> dat;
if(dat.dsg !is null) {
double dps = dat.dsg.total(SV_DPS);
gHP += double(dat.ghost) * dat.dsg.totalHP;
gDPS += double(dat.ghost) * dps;
oHP += double(dat.ordered) * dat.dsg.totalHP;
oDPS += double(dat.ordered) * dps;
}
}
ghostHP = gHP;
ghostDPS = gDPS;
orderedHP = oHP;
orderedDPS = oDPS;
bool hadSupply = supplyCapacity > 0;
supplyCapacity = msg.readSmall();
supplyUsed = msg.readSmall();
if(msg.readBit())
fleetEffectiveness = msg.readFixed(0.f, 50.f, 16);
else
fleetEffectiveness = 1.f;
if(msg.readBit())
permanentEffectiveness = msg.readFixed(0.f, 50.f, 16);
else
permanentEffectiveness = 0.f;
if(msg.readBit())
bonusDPS = msg.read_float();
else
bonusDPS = 0.0;
if(msg.readBit())
needExperience = msg.read_float();
else
needExperience = 0.0;
if(node !is null) {
if(hadSupply != (supplyCapacity > 0))
node.hasSupply = supplyCapacity > 0;
}
}
void readGroup(Message& msg) {
bool hadSupports = supports.length > 0;
uint cnt = msg.readSmall();
supports.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> supports[i];
readLeaderData(msg);
if(node !is null) {
if(hadSupports != (supports.length > 0))
node.hasFleet = supports.length > 0;
}
}
void readGroupDelta(Message& msg) {
bool hadSupports = supports.length > 0;
//Added
if(msg.readBit()) {
uint cnt = msg.readSmall();
for(uint i = 0; i < cnt; ++i) {
Object@ ship;
msg >> ship;
supports.insertLast(ship);
}
}
//Removed
if(msg.readBit()) {
uint cnt = msg.readSmall();
for(uint i = 0; i < cnt; ++i) {
Object@ ship;
msg >> ship;
supports.remove(ship);
}
}
readLeaderData(msg);
if(node !is null) {
if(hadSupports != (supports.length > 0))
node.hasFleet = supports.length > 0;
}
}
void readLeaderAI(Object& obj, Message& msg) {
readGroup(msg);
readOrders(msg);
msg >> allowSatellites;
}
void readLeaderAIDelta(Message& msg) {
if(msg.readBit())
readGroupDelta(msg);
if(msg.readBit())
readOrders(msg);
}
};
+11
View File
@@ -0,0 +1,11 @@
tidy class SupportAI : Component_SupportAI {
void readSupportAI(Object& obj, Message& msg) {
Ship@ ship = cast<Ship>(obj);
@ship.Leader = msg.readObject();
}
void readSupportAIDelta(Object& obj, Message& msg) {
Ship@ ship = cast<Ship>(obj);
@ship.Leader = msg.readObject();
}
}
+746
View File
@@ -0,0 +1,746 @@
import biomes;
import resources;
import systems;
import planets.PlanetSurface;
import planet_levels;
import bool getCheatsEverOn() from "cheats";
const double COLONYSHIP_BASE_ACCEL = 5.5;
tidy class SurfaceComponent : Component_SurfaceComponent {
uint biome0, biome1, biome2;
vec2u originalSurfaceSize;
double Population = 0.0;
int MaxPopulation = 0;
int Income = 0;
bool needsPopulationForLevel = true;
PlanetIconNode@ icon;
array<int> iconMemory(getEmpireCount(), -1);
Object@[] colonization;
double colonyshipAccel = 1.0;
uint Level = 0;
uint LevelChainId = 0;
uint ResourceLevel = 0;
uint ColonizingMask = 0;
uint protectedFromMask = 0;
int maxPlanetLevel = -1;
uint orbitsMask = 0;
uint DecayLevel = 0;
double DecayTimer = -1.0;
int Quarantined = 0;
int Contestion = 0;
double tileDevelopRate = 1.0;
double bldConstructRate = 1.0;
double undevelopedMaint = 1.0;
bool isSendingColonizers = false;
bool wasMoving = false;
uint ResourceModID = 0;
int BaseLoyalty = 10;
int LoyaltyBonus = 0;
bool disableProtection = false;
double[] LoyaltyEffect = double[](getEmpireCount(), 0);
double growthRate = 1.0;
uint gfxFlags = 0;
array<uint> affinities;
PlanetSurface grid;
uint SurfaceModId = 0;
SurfaceComponent() {
}
uint get_planetGraphicsFlags() const {
return gfxFlags;
}
uint get_Biome0() {
return biome0;
}
uint get_Biome1() {
return biome1;
}
uint get_Biome2() {
return biome2;
}
uint get_maxPopulation() const {
return MaxPopulation;
}
double get_population() const {
return Population;
}
uint get_level() {
return Level;
}
uint get_levelChain() {
return LevelChainId;
}
int get_maxLevel() {
return maxPlanetLevel;
}
uint get_resourceLevel() {
return ResourceLevel;
}
int get_income() const {
return Income;
}
double get_decayTime() const {
return DecayTimer;
}
bool get_quarantined() const {
return Quarantined != 0;
}
double get_undevelopedMaintenance() const {
return undevelopedMaint;
}
double get_buildingConstructRate() const {
return bldConstructRate;
}
double get_tileDevelopmentRate() const {
return tileDevelopRate;
}
int get_buildingMaintenance() const {
return grid.Maintenance;
}
uint get_pressureCap() const {
return grid.pressureCap;
}
float get_totalPressure() const {
return grid.totalPressure;
}
vec3d get_planetIconPosition(const Object& obj) const {
if(icon is null)
return obj.position;
return icon.position;
}
uint get_visibleLevel(Player& pl, const Object& obj) const {
Empire@ emp = pl.emp;
if(emp is null)
return 0;
if(obj.isVisibleTo(emp))
return Level;
if(obj.isKnownTo(emp) && emp.valid)
return iconMemory[emp.index] & 0xff;
return 0;
}
Empire@ get_visibleOwner(Player& pl, const Object& obj) const {
Empire@ emp = pl.emp;
if(emp is null)
return defaultEmpire;
if(obj.isVisibleTo(emp))
return obj.owner;
if(obj.isKnownTo(emp) && emp.valid) {
Empire@ other = getEmpireByID((iconMemory[emp.index] & 0xff00) >> 8);
if(other is null)
return defaultEmpire;
return other;
}
return defaultEmpire;
}
uint getBuildingCount(uint buildingId) const {
uint amount = 0;
for(uint i = 0, cnt = grid.buildings.length; i < cnt; ++i) {
if(grid.buildings[i].type.id == buildingId)
amount += 1;
}
return amount;
}
uint getBuildingCount() const {
return grid.buildings.length;
}
uint get_buildingType(uint index) const {
if(index >= grid.buildings.length)
return uint(-1);
return grid.buildings[index].type.id;
}
bool isProtected(const Object& obj, Empire@ siegeEmp = null) const {
if(disableProtection)
return false;
if(siegeEmp !is null && protectedFromMask & siegeEmp.mask != 0)
return true;
const Region@ region = obj.region;
if(region !is null) {
Empire@ owner = obj.owner;
if(region.ProtectedMask & owner.mask != 0) {
if(disableProtection)
return false;
return true;
}
}
return false;
}
int get_baseLoyalty(const Object& obj) const {
return BaseLoyalty + obj.owner.GlobalLoyalty.value;
}
bool get_isBeingColonized(Player& pl, const Object& obj) {
Empire@ emp = pl.emp;
if(emp is null)
return false;
return ColonizingMask & emp.mask != 0;
}
Empire@ get_captureEmpire(const Object& obj) const {
double best = 0;
Empire@ bestEmp;
uint owner = obj.owner.index;
for(uint i = 0, cnt = LoyaltyEffect.length; i < cnt; ++i) {
if(LoyaltyEffect[i] < best && i != owner) {
best = LoyaltyEffect[i];
@bestEmp = getEmpire(i);
}
}
return bestEmp;
}
float get_capturePct(const Object& obj) const {
double baseLoy = double(BaseLoyalty + obj.owner.GlobalLoyalty.value);
if(baseLoy == 0)
return 1.f;
float best = 0;
uint owner = obj.owner.index;
for(uint i = 0, cnt = LoyaltyEffect.length; i < cnt; ++i) {
if(i != owner) {
float pct = (-LoyaltyEffect[i]) / baseLoy;
if(pct > best)
best = pct;
}
}
return best;
}
int get_lowestLoyalty(const Object& obj) const {
int global = obj.owner.GlobalLoyalty.value;
int lowest = BaseLoyalty + global;
for(uint i = 0, cnt = LoyaltyEffect.length; i < cnt; ++i) {
Empire@ emp = getEmpire(i);
if(!emp.major || emp is obj.owner)
continue;
int loy = BaseLoyalty + global + ceil(LoyaltyEffect[i]);
if(loy < lowest)
lowest = loy;
}
return lowest;
}
int get_currentLoyalty(Player& requestor, const Object& obj) const {
Empire@ emp = requestor.emp;
if(emp is null || !emp.valid)
return BaseLoyalty + obj.owner.GlobalLoyalty.value;
if(emp is obj.owner)
return get_lowestLoyalty(obj);
return BaseLoyalty + obj.owner.GlobalLoyalty.value + ceil(LoyaltyEffect[emp.index]);
}
int getLoyaltyFacing(Player& requestor, const Object& obj, Empire@ emp) const {
Empire@ reqEmp = requestor.emp;
if(requestor != SERVER_PLAYER && reqEmp !is emp && emp !is obj.owner)
return BaseLoyalty + obj.owner.GlobalLoyalty.value;
if(!emp.valid)
return BaseLoyalty + obj.owner.GlobalLoyalty.value;
return max(BaseLoyalty + obj.owner.GlobalLoyalty.value + int(ceil(LoyaltyEffect[emp.index])), 0);
}
bool get_hasContestion() {
return Contestion > 0;
}
bool get_isContested(const Object& obj) const {
int cont = Contestion;
if(cont > 0)
return true;
Empire@ owner = obj.owner;
Region@ reg = obj.region;
if(reg !is null) {
if(reg.ContestedMask & owner.mask != 0)
return true;
}
return false;
}
bool get_isUnderSiege(const Object& obj) const {
bool haveSiege = false;
for(uint i = 0, cnt = LoyaltyEffect.length; i < cnt; ++i) {
if(LoyaltyEffect[i] < -0.01) {
haveSiege = true;
break;
}
}
if(orbitsMask & obj.owner.hostileMask != 0)
return haveSiege;
return false;
}
bool get_isOverPressure() const {
return grid.totalPressure > int(grid.pressureCap);
}
void getPlanetSurface() {
yield(grid);
}
double get_colonyShipAccel(const Object& obj) {
return colonyshipAccel * COLONYSHIP_BASE_ACCEL * obj.owner.ModSpeed.value * obj.owner.ColonizerSpeed;
}
bool get_isColonizing() const {
return colonization.length != 0;
}
bool get_canSafelyColonize(const Object& obj) const {
if(Level < 1)
return false;
auto@ lv = getPlanetLevel(LevelChainId, ResourceLevel);
//Calculate growth rate
double growthFactor = growthRate;
float debtFactor = obj.owner.DebtFactor;
for(; debtFactor > 0; debtFactor -= 1.f)
growthFactor *= 0.33f + 0.67f * (1.f - min(debtFactor, 1.f));
growthFactor *= obj.owner.PopulationGrowthFactor;
growthFactor *= config::COLONIZING_GROWTH_PENALTY;
growthFactor *= getPlanetLevel(LevelChainId, min(Level,obj.primaryResourceLevel)).popGrowth;
if(obj.inCombat)
growthFactor = 0;
//Calculate projected final population
double colonizes = colonization.length + 1.0;
if(!isSendingColonizers)
colonizes = 1.0;
double finalPop = Population;
finalPop -= colonizes;
finalPop += colonizes * (1.0 - obj.owner.PopulationPerColonizer) * growthFactor;
if(!needsPopulationForLevel)
return finalPop >= 1.0;
else
return finalPop >= lv.requiredPop;
}
bool hasColonyTarget(Object& other) const {
for(uint i = 0, cnt = colonization.length; i < cnt; ++i)
if(colonization[i] is other)
return true;
return false;
}
uint get_colonyOrderCount() const {
return colonization.length;
}
Object@ get_colonyTarget(uint index) const {
if(index < colonization.length)
return colonization[index];
return null;
}
void setSystemCounter(uint index, uint amount) {
if(icon !is null)
icon.setCounter(index, amount);
}
double getResourceProduction(uint resource) {
if(resource >= grid.resources.length)
return 0.0;
return grid.resources[resource];
}
double getResourcePressure(uint resource) {
if(resource >= grid.pressures.length)
return 0.0;
return grid.pressures[resource];
}
void surfaceTick(Object& obj, double time) {
//Set icon visibility
if(icon !is null) {
icon.visible = obj.isVisibleTo(playerEmpire);
updateIconVision(obj);
if(wasMoving != obj.isMoving) {
if(wasMoving) {
if(obj.region !is null)
obj.region.addStrategicIcon(0, obj, icon);
}
else {
if(obj.region !is null)
obj.region.removeStrategicIcon(0, icon);
}
wasMoving = obj.isMoving;
}
}
//Do level decay
if(DecayTimer > 0)
DecayTimer = max(0.0, DecayTimer - time);
//Update icon
uint mod = obj.resourceModID;
if(mod != ResourceModID) {
ResourceModID = mod;
updateIcon(obj);
}
if(reqSurfaceData)
requestSurface(obj);
}
void _readVis(Message& msg) {
uint cnt = getEmpireCount();
for(uint i = 0; i < cnt; ++i)
msg >> iconMemory[i];
}
void _readPop(Message& msg) {
MaxPopulation = msg.readSmall();
Population = msg.read_float();
Income = msg.readSignedSmall();
msg >> Contestion;
if(msg.readBit())
growthRate = msg.read_float();
else
growthRate = 1.0;
needsPopulationForLevel = msg.readBit();
int maxLevel = getLevelChain(LevelChainId).levels.length-1;
ResourceLevel = msg.readLimited(maxLevel);
isSendingColonizers = msg.readBit();
}
void _readRes(Object& obj, Message& msg, bool initial = false) {
uint prevLevel = Level;
bool prevColonizing = ColonizingMask & playerEmpire.mask != 0;
double prevDecay = DecayTimer;
LevelChainId = msg.readLimited(getLevelChainCount());
int maxLevel = getLevelChain(LevelChainId).levels.length-1;
Level = msg.readLimited(maxLevel);
if(msg.readBit()) {
DecayLevel = msg.readLimited(Level-1);
DecayTimer = msg.read_float();
}
else {
DecayLevel = Level;
DecayTimer = -1.0;
}
if(msg.readBit())
maxPlanetLevel = msg.readSmall();
else
maxPlanetLevel = -1;
if(msg.readBit())
msg >> ColonizingMask;
else
ColonizingMask = 0;
msg >> disableProtection;
if(msg.readBit())
colonyshipAccel = msg.read_float();
else
colonyshipAccel = 1.0;
//Unlock achievement "Reach Level 4"
if(!initial && Level == 4 && prevLevel < 4 && obj.owner is playerEmpire && !getCheatsEverOn())
unlockAchievement("ACH_LEVEL4");
if(icon !is null) {
if(prevLevel != Level)
icon.setLevel(Level);
if((prevDecay < 0.0) != (DecayTimer < 0.0))
updateIcon(obj);
bool isColonizing = ColonizingMask & playerEmpire.mask != 0;
if(prevColonizing != isColonizing)
icon.setBeingColonized(isColonizing);
}
uint prevFlags = gfxFlags;
if(msg.readBit())
gfxFlags = msg.readSmall();
else
gfxFlags = 0;
if(prevFlags != gfxFlags) {
PlanetNode@ plNode = cast<PlanetNode>(obj.getNode());
if(plNode !is null)
plNode.flags = gfxFlags;
}
}
void _readAff(Object& obj, Message& msg) {
uint cnt = msg.readSmall();
affinities.length = cnt;
for(uint i = 0; i < cnt; ++i)
affinities[i] = msg.readSmall();
Quarantined = msg.readSmall();
}
void _readLoy(Object& obj, Message& msg) {
BaseLoyalty = msg.readSignedSmall();
LoyaltyBonus = msg.readSignedSmall();
protectedFromMask = msg.readSmall();
orbitsMask = msg.readSmall();
double base = double(BaseLoyalty + obj.owner.GlobalLoyalty.value);
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
if(msg.readBit())
LoyaltyEffect[i] = msg.readFixed(-base, 0, 12);
else
LoyaltyEffect[i] = 0;
}
if(icon !is null) {
Empire@ captEmp = get_captureEmpire(obj);
float captPct = get_capturePct(obj);
icon.setCapture(captEmp, captPct);
}
}
void _readColonization(Message& msg) {
if(!msg.readBit())
return;
uint8 count = msg.read_uint8();
colonization.length = count;
for(uint8 i = 0; i < count; ++i)
msg >> colonization[i];
}
void readSurfaceDelta(Object& obj, Message& msg) {
if(msg.readBit())
_readRes(obj, msg);
if(msg.readBit())
_readAff(obj, msg);
if(msg.readBit())
_readPop(msg);
if(msg.readBit()) {
if(grid.read(msg, true))
++SurfaceModId;
}
if(msg.readBit())
_readColonization(msg);
if(msg.readBit())
_readLoy(obj, msg);
}
Empire@ prevPlayer = playerEmpire;
void updateIconVision(Object& obj) {
uint resource = 0xfffe;
if(obj.nativeResourceCount != 0) {
const ResourceType@ type = getResource(obj.nativeResourceType[0]);
resource = type.id;
}
//Update remembered icon states
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
Empire@ emp = getEmpire(i);
if(obj.isVisibleTo(emp)) {
int mem = iconMemory[i];
if(mem != -1) {
iconMemory[i] = -1;
if(emp is playerEmpire)
updateIcon(obj);
}
}
else {
int mem = iconMemory[i];
if(mem == -1) {
mem = 0;
mem |= Level;
mem |= obj.owner.id << 8;
mem |= resource << 16;
iconMemory[i] = mem;
if(emp is playerEmpire)
updateIcon(obj);
}
}
}
if(prevPlayer !is playerEmpire) {
updateIcon(obj);
}
}
void updateIcon(Object& obj) {
//Update actual icon
if(icon !is null) {
if(obj.isVisibleTo(playerEmpire)) {
//Use active
icon.setLevel(Level);
if(obj.nativeResourceCount != 0) {
const ResourceType@ type = getResource(obj.nativeResourceType[0]);
Object@ destination = obj.getNativeResourceDestination(playerEmpire, 0);
icon.setResource(type.id);
icon.setState(!obj.nativeResourceUsable[0],
destination !is null,
(type.isMaterial(Level) || destination !is null)
&& (destination is null || !destination.owner.valid || destination.owner is obj.owner),
DecayTimer > 0.0);
}
else {
icon.setResource(uint(-1));
icon.setState(false, false, true, false);
}
}
else if(obj.isKnownTo(playerEmpire) && playerEmpire.valid) {
int mem = iconMemory[playerEmpire.index];
int level = mem & 0xff;
int empId =(mem & 0xff00) >> 8;
int res = (mem & 0xffff0000) >> 16;
icon.setLevel(level);
icon.setOwner(getEmpireByID(empId));
if(res == 0xfffe) {
icon.setResource(uint(-1));
icon.setState(false, false, true, false);
}
else {
icon.setResource(res);
Object@ destination = obj.getNativeResourceDestination(playerEmpire, 0);
icon.setState(false, destination !is null, true, false);
}
}
}
}
void changeSurfaceRegion(Object& obj, Region@ prevRegion, Region@ newRegion) {
if(icon !is null && !wasMoving) {
if(prevRegion !is null)
prevRegion.removeStrategicIcon(0, icon);
if(newRegion !is null)
newRegion.addStrategicIcon(0, obj, icon);
else
icon.clearStrategic();
}
}
uint get_totalSurfaceTiles() const {
return grid.tileBuildings.length;
}
uint get_usedSurfaceTiles() const {
uint used = 0;
for(uint i = 0, cnt = grid.buildings.length; i < cnt; ++i) {
vec2u size = grid.buildings[i].type.size;
used += size.x * size.y;
}
return used;
}
void destroySurface(Object& obj) {
if(icon !is null) {
Region@ region = obj.region;
if(region !is null)
region.removeStrategicIcon(0, icon);
icon.markForDeletion();
}
}
void readSurface(Object& obj, Message& msg) {
_readPop(msg);
_readRes(obj, msg, true);
_readAff(obj, msg);
_readLoy(obj, msg);
_readVis(msg);
_readColonization(msg);
msg >> Quarantined;
tileDevelopRate = msg.read_float();
bldConstructRate = msg.read_float();
undevelopedMaint = msg.read_float();
originalSurfaceSize.x = msg.readSmall();
originalSurfaceSize.y = msg.readSmall();
biome0 = msg.readSmall();
biome1 = msg.readSmall();
biome2 = msg.readSmall();
grid.read(msg);
Planet@ pl = cast<Planet>(obj);
if(pl !is null && icon is null) {
@icon = PlanetIconNode();
icon.establish(pl);
updateIcon(obj);
if(obj.region !is null)
obj.region.addStrategicIcon(0, obj, icon);
}
else {
updateIcon(obj);
}
}
Image@ surfaceData;
bool reqSurfaceData = false;
uint surfaceDataMod = uint(-1);
uint getSurfaceData(Object& obj, Image& img) {
reqSurfaceData = true;
if(surfaceData is null) {
obj.requestSurface();
return uint(-1);
}
img = surfaceData;
return surfaceDataMod;
}
void requestSurface(Object& obj) {
if(surfaceData is null)
@surfaceData = Image(originalSurfaceSize, 4);
if(surfaceDataMod != SurfaceModId) {
renderSurfaceData(obj, grid, surfaceData, sizeLimit=originalSurfaceSize, citiesMode=true);
surfaceDataMod = SurfaceModId;
}
reqSurfaceData = false;
}
uint get_surfaceModId() {
return SurfaceModId;
}
vec2i get_surfaceGridSize() {
return vec2i(grid.size);
}
vec2i get_originalGridSize() {
return vec2i(originalSurfaceSize);
}
}
+91
View File
@@ -0,0 +1,91 @@
tidy class RegionScript {
array<bool> systemFlags;
void init(Region& region) {
region.initRegion();
}
double tick(Region& region, double time) {
region.tickRegion(time);
return 0.2;
}
uint readTypicalMask(Message& msg) const {
if(msg.readBit())
return msg.read_uint();
else
return 0;
}
bool getSystemFlag(Empire@ emp, uint flagIndex) const {
if(emp is null || !emp.valid)
return false;
uint ind = flagIndex * getEmpireCount() + emp.index;
if(ind >= systemFlags.length)
return false;
return systemFlags[ind];
}
bool getSystemFlagAny(uint flagIndex) const {
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
uint ind = flagIndex * getEmpireCount() + i;
if(ind < systemFlags.length && systemFlags[ind])
return true;
}
return false;
}
void readMasks(Region& region, Message& msg) {
msg >> region.VisionMask;
region.ProtectedMask.value = readTypicalMask(msg);
region.FreeFTLMask.value = readTypicalMask(msg);
region.SiegedMask.value = readTypicalMask(msg);
region.SiegingMask.value = readTypicalMask(msg);
region.GateMask.value = readTypicalMask(msg);
region.BlockFTLMask.value = readTypicalMask(msg);
region.CombatMask = readTypicalMask(msg);
region.TradeMask = readTypicalMask(msg);
region.MemoryMask = readTypicalMask(msg);
region.ExploredMask.value = readTypicalMask(msg);
}
void syncInitial(Region& region, Message& msg) {
region.SystemId = msg.readSmall();
region.AngleOffset = msg.readFixed(0.0, twopi);
msg >> region.OuterRadius;
region.InnerRadius = msg.readFixed(0.0, region.OuterRadius);
region.TargetCostMod = msg.readSignedSmall();
readMasks(region, msg);
region.updateRegionPlane();
systemFlags.length = msg.readSmall();
for(uint i = 0, cnt = systemFlags.length; i < cnt; ++i)
systemFlags[i] = msg.readBit();
}
void syncDetailed(Region& region, Message& msg, double tDiff) {
region.TargetCostMod = msg.readSignedSmall();
readMasks(region, msg);
region.updateRegionPlane();
systemFlags.length = msg.readSmall();
for(uint i = 0, cnt = systemFlags.length; i < cnt; ++i)
systemFlags[i] = msg.readBit();
}
void syncDelta(Region& region, Message& msg, double tDiff) {
if(msg.readBit()) {
region.TargetCostMod = msg.readSignedSmall();
readMasks(region, msg);
region.updateRegionPlane();
}
if(msg.readBit()) {
for(uint i = 0, cnt = systemFlags.length; i < cnt; ++i)
systemFlags[i] = msg.readBit();
}
}
};
+1
View File
@@ -0,0 +1 @@
#include "server/regions/RegionObjects.as"
+1
View File
@@ -0,0 +1 @@
#include "server/regions/regions.as"
+12
View File
@@ -0,0 +1,12 @@
bool hasDialogue_cl() {
return false;
}
void getActiveDialogue_cl() {
}
void getActiveDialogueObjective_cl() {
}
void skipObjective_cl() {
}
+13
View File
@@ -0,0 +1,13 @@
bool hasGameEnded = false;
bool hasGameEnded_client() {
return hasGameEnded;
}
void serverGameEnd() {
hasGameEnded = true;
}
void syncInitial(Message& msg) {
msg >> hasGameEnded;
}
+11
View File
@@ -0,0 +1,11 @@
bool hasProposedPeace(Player& player, Empire& from, Empire& to) {
if(player.emp !is from && player.emp !is to)
return false;
return from.PeaceMask.value & to.mask != 0;
}
bool isForcedPeace(Player& player, Empire& from, Empire& to) {
if(player.emp !is from && player.emp !is to)
return false;
return from.ForcedPeaceMask.value & to.mask != 0;
}