535 lines
13 KiB
ActionScript
535 lines
13 KiB
ActionScript
import resources;
|
|
import util.design_export;
|
|
import influence;
|
|
from influence import InfluenceStore;
|
|
import saving;
|
|
import ftl;
|
|
import settings.game_settings;
|
|
import abilities;
|
|
from empire_ai.EmpireAI import EmpireAI;
|
|
import void initCreepCampTypes() from "camps";
|
|
import util.convar;
|
|
|
|
import void EGCoordinationTick(Empire& emp, double time) from "gameplay.extragalactic";
|
|
|
|
ConVar ai_full_speed("ai_full_speed", 0);
|
|
|
|
Empire@ Creeps;
|
|
Empire@ Pirates;
|
|
uint majorEmpireCount = 0;
|
|
|
|
array<ScriptThread@> threads;
|
|
DesignSet creepDesigns;
|
|
DesignSet pirateDesigns;
|
|
|
|
uint getMajorEmpireCount() {
|
|
return majorEmpireCount;
|
|
}
|
|
|
|
void init(Empire& emp) {
|
|
emp.initResearch();
|
|
emp.initAttributes();
|
|
emp.modFTLCapacity(+250);
|
|
emp.modFTLIncome(+1);
|
|
emp.modTotalBudget(+550, MoT_Planet_Income);
|
|
|
|
//Handle handicap
|
|
if(emp.handicap < 0) {
|
|
emp.modTotalBudget(-emp.handicap * 40.0, MoT_Handicap);
|
|
emp.handicap = 0;
|
|
}
|
|
else if(emp.handicap > 0) {
|
|
int take = min(10, emp.handicap);
|
|
emp.modMaintenance(take * 40.0, MoT_Handicap);
|
|
emp.handicap -= take;
|
|
}
|
|
|
|
//Add abilities
|
|
for(uint i = 0, cnt = getAbilityTypeCount(); i < cnt; ++i) {
|
|
auto@ abl = getAbilityType(i);
|
|
if(abl.empireDefault)
|
|
emp.addAbility(abl.id);
|
|
}
|
|
|
|
//Initialize traits
|
|
emp.initTraits();
|
|
|
|
//Initialize the AI
|
|
if(emp.hasEmpireAI)
|
|
cast<EmpireAI@>(emp.EmpireAI).init(emp);
|
|
|
|
EmpireTickData@ tickData = EmpireTickData(emp);
|
|
threads.insertLast(ScriptThread("empire::empireTickThread", @tickData));
|
|
}
|
|
|
|
void sendPeriodic(Empire& emp, Message& msg) {
|
|
msg.writeBit(emp.major);
|
|
if(!emp.major)
|
|
return;
|
|
|
|
msg << emp.visionMask << emp.hostileMask;
|
|
msg << emp.GlobalLoyalty.value;
|
|
msg.writeSignedSmall(emp.Victory);
|
|
emp.writeResources(msg);
|
|
if(!emp.writeAbilityDelta(msg))
|
|
msg.write0();
|
|
emp.writeNotifications(msg, true);
|
|
emp.writeResearch(msg);
|
|
emp.writeInfluenceManager(msg);
|
|
emp.writeAttributes(msg);
|
|
emp.writeObjects(msg);
|
|
emp.writeSyncedStates(msg);
|
|
emp.writeEvents(msg);
|
|
emp.writeDelta(msg);
|
|
emp.writeAttitudes(msg, false);
|
|
}
|
|
|
|
void syncInitial(Empire& emp, Message& msg) {
|
|
emp.writeNotifications(msg, false);
|
|
emp.writeInfluenceManager(msg, true);
|
|
emp.writeAttributes(msg, true);
|
|
emp.writeObjects(msg, true);
|
|
emp.writeResearch(msg, true);
|
|
emp.writeSyncedStates(msg);
|
|
emp.writeAbilities(msg);
|
|
emp.writeTraits(msg);
|
|
emp.writeAttitudes(msg, true);
|
|
emp.writeEvents(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;
|
|
}
|
|
|
|
void initEmpireDesigns() {
|
|
//Read default blueprints
|
|
creepDesigns.readDirectory("data/designs/creeps");
|
|
creepDesigns.log = true;
|
|
pirateDesigns.readDirectory("data/designs/pirates");
|
|
pirateDesigns.log = true;
|
|
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
|
Empire@ emp = getEmpire(i);
|
|
|
|
if(emp is Creeps) {
|
|
//Give the creeps their blueprints
|
|
creepDesigns.createFor(emp);
|
|
}
|
|
else if(emp is Pirates) {
|
|
//Give the pirates their blueprints
|
|
pirateDesigns.createFor(emp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void init() {
|
|
if(isLoadedSave)
|
|
return;
|
|
|
|
//Initialize empires
|
|
uint cnt = getEmpireCount();
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
Empire@ emp = getEmpire(i);
|
|
|
|
//Initialize the AI settings
|
|
if(i < gameSettings.empires.length) {
|
|
if(emp.hasEmpireAI)
|
|
cast<EmpireAI@>(emp.EmpireAI).init(emp, gameSettings.empires[i]);
|
|
}
|
|
|
|
init(emp);
|
|
}
|
|
|
|
//Post-initialize traits
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i)
|
|
getEmpire(i).postInitTraits();
|
|
}
|
|
|
|
void save(SaveFile& msg) {
|
|
msg << Creeps;
|
|
msg << Pirates;
|
|
msg << majorEmpireCount;
|
|
|
|
uint cnt = getEmpireCount();
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
Empire@ emp = getEmpire(i);
|
|
EmpireTickData@ dat;
|
|
threads[i].getObject(@dat);
|
|
msg << dat.lastTick;
|
|
|
|
msg << cast<Savable>(emp.ResearchGrid);
|
|
msg << cast<Savable>(emp.ResourceManager);
|
|
msg << cast<Savable>(emp.EnergyManager);
|
|
msg << cast<Savable>(emp.ObjectManager);
|
|
msg << cast<Savable>(emp.InfluenceManager);
|
|
msg << cast<Savable>(emp.FleetManager);
|
|
msg << cast<Savable>(emp.Notifications);
|
|
msg << cast<Savable>(emp.Attributes);
|
|
msg << cast<Savable>(emp.Traits);
|
|
msg << cast<Savable>(emp.RandomEvents);
|
|
emp.NS.save(msg);
|
|
msg << emp.Homeworld;
|
|
msg << emp.HomeSystem;
|
|
msg << emp.HomeObj;
|
|
msg << emp.PeaceMask.value;
|
|
msg << emp.ForcedPeaceMask.value;
|
|
msg << emp.AllyMask.value;
|
|
msg << emp.TotalMilitary;
|
|
msg << emp.TotalPlanets;
|
|
msg << emp.VotesWonCounter;
|
|
msg << emp.major;
|
|
msg << emp.points;
|
|
msg << emp.prevPoints;
|
|
msg << emp.team;
|
|
msg << emp.DiplomacyPoints;
|
|
msg << emp.MilitaryStrength;
|
|
msg << emp.PoliticalStrength;
|
|
msg << emp.EmpireStrength;
|
|
msg << emp.Victory;
|
|
msg << emp.cheatLevel;
|
|
|
|
msg << emp.TotalSupportsBuilt.value;
|
|
msg << emp.TotalSupportsActive.value;
|
|
msg << emp.TotalFlagshipsBuilt.value;
|
|
msg << emp.TotalFlagshipsActive.value;
|
|
msg << emp.GlobalLoyalty.value;
|
|
msg << emp.GlobalCharge;
|
|
msg << emp.GlobalTrade;
|
|
msg << emp.ContactMask.value;
|
|
msg << emp.TradeMask.value;
|
|
|
|
msg << emp.ModHP.value;
|
|
msg << emp.ModArmor.value;
|
|
msg << emp.ModShield.value;
|
|
msg << emp.ModSpeed.value;
|
|
|
|
msg << emp.CivilianTradeShips.value;
|
|
msg << emp.SubjugatedBy;
|
|
msg << emp.PathId.value;
|
|
msg << emp.ColonizerName;
|
|
msg << emp.ColonizerModel;
|
|
msg << emp.ColonizerMaterial;
|
|
msg << emp.RaceName;
|
|
msg << emp.mutualDefenseMask;
|
|
|
|
msg << cast<Savable>(emp.EmpireAI);
|
|
}
|
|
}
|
|
|
|
void load(SaveFile& msg) {
|
|
msg >> Creeps;
|
|
if(msg >= SV_0084)
|
|
msg >> Pirates;
|
|
msg >> majorEmpireCount;
|
|
|
|
initCreepCampTypes();
|
|
|
|
uint cnt = getEmpireCount();
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
Empire@ emp = getEmpire(i);
|
|
EmpireTickData dat(emp);
|
|
msg >> dat.lastTick;
|
|
|
|
msg >> cast<Savable>(emp.ResearchGrid);
|
|
msg >> cast<Savable>(emp.ResourceManager);
|
|
msg >> cast<Savable>(emp.EnergyManager);
|
|
msg >> cast<Savable>(emp.ObjectManager);
|
|
if(msg < SV_0119)
|
|
msg >> cast<Savable>(emp.EmpireAI);
|
|
msg >> cast<Savable>(emp.InfluenceManager);
|
|
msg >> cast<Savable>(emp.FleetManager);
|
|
msg >> cast<Savable>(emp.Notifications);
|
|
|
|
emp.initAttributes();
|
|
msg >> cast<Savable>(emp.Attributes);
|
|
emp.syncAttributes();
|
|
|
|
if(msg >= SV_0012)
|
|
msg >> cast<Savable>(emp.Traits);
|
|
if(msg >= SV_0125)
|
|
msg >> cast<Savable>(emp.RandomEvents);
|
|
|
|
emp.NS.load(msg);
|
|
|
|
Object@ hw;
|
|
msg >> hw;
|
|
@emp.Homeworld = cast<Planet>(hw);
|
|
if(msg >= SV_0153) {
|
|
msg >> emp.HomeSystem;
|
|
}
|
|
else {
|
|
if(emp.Homeworld !is null)
|
|
@emp.HomeSystem = emp.Homeworld.region;
|
|
}
|
|
if(msg >= SV_0113) {
|
|
msg >> hw;
|
|
@emp.HomeObj = hw;
|
|
}
|
|
|
|
msg >> emp.PeaceMask.value;
|
|
msg >> emp.ForcedPeaceMask.value;
|
|
msg >> emp.AllyMask.value;
|
|
msg >> emp.TotalMilitary;
|
|
msg >> emp.TotalPlanets;
|
|
msg >> emp.VotesWonCounter;
|
|
msg >> emp.major;
|
|
msg >> emp.points;
|
|
if(msg >= SV_0069)
|
|
msg >> emp.prevPoints;
|
|
if(msg >= SV_0064)
|
|
msg >> emp.team;
|
|
msg >> emp.DiplomacyPoints;
|
|
msg >> emp.MilitaryStrength;
|
|
msg >> emp.PoliticalStrength;
|
|
msg >> emp.EmpireStrength;
|
|
if(msg >= SV_0042)
|
|
msg >> emp.Victory;
|
|
if(msg >= SV_0155)
|
|
msg >> emp.cheatLevel;
|
|
|
|
msg >> emp.TotalSupportsBuilt.value;
|
|
msg >> emp.TotalSupportsActive.value;
|
|
msg >> emp.TotalFlagshipsBuilt.value;
|
|
msg >> emp.TotalFlagshipsActive.value;
|
|
msg >> emp.GlobalLoyalty.value;
|
|
msg >> emp.GlobalCharge;
|
|
if(msg >= SV_0026)
|
|
msg >> emp.GlobalTrade;
|
|
if(msg >= SV_0053)
|
|
msg >> emp.ContactMask.value;
|
|
else
|
|
emp.ContactMask.value = int(~0);
|
|
if(msg >= SV_0057)
|
|
msg >> emp.TradeMask.value;
|
|
else
|
|
emp.TradeMask.value = emp.mask;
|
|
|
|
msg >> emp.ModHP.value;
|
|
msg >> emp.ModArmor.value;
|
|
msg >> emp.ModShield.value;
|
|
msg >> emp.ModSpeed.value;
|
|
|
|
if(msg >= SV_0048)
|
|
msg >> emp.CivilianTradeShips.value;
|
|
if(msg >= SV_0061)
|
|
msg >> emp.SubjugatedBy;
|
|
if(msg >= SV_0067)
|
|
msg >> emp.PathId.value;
|
|
if(msg >= SV_0109) {
|
|
msg >> emp.ColonizerName;
|
|
if(msg >= SV_0118) {
|
|
msg >> emp.ColonizerModel;
|
|
msg >> emp.ColonizerMaterial;
|
|
}
|
|
if(msg >= SV_0125)
|
|
msg >> emp.RaceName;
|
|
if(msg >= SV_0149)
|
|
msg >> emp.mutualDefenseMask;
|
|
}
|
|
|
|
if(msg >= SV_0119)
|
|
msg >> cast<Savable>(emp.EmpireAI);
|
|
|
|
@emp.background = getMaterial(emp.backgroundDef);
|
|
@emp.flag = getMaterial(emp.flagDef);
|
|
@emp.portrait = getMaterial(emp.portraitDef);
|
|
|
|
if(emp.flag is material::error)
|
|
@emp.flag = getMaterial("emp_flag_flag1");
|
|
if(emp.portrait is material::error)
|
|
@emp.portrait = getMaterial("emp_portrait_feyh");
|
|
if(emp.background is material::error)
|
|
@emp.background = getMaterial("emp_bg_blue");
|
|
|
|
if(msg < SV_0085)
|
|
emp.initResearch();
|
|
if(msg < SV_0134)
|
|
emp.ResearchUnlockSpeed = 1.0;
|
|
|
|
threads.insertLast(ScriptThread("empire::empireTickThread", @dat));
|
|
}
|
|
}
|
|
|
|
void tick(double time) {
|
|
//Restart any disabled threads (can happen if there was an error)
|
|
for(uint i = 0, cnt = threads.length; i < cnt; ++i) {
|
|
if(!threads[i].running && threads[i].wasError)
|
|
threads[i].start("empire::empireTickThread");
|
|
}
|
|
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i)
|
|
getEmpire(i).cacheVision();
|
|
}
|
|
|
|
void deinit() {
|
|
uint cnt = threads.length;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
threads[i].stop();
|
|
for(uint i = 0; i < cnt; ++i)
|
|
while(threads[i].running)
|
|
sleep(0);
|
|
threads.length = 0;
|
|
}
|
|
|
|
void playAsEmpire(Player& pl, Empire@ emp) {
|
|
if(emp.player !is null || (!emp.valid && emp !is spectatorEmpire)) {
|
|
info("Player attempted to play as disallowed empire.");
|
|
return;
|
|
}
|
|
if(emp !is spectatorEmpire) {
|
|
pl.linkEmpire(emp);
|
|
string msg = format("[color=#aaa]"+locale::MP_PLAY_EVENT+"[/color]",
|
|
format("[b][color=$1]$2[/color][/b]", toString(emp.color), bbescape(pl.name)),
|
|
format("[b][color=$1]$2[/color][/b]", toString(emp.color), bbescape(emp.name)));
|
|
sendChatMessage(msg, offset=30);
|
|
}
|
|
else {
|
|
string msg = format("[color=#aaa]"+locale::MP_SPECTATING_EVENT+"[/color]",
|
|
format("[b]$1[/b]", bbescape(pl.name)));
|
|
sendChatMessage(msg, offset=30);
|
|
pl.linkEmpire(spectatorEmpire);
|
|
}
|
|
allowPlayEmpire(pl, emp);
|
|
if(pl == CURRENT_PLAYER)
|
|
@playerEmpire = emp;
|
|
}
|
|
|
|
void markDesignObsolete(Player& pl, const Design@ dsg, bool value) {
|
|
if(pl.controls(dsg.owner))
|
|
dsg.setObsolete(value);
|
|
}
|
|
|
|
class EmpireTickData {
|
|
double lastTick = gameTime;
|
|
double aiTickTimer = -1.0;
|
|
uint nextStep = 0;
|
|
Empire@ empire;
|
|
|
|
double[] tickMoments(5, gameTime);
|
|
|
|
EmpireTickData(Empire& Emp) {
|
|
@empire = @Emp;
|
|
//Attempt to distribute empire ticks over time
|
|
nextStep = empire.id;
|
|
lastTick += double(empire.id % 4) * 0.25 / 4.0;
|
|
aiTickTimer -= double(empire.id % 4) * 0.25 / 4.0;
|
|
}
|
|
};
|
|
|
|
double empireTickThread(double time, ScriptThread& thread) {
|
|
if(!game_running)
|
|
return 0.05;
|
|
|
|
EmpireTickData@ data;
|
|
thread.getObject(@data);
|
|
Empire@ emp = data.empire;
|
|
|
|
double curTime = gameTime;
|
|
double tick = curTime - data.lastTick;
|
|
double speed = gameSpeed;
|
|
|
|
if(speed > 0.0) {
|
|
if(ai_full_speed.value == 1.0)
|
|
speed = 1.0;
|
|
data.aiTickTimer += tick / speed;
|
|
if(data.aiTickTimer > 0.25) {
|
|
emp.aiTick(data.aiTickTimer);
|
|
data.aiTickTimer = randomd(-0.02,0.02);
|
|
}
|
|
}
|
|
|
|
if(tick >= 0.05) {
|
|
data.lastTick = curTime;
|
|
|
|
switch(data.nextStep++ % 5) {
|
|
case 0: {
|
|
double t = curTime - data.tickMoments[0];
|
|
if(t > 0.0) {
|
|
emp.resourceTick(t);
|
|
EGCoordinationTick(emp, t);
|
|
}
|
|
data.tickMoments[0] = curTime;
|
|
} break;
|
|
case 1: {
|
|
double t = curTime - data.tickMoments[1];
|
|
if(t > 0.0)
|
|
emp.planetTick(t);
|
|
data.tickMoments[1] = curTime;
|
|
} break;
|
|
case 2: {
|
|
double t = curTime - data.tickMoments[2];
|
|
if(t > 0.0)
|
|
emp.influenceTick(t);
|
|
data.tickMoments[2] = curTime;
|
|
} break;
|
|
case 3: {
|
|
double t = curTime - data.tickMoments[3];
|
|
if(t > 0.0) {
|
|
emp.researchTick(t);
|
|
emp.powerTick(t);
|
|
}
|
|
data.tickMoments[3] = curTime;
|
|
} break;
|
|
case 4: {
|
|
double t = curTime - data.tickMoments[4];
|
|
if(t > 0.0) {
|
|
emp.attributesTick(t);
|
|
emp.traitsTick(t);
|
|
emp.eventsTick(t);
|
|
}
|
|
data.tickMoments[4] = curTime;
|
|
} break;
|
|
}
|
|
|
|
return 0.05;
|
|
}
|
|
else {
|
|
return 0.05 - tick;
|
|
}
|
|
}
|
|
|
|
bool sendPeriodic(Message& msg) {
|
|
uint cnt = getEmpireCount();
|
|
for(uint i = 0; i < cnt; ++i)
|
|
sendPeriodic(getEmpire(i), msg);
|
|
return true;
|
|
}
|
|
|
|
void syncInitial(Message& msg) {
|
|
uint cnt = getEmpireCount();
|
|
for(uint i = 0; i < cnt; ++i)
|
|
syncInitial(getEmpire(i), msg);
|
|
}
|
|
|
|
void sendChatMessage(const string& text, const string& user = "*", const Color& color = colors::White, int offset = 100) {
|
|
string msg = format("[b][color=$1]$2[/color][/b][offset=$4]$3[/offset]",
|
|
toString(color), bbescape(user), text, toString(offset));
|
|
recvMPChat(ALL_PLAYERS, msg);
|
|
}
|
|
|
|
void chatMessage(Player& pl, string text, uint empMask = 0xffffffff, string spec = "") {
|
|
Color color;
|
|
if(pl.emp !is null)
|
|
color = pl.emp.color;
|
|
string txt = bbescape(text);
|
|
if(spec.length != 0)
|
|
txt = "[color=#aaa]("+bbescape(spec)+")[/color] "+txt;
|
|
string msg = format("[b][color=$1]$2[/color][/b][offset=100]$3[/offset]",
|
|
toString(color), bbescape(pl.name), txt);
|
|
if(empMask == 0xffffffff) {
|
|
recvMPChat(ALL_PLAYERS, msg);
|
|
}
|
|
else {
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
|
auto@ other = getEmpire(i);
|
|
if(empMask & other.mask != 0 && other.player !is null)
|
|
recvMPChat(other.player, msg);
|
|
}
|
|
}
|
|
}
|