Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
import bool getCheatsEverOn() from "cheats";
|
||||
|
||||
void clientAchive(string id) {
|
||||
if(!getCheatsEverOn())
|
||||
unlockAchievement(id);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#include "server/components/Orbit.as"
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#include "server/regions/RegionObjects.as"
|
||||
@@ -0,0 +1 @@
|
||||
#include "server/regions/regions.as"
|
||||
@@ -0,0 +1,12 @@
|
||||
bool hasDialogue_cl() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void getActiveDialogue_cl() {
|
||||
}
|
||||
|
||||
void getActiveDialogueObjective_cl() {
|
||||
}
|
||||
|
||||
void skipObjective_cl() {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
bool hasGameEnded = false;
|
||||
|
||||
bool hasGameEnded_client() {
|
||||
return hasGameEnded;
|
||||
}
|
||||
|
||||
void serverGameEnd() {
|
||||
hasGameEnded = true;
|
||||
}
|
||||
|
||||
void syncInitial(Message& msg) {
|
||||
msg >> hasGameEnded;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user