Files
starruler-linux/scripts/server/influence_global.as
T
2018-07-17 14:15:37 +02:00

1488 lines
34 KiB
ActionScript

import influence;
import saving;
import attributes;
from influence import InfluenceStore;
from empire import majorEmpireCount;
import notifications;
from notifications import NotificationStore;
import util.formatting;
from achievements import giveAchievement;
Mutex influenceLock;
// {{{ Galactic Influence
locked_int galacticInfluence = 0;
bool activeDelta = false;
double leaderCardTimer = 0;
Empire@ SenateLeader;
Empire@ getSenateLeader() {
return SenateLeader;
}
void electSenateLeader(Empire@ emp) {
{
Lock lck(influenceLock);
@SenateLeader = emp;
}
if(emp is playerEmpire || emp.player !is null)
giveAchievement(emp, "ACH_SENATE_LEADER");
}
void modGalacticInfluence(int mod) {
galacticInfluence += mod;
}
double getInfluenceIncome(int stock, int stored, double factor) {
int total = max(galacticInfluence.value, 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.value, 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);
}
// }}}
// {{{ Cards
array<StackInfluenceCard@> cardStack;
array<StackInfluenceCard@> deck;
int nextStackId = 1;
uint stackSize;
double drawInterval;
bool stackDelta = false;
double drawTimer = 0.0;
double getInfluenceDrawInterval() {
return drawInterval;
}
double getInfluenceDrawTimer() {
return drawTimer;
}
void saveStack(SaveFile& file) {
file << drawTimer;
file << nextStackId;
file << stackSize;
file << drawInterval;
uint cnt = cardStack.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << cardStack[i];
cnt = deck.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << deck[i];
}
void loadStack(SaveFile& file) {
file >> drawTimer;
file >> nextStackId;
file >> stackSize;
file >> drawInterval;
uint cnt = 0;
file >> cnt;
cardStack.length = cnt;
for(uint i = 0; i < cnt; ++i)
@cardStack[i] = StackInfluenceCard(file);
if(file >= SV_0034) {
file >> cnt;
deck.length = cnt;
for(uint i = 0; i < cnt; ++i)
@deck[i] = StackInfluenceCard(file);
}
}
void writeStack(Message& msg, bool initial = false) {
if(initial)
msg << drawInterval;
msg << drawTimer;
if(stackDelta || initial) {
msg.write1();
Lock lock(influenceLock);
uint cnt = cardStack.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << cardStack[i];
if(!initial)
stackDelta = false;
}
else {
msg.write0();
}
}
void initStack() {
Lock lock(influenceLock);
//Precalculate stack
stackSize = clamp(config::CARD_STACK_BASE + config::CARD_STACK_PER_PLAYER * majorEmpireCount,
config::CARD_STACK_MIN, config::CARD_STACK_MAX);
drawInterval = config::CARD_STACK_DRAW_INTERVAL;
//Create the initial deck
generateDeck();
//Cut down first deck to a randomized size
deck.reverse();
deck.length = randomi(stackSize, deck.length);
deck.reverse();
//Create the initial stack
for(uint i = 0; i < stackSize; ++i)
drawOntoStack();
//Set stack card placement
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i)
cardStack[i].placement = i;
//Start the timers
drawTimer = drawInterval;
}
void modInfluenceStackSize(int mod) {
Lock lock(influenceLock);
stackSize = clamp(stackSize + mod, config::CARD_STACK_MIN, config::CARD_STACK_MAX);
}
void generateDeck() {
Lock lock(influenceLock);
//Create all the cards
array<StackInfluenceCard@> events;
deck.reserve(getInfluenceDeckSize());
for(uint i = 0, cnt = getInfluenceCardTypeCount(); i < cnt; ++i) {
auto@ type = getInfluenceCardType(i);
if(type.cls == ICC_Event && config::ENABLE_INFLUENCE_EVENTS == 0)
continue;
if(type.dlc.length != 0 && !hasDLC(type.dlc))
continue;
if(!type.canGenerateOnStack())
continue;
int amt = int(floor(type.frequency));
double partial = type.frequency - floor(type.frequency);
if(randomd() < partial)
amt += 1;
for(int n = 0; n < amt; ++n) {
StackInfluenceCard card;
card.id = nextStackId++;
type.generate(card);
if(type.cls == ICC_Event)
events.insertLast(card);
else
deck.insertLast(card);
}
}
//Shuffle the deck
for(int i = deck.length - 1; i >= 0; --i) {
int swapIndex = randomi(0, i);
auto@ first = deck[i];
auto@ second = deck[swapIndex];
@deck[i] = second;
@deck[swapIndex] = first;
}
//Seed in the events
if(events.length != 0) {
uint eventCount = round(randomd(config::INFLUENCE_EVENT_FREQ_MIN, config::INFLUENCE_EVENT_FREQ_MAX) * double(deck.length));
eventCount = min(eventCount, events.length);
uint spacing = deck.length / max(eventCount, 2);
uint index = randomi(0, spacing/2);
for(uint i = 0; i < eventCount; ++i) {
uint takeIndex = randomi(0, events.length-1);
deck.insertAt(index, events[takeIndex]);
events.removeAt(takeIndex);
index += spacing;
}
}
//Print out the deck
//print("Generated deck: "+deck.length);
//for(int i = deck.length - 1; i >= 0; --i) {
// print(" * "+deck[i].type.name);
//}
}
void drawOntoStack() {
Lock lock(influenceLock);
//Generate new deck if needed
if(deck.length == 0)
generateDeck();
//Take card from the top of the deck
StackInfluenceCard@ card = deck.last;
deck.removeLast();
//Add the card to the stack
cardStack.insertLast(card);
card.enterStack();
//Activate event cards
if(card.type.cls == ICC_Event) {
@card.owner = defaultEmpire;
card.play(Targets(card.targets));
}
stackDelta = true;
}
void rotateInfluenceStack() {
Lock lock(influenceLock);
for(int i = cardStack.length - 1; i >= 0; --i) {
auto@ card = cardStack[i];
if(card.purchasedBy !is null || i == 0)
cardStack.removeAt(i);
}
while(cardStack.length < stackSize)
drawOntoStack();
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i)
cardStack[i].placement = i;
stackDelta = true;
drawTimer = drawInterval;
}
void tickStack(double time) {
Lock lock(influenceLock);
//Draw new cards periodically
drawTimer -= time;
while(drawTimer <= 0.0)
rotateInfluenceStack();
//Tick all the cards on the stack
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i)
cardStack[i].tickStack(time);
}
array<const StackInfluenceCard@>@ getInfluenceStack() {
array<const StackInfluenceCard@> cards;
{
Lock lock(influenceLock);
cards.length = cardStack.length;
for(uint i = 0; i < cards.length; ++i) {
StackInfluenceCard card;
card = cardStack[i];
@cards[i] = card;
}
}
return cards;
}
void disableBuyCardsTargetedAgainst(Empire& emp) {
Lock lock(influenceLock);
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i) {
auto@ card = cardStack[i];
if(card.purchasedBy !is null)
continue;
if(card.targets.length == 0)
continue;
auto@ targ = card.targets[0];
if(targ.filled && targ.emp is emp) {
@card.purchasedBy = emp;
stackDelta = true;
}
}
}
void buyCardFromInfluenceStack(Player& pl, int id) {
Empire@ emp = pl.emp;
if(emp is null || !emp.valid)
return;
buyCardFromInfluenceStack(emp, id, pay=true);
}
void buyCardFromInfluenceStack(Empire@ emp, int id, bool pay = true) {
StackInfluenceCard@ card;
{
Lock lock(influenceLock);
uint index = 0;
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i) {
if(cardStack[i].id == id) {
index = i;
@card = cardStack[i];
break;
}
}
if(card is null)
return;
if(!card.canPurchase(emp))
return;
if(pay) {
if(!card.purchaseConsume(emp))
return;
int cost = card.getPurchaseCost(emp);
if(!emp.consumeInfluence(cost))
return;
emp.modAttribute(EA_InfluenceCardsBought, AC_Add, 1.0);
int extraQuality = floor(emp.InfluenceBuysExtraQuality);
if(extraQuality != 0) {
card.quality += extraQuality;
card.quality = max(card.quality, card.type.minQuality);
if(!card.type.canOverquality || card.type.minQuality == card.type.maxQuality)
card.quality = min(card.quality, card.type.maxQuality);
}
int extraUses = floor(emp.InfluenceBuysExtraUses);
if(extraUses != 0)
card.uses += extraUses;
}
//Remove from the stack
@card.purchasedBy = emp;
card.leaveStack(true);
}
//Add to the empire
int cardId = cast<InfluenceStore>(emp.InfluenceManager).addCard(emp, card);
if(card.type.cls == ICC_Instant)
cast<InfluenceStore>(emp.InfluenceManager).playCard(emp, cardId, card.targets);
stackDelta = true;
}
void getInfluenceCardStack() {
Lock lock(influenceLock);
for(uint i = 0, cnt = cardStack.length; i < cnt; ++i)
yield(cardStack[i]);
}
void playInfluenceCard(Player& pl, int id, Targets@ targets, uint voteId = uint(-1)) {
Empire@ emp = pl.emp;
if(emp is null || !emp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote;
if(voteId != uint(-1))
@vote = getInfluenceVoteByID(voteId);
cast<InfluenceStore>(emp.InfluenceManager).playCard(emp, id, targets, true, vote = vote);
}
void playInfluenceCard_server(Empire@ emp, int id, Targets@ targets, uint voteId = uint(-1)) {
if(emp is null || !emp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote;
if(voteId != uint(-1))
@vote = getInfluenceVoteByID(voteId);
cast<InfluenceStore>(emp.InfluenceManager).playCard(emp, id, targets, true, vote = vote);
}
void fillInfluenceStackWithLeverage(Empire@ against, int count = -1) {
if(against is null || !against.valid)
return;
auto@ type = getInfluenceCardType("Leverage");
if(type is null)
return;
Lock lock(influenceLock);
if(count == -1)
count = cardStack.length;
for(int i = 0; i < count; ++i) {
StackInfluenceCard card;
card.id = nextStackId++;
type.generate(card);
auto@ targ = card.targets.fill("onEmpire");
if(targ is null)
return;
@targ.emp = against;
deck.insertLast(card);
}
}
// }}}
// {{{ Votes
array<InfluenceVote@> voteList;
array<InfluenceVote@> activeVotes;
bool activeVoteDelta = false;
void saveVotes(SaveFile& file) {
uint cnt = voteList.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << voteList[i];
cnt = activeVotes.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << activeVotes[i].id;
}
void loadVotes(SaveFile& file) {
uint cnt = 0;
file >> cnt;
voteList.length = cnt;
for(uint i = 0; i < cnt; ++i)
@voteList[i] = InfluenceVote(file);
file >> cnt;
activeVotes.length = cnt;
for(uint i = 0; i < cnt; ++i) {
uint id = 0;
file >> id;
@activeVotes[i] = voteList[id];
}
}
void writeVotes(Message& msg, bool initial = false) {
Lock lock(influenceLock);
msg.writeAlign();
uint pos = msg.reserve();
uint amount = 0;
for(uint i = 0, cnt = voteList.length; i < cnt; ++i) {
if(voteList[i].delta || initial) {
msg << i;
msg << voteList[i];
++amount;
if(!initial)
voteList[i].delta = false;
}
}
msg.fill(pos, amount);
if(activeVoteDelta || initial) {
msg.write1();
if(!initial)
activeVoteDelta = false;
uint cnt = activeVotes.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << activeVotes[i].id;
}
else {
msg.write0();
}
}
uint getLastInfluenceVoteId() {
return voteList.length - 1;
}
InfluenceVote@ getInfluenceVoteByID(uint id) {
Lock lock(influenceLock);
if(id >= voteList.length)
return null;
return voteList[id];
}
void syncInfluenceVote(uint id, InfluenceVote& vote) {
Lock lock(influenceLock);
if(id >= voteList.length)
return;
vote = 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(influenceLock);
if(id >= voteList.length)
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(influenceLock);
for(uint i = 0, cnt = activeVotes.length; i < cnt; ++i) {
auto@ vote = activeVotes[i];
if(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(influenceLock);
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;
}
}
}
array<InfluenceVoteStub@>@ getActiveInfluenceVotes() {
array<InfluenceVoteStub@> stubs;
if(activeVotes.isEmpty())
return stubs;
{
Lock lock(influenceLock);
stubs.length = activeVotes.length;
for(uint i = 0, cnt = activeVotes.length; i < cnt; ++i)
@stubs[i] = InfluenceVoteStub(activeVotes[i]);
}
return stubs;
}
array<InfluenceVoteStub@>@ getInfluenceVotesSince(uint id) {
if(activeVotes.isEmpty())
return null;
array<InfluenceVoteStub@>@ stubs = null;
{
Lock lock(influenceLock);
for(uint i = 0, cnt = activeVotes.length; i < cnt; ++i) {
if(id != uint(-1) && activeVotes[i].id <= id)
continue;
if(stubs is null)
@stubs = array<InfluenceVoteStub@>();
stubs.insertLast(InfluenceVoteStub(activeVotes[i]));
}
}
return stubs;
}
array<InfluenceVoteStub@>@ getActiveInfluenceVotes_server() {
return getActiveInfluenceVotes();
}
InfluenceVote@ startInfluenceVote(Empire@ byEmpire, const InfluenceVoteType@ type, Targets@ targets, InfluenceCard@ fromCard = null) {
Lock lock(influenceLock);
InfluenceVote vote(type);
if(!vote.checkTargets(targets))
return null;
if(byEmpire !is defaultEmpire)
vote.empiresPresent &= byEmpire.ContactMask.value;
vote.id = voteList.length;
vote.targets = targets;
voteList.insertLast(vote);
activeVotes.insertLast(vote);
vote.start(targets, byEmpire, fromCard);
activeVoteDelta = true;
return vote;
}
void tickVotes(double time) {
Lock lock(influenceLock);
for(int i = activeVotes.length - 1; i >= 0; --i) {
auto@ vote = activeVotes[i];
if(vote.active) {
vote.tick(time);
}
if(!vote.active) {
activeVotes.removeAt(i);
activeVoteDelta = true;
}
}
}
void withdrawVote_client(Player& pl, uint voteId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.active && vote.startedBy is plEmp)
vote.withdraw();
}
void leaveVote_client(Player& pl, uint voteId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.active && vote.isPresent(plEmp)) {
vote.addEvent(InfluenceVoteEvent(IVET_Leave, plEmp));
vote.leave(plEmp);
}
}
void makeInfluenceVoteOffer_client(Player& pl, uint voteId, InfluenceVoteOffer@ offer) {
@offer.fromEmpire = pl.emp;
if(offer.fromEmpire is null || !offer.fromEmpire.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.isPresent(offer.fromEmpire))
vote.makeOffer(offer);
}
void claimInfluenceVoteOffer_client(Player& pl, uint voteId, int offerId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.isPresent(plEmp))
vote.claimOffer(plEmp, offerId);
}
void makeInfluenceVoteOffer_server(Empire@ emp, uint voteId, InfluenceVoteOffer@ offer) {
@offer.fromEmpire = emp;
if(offer.fromEmpire is null || !offer.fromEmpire.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.isPresent(offer.fromEmpire))
vote.makeOffer(offer);
}
void claimInfluenceVoteOffer_server(Empire@ emp, uint voteId, int offerId) {
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.isPresent(emp))
vote.claimOffer(emp, offerId);
}
void voteMessage_client(Player& pl, uint voteId, string message) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
InfluenceVote@ vote = getInfluenceVoteByID(voteId);
if(vote !is null && vote.isPresent(plEmp)) {
InfluenceVoteEvent evt(IVET_Message, plEmp);
evt.text = message;
vote.addEvent(evt);
}
}
void makeDonation_client(Player& pl, Empire@ toEmp, DiplomacyOffer@ offer) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid || toEmp is null || !toEmp.valid)
return;
makeDonation(plEmp, toEmp, offer);
}
void makeDonation(Empire@ emp, Empire@ toEmp, DiplomacyOffer@ offer) {
if(!offer.canOffer(emp))
return;
offer.memo(emp);
offer.take(emp);
offer.give(emp, toEmp, delayMaintenance = true, preventFloat = true);
DonationNotification n;
n.offer = offer;
@n.fromEmpire = emp;
cast<NotificationStore>(toEmp.Notifications).addNotification(toEmp, n);
}
// }}}
// {{{ Effects
array<InfluenceEffect@> activeEffects;
int nextEffectId = 1;
bool activeEffectDelta = false;
void saveEffects(SaveFile& file) {
file << nextEffectId;
uint cnt = activeEffects.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << activeEffects[i];
}
void loadEffects(SaveFile& file) {
file >> nextEffectId;
uint cnt = 0;
file >> cnt;
activeEffects.length = cnt;
for(uint i = 0; i < cnt; ++i)
@activeEffects[i] = InfluenceEffect(file);
}
void writeEffects(Message& msg, bool initial = false) {
Lock lock(influenceLock);
uint cnt = activeEffects.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i) {
if(activeEffects[i].delta || initial || activeEffectDelta) {
msg.write1();
msg << activeEffects[i];
if(!initial)
activeEffects[i].delta = false;
}
else {
msg.write0();
}
}
if(!initial)
activeEffectDelta = false;
}
int getLastInfluenceEffectId() {
return nextEffectId - 1;
}
void getActiveInfluenceEffects_client() {
Lock lock(influenceLock);
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i)
yield(activeEffects[i]);
}
array<InfluenceEffect@>@ getActiveInfluenceEffects() {
array<InfluenceEffect@> stubs;
if(activeEffects.isEmpty())
return stubs;
{
Lock lock(influenceLock);
stubs.length = activeEffects.length;
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i) {
InfluenceEffect eff;
eff = activeEffects[i];
@stubs[i] = eff;
}
}
return stubs;
}
InfluenceEffect@ getRandomActiveInfluenceEffect() {
if(activeEffects.length == 0)
return null;
InfluenceEffect eff;
{
Lock lock(influenceLock);
if(activeEffects.length == 0)
return null;
eff = activeEffects[randomi(0, activeEffects.length-1)];
}
return eff;
}
Empire@ getTaggedEffectOwner(const string& tag) {
if(activeEffects.length == 0)
return null;
Lock lock(influenceLock);
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i) {
if(activeEffects[i].type.hasTag(tag))
return activeEffects[i].owner;
}
return null;
}
array<InfluenceEffect@>@ getInfluenceEffectsSince(int id) {
if(activeEffects.isEmpty())
return null;
array<InfluenceEffect@>@ stubs = null;
{
Lock lock(influenceLock);
for(uint i = 0, cnt = activeEffects.length; i < cnt; ++i) {
if(activeEffects[i].id <= id)
continue;
if(stubs is null)
@stubs = array<InfluenceEffect@>();
InfluenceEffect eff;
eff = activeEffects[i];
stubs.insertLast(eff);
}
}
return stubs;
}
InfluenceEffect@ createInfluenceEffect(Empire@ byEmpire, const InfluenceEffectType@ type, Targets@ targets, double duration = 0.0) {
Lock lock(influenceLock);
InfluenceEffect effect(type);
if(!effect.checkTargets(targets))
return null;
effect.id = nextEffectId++;
effect.targets = targets;
activeEffects.insertLast(effect);
effect.start(targets, byEmpire, duration);
activeEffectDelta = true;
return effect;
}
void tickEffects(double time) {
Lock lock(influenceLock);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.active)
effect.tick(time);
if(!effect.active) {
activeEffectDelta = true;
activeEffects.removeAt(i);
}
}
}
void dismissEffect_client(Player& pl, int id) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.id == id) {
if(!effect.canDismiss(plEmp))
return;
effect.dismiss(plEmp);
if(!effect.active) {
activeEffectDelta = true;
activeEffects.removeAt(i);
}
return;
}
}
}
void dismissEffect(Empire@ byEmpire, int id) {
Lock lock(influenceLock);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.id == id) {
effect.dismiss(byEmpire);
if(!effect.active) {
activeEffectDelta = true;
activeEffects.removeAt(i);
}
return;
}
}
}
InfluenceEffect@ getInfluenceEffect(int id) {
Lock lock(influenceLock);
for(int i = activeEffects.length - 1; i >= 0; --i) {
auto@ effect = activeEffects[i];
if(effect.id == id)
return effect;
}
return null;
}
Empire@ getInfluenceEffectOwner(int id) {
Lock lock(influenceLock);
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(influenceLock);
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
array<Treaty@> activeTreaties;
int nextTreatyId = 1;
bool activeTreatiesDelta = false;
void saveTreaties(SaveFile& file) {
file << nextTreatyId;
uint cnt = activeTreaties.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << activeTreaties[i];
}
void loadTreaties(SaveFile& file) {
if(file < SV_0057)
return;
file >> nextTreatyId;
uint cnt = 0;
file >> cnt;
activeTreaties.length = cnt;
for(uint i = 0; i < cnt; ++i) {
@activeTreaties[i] = Treaty();
file >> activeTreaties[i];
}
}
void writeTreaties(Message& msg, bool initial = false) {
Lock lock(influenceLock);
uint cnt = activeTreaties.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i) {
if(activeTreaties[i].delta || initial || activeTreatiesDelta) {
msg.write1();
msg << activeTreaties[i];
if(!initial)
activeTreaties[i].delta = false;
}
else {
msg.write0();
}
}
if(!initial)
activeTreatiesDelta = false;
}
void getActiveInfluenceTreaties_client(Player& pl) {
Empire@ plEmp = pl.emp;
Lock lock(influenceLock);
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
if(activeTreaties[i].isVisibleTo(plEmp))
yield(activeTreaties[i]);
}
}
void createTreaty_client(Player& pl, Treaty@ treaty) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
if(plEmp.SubjugatedBy !is null)
return;
for(uint i = 0, cnt = treaty.clauses.length; i < cnt; ++i) {
if(!treaty.clauses[i].type.freeClause)
return;
}
uint invMask = treaty.inviteMask;
treaty.inviteMask = 0;
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
auto@ other = getEmpire(i);
if(other.mask & invMask == 0)
continue;
if(!treaty.canInvite(plEmp, other))
return;
}
treaty.inviteMask = invMask;
if(treaty.joinedEmpires.length != 0)
return;
createTreaty(plEmp, treaty);
}
Treaty@ createTreaty(Empire@ byEmpire, Treaty@ treaty) {
Lock lock(influenceLock);
treaty.init(byEmpire);
treaty.join(byEmpire, force = true);
treaty.id = nextTreatyId++;
activeTreaties.insertLast(treaty);
activeTreatiesDelta = true;
if(treaty.joinedEmpires.length >= 2)
treaty.start();
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
auto@ other = getEmpire(i);
if(!other.major || treaty.inviteMask & other.mask == 0)
continue;
other.notifyTreaty(treaty.id, TET_Invite, byEmpire, other);
}
return treaty;
}
void offerSurrender_client(Player& pl, Empire@ toEmp) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
offerSurrender(plEmp, toEmp);
}
void offerSurrender(Empire@ from, Empire@ to) {
auto@ subjugateClause = getInfluenceClauseType("SubjugateClause");
if(subjugateClause is null)
return;
if(from.SubjugatedBy !is null || to.SubjugatedBy !is null)
return;
if(from.team != -1 && config::ALLOW_TEAM_SURRENDER == 0)
return;
Treaty treaty;
treaty.inviteMask = to.mask;
treaty.name = format(locale::SURRENDER_OFFER, formatEmpireName(from));
Lock lock(influenceLock);
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
auto@ other = activeTreaties[i];
if(other.leader !is null)
continue;
if(other.inviteMask != treaty.inviteMask)
continue;
if(other.presentMask != from.mask)
continue;
if(!other.hasClause(subjugateClause))
continue;
return;
}
treaty.addClause(subjugateClause);
treaty.addClause(getInfluenceClauseType("ConstClause"));
createTreaty(from, treaty);
}
void demandSurrender_client(Player& pl, Empire@ toEmp) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
demandSurrender(plEmp, toEmp);
}
void demandSurrender(Empire@ from, Empire@ to) {
auto@ subjugateClause = getInfluenceClauseType("SubjugateClause");
if(subjugateClause is null)
return;
if(from.SubjugatedBy !is null || to.SubjugatedBy !is null)
return;
if(to.team != -1 && config::ALLOW_TEAM_SURRENDER == 0)
return;
Treaty treaty;
@treaty.leader = from;
treaty.inviteMask = to.mask;
treaty.name = format(locale::SURRENDER_DEMAND, formatEmpireName(to));
Lock lock(influenceLock);
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
auto@ other = activeTreaties[i];
if(other.leader !is treaty.leader)
continue;
if(other.inviteMask != treaty.inviteMask)
continue;
if(!other.hasClause(subjugateClause))
continue;
return;
}
treaty.addClause(subjugateClause);
treaty.addClause(getInfluenceClauseType("ConstClause"));
createTreaty(from, treaty);
}
void forceSubjugate(Empire& master, Empire& vassal) {
auto@ subjugateClause = getInfluenceClauseType("SubjugateClause");
if(subjugateClause is null)
return;
if(master.SubjugatedBy !is null || vassal.SubjugatedBy !is null)
return;
if(master.team != -1 && master.team == vassal.team)
return;
Treaty treaty;
@treaty.leader = master;
treaty.inviteMask = vassal.mask;
treaty.name = format(locale::SURRENDER_DEMAND, formatEmpireName(master));
treaty.addClause(subjugateClause);
treaty.addClause(getInfluenceClauseType("ConstClause"));
Lock lock(influenceLock);
createTreaty(master, treaty);
joinTreaty(vassal, treaty.id);
}
void inviteToTreaty_client(Player& pl, int treatyId, Empire@ invite) {
if(invite is null)
return;
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
if(!treaty.canInvite(plEmp, invite))
return;
treaty.invite(plEmp, invite);
}
void inviteToTreaty(Empire& emp, int treatyId, Empire& invite, bool force = false) {
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
if(!force && !treaty.canInvite(emp, invite))
return;
treaty.invite(emp, invite);
}
void joinTreaty_client(Player& pl, int treatyId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
joinTreaty(plEmp, treatyId);
}
void joinTreaty(Empire& emp, int treatyId, bool force = false) {
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
treaty.join(emp, force=force);
}
void leaveTreaty_client(Player& pl, int treatyId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
leaveTreaty(plEmp, treatyId);
}
void leaveTreaty(Empire& emp, int treatyId, bool force = false) {
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
if(!force && !treaty.canLeave(emp))
return;
if(emp !is treaty.leader) {
treaty.leave(emp);
_checkTreaty(treaty);
}
else {
endTreaty(treaty.id);
}
}
void sendPeaceOffer_client(Player& pl, Empire& toEmp) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
sendPeaceOffer(plEmp, toEmp);
}
void sendPeaceOffer(Empire& from, Empire@ to) {
auto@ clause = getInfluenceClauseType("PeaceClause");
if(clause is null)
return;
if(from.SubjugatedBy !is null || to.SubjugatedBy !is null)
return;
Treaty treaty;
treaty.inviteMask = to.mask;
treaty.name = format(locale::PEACE_OFFER, formatEmpireName(from));
Lock lock(influenceLock);
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
auto@ other = activeTreaties[i];
if(other.leader !is null)
continue;
if(!other.hasClause(clause))
continue;
if(other.presentMask == to.mask) {
if(other.inviteMask & from.mask != 0) {
joinTreaty(from, other.id);
return;
}
}
else if(other.presentMask == from.mask) {
if(other.inviteMask == treaty.inviteMask)
return;
}
}
treaty.addClause(clause);
createTreaty(from, treaty);
}
void leaveTreatiesWith(Empire& emp, uint leaveMask) {
Lock lock(influenceLock);
for(int i = activeTreaties.length - 1; i >= 0; --i) {
auto@ treaty = activeTreaties[i];
if(treaty is null)
continue;
if(treaty.presentMask & leaveMask == 0)
continue;
if(!treaty.canLeave(emp))
continue;
if(emp !is treaty.leader) {
treaty.leave(emp);
_checkTreaty(treaty);
}
else {
endTreaty(treaty.id);
}
}
}
bool isInTreatiesWith(Empire& emp, uint otherMask) {
Lock lock(influenceLock);
for(int i = activeTreaties.length - 1; i >= 0; --i) {
auto@ treaty = activeTreaties[i];
if(treaty is null)
continue;
if(treaty.presentMask & otherMask != 0)
return true;
}
return false;
}
void declineTreaty_client(Player& pl, int treatyId) {
Empire@ plEmp = pl.emp;
if(plEmp is null || !plEmp.valid)
return;
declineTreaty(plEmp, treatyId);
}
void declineTreaty(Empire& emp, int treatyId) {
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
if(treaty.inviteMask & emp.mask == 0)
return;
treaty.decline(emp);
_checkTreaty(treaty);
}
void _checkTreaty(Treaty@ treaty) {
uint potential = 0, actual = 0;
if(treaty.leader !is null) {
potential += 1;
actual += 1;
}
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
auto@ other = getEmpire(i);
if(other is treaty.leader)
continue;
if(treaty.presentMask & other.mask != 0) {
potential += 1;
actual += 1;
}
else if(treaty.inviteMask & other.mask != 0) {
potential += 1;
}
}
if(potential <= 1)
endTreaty(treaty.id);
else if(actual <= 1 && treaty.started)
treaty.end();
}
void endTreaty(int treatyId) {
Lock lock(influenceLock);
Treaty@ treaty = getTreaty(treatyId);
if(treaty is null)
return;
treaty.end();
activeTreaties.remove(treaty);
activeTreatiesDelta = true;
}
void tickTreaties(double time) {
Lock lock(influenceLock);
for(int i = activeTreaties.length - 1; i >= 0; --i) {
auto@ treaty = activeTreaties[i];
if(treaty.started) {
treaty.tick(time);
if(!treaty.started) {
activeTreatiesDelta = true;
activeTreaties.remove(treaty);
}
}
}
}
Treaty@ getTreaty(uint id) {
Lock lock(influenceLock);
for(int i = activeTreaties.length - 1; i >= 0; --i) {
auto@ treaty = activeTreaties[i];
if(treaty.id == id)
return treaty;
}
return null;
}
Treaty@ getTreatyDesc(uint id) {
Lock lock(influenceLock);
for(int i = activeTreaties.length - 1; i >= 0; --i) {
auto@ treaty = activeTreaties[i];
if(treaty.id == id) {
Treaty output;
output = treaty;
return output;
}
}
return null;
}
// }}}
// {{{ Rewards
void giveRandomReward(Empire& emp, double magnitude = 1.0) {
switch(randomi(0,3)) {
case 0: emp.addBonusBudget(int(magnitude * 100.0)); break;
case 1: emp.addInfluence(magnitude * 0.5 * emp.InfluenceEfficiency); break;
case 2: emp.generatePoints(magnitude * 8.0); break;
case 3: emp.modEnergyStored(magnitude * 50.0 * emp.EnergyEfficiency); break;
}
}
// }}}
void init() {
if(isLoadedSave)
return;
initStack();
}
void tick(double time) {
tickStack(time);
tickVotes(time);
tickEffects(time);
tickTreaties(time);
leaderCardTimer += time;
if(leaderCardTimer >= config::SENATE_LEADER_CARD_TIMER) {
leaderCardTimer = 0.0;
auto@ leader = SenateLeader;
if(leader !is null) {
const InfluenceCardType@ giveCard;
double checked = 0.0;
for(uint i = 0, cnt = getInfluenceCardTypeCount(); i < cnt; ++i) {
auto@ type = getInfluenceCardType(i);
if(!type.leaderOnly)
continue;
if(type.frequency == 0)
continue;
if(leader.getUsesOfCardType(type.id) != 0)
continue;
checked += 1.0;
if(randomd() < 1.0 / checked)
@giveCard = type;
}
if(giveCard !is null) {
InfluenceCard@ card = giveCard.create(uses=1);
cast<InfluenceStore>(leader.InfluenceManager).addCard(leader, card);
leader.notifyGeneric(
locale::NOTIFY_LEADER_CARD,
format(locale::NOTIFY_LEADER_CARD_DESC, card.formatTitle()),
getSpriteDesc(card.type.icon));
}
}
}
}
void save(SaveFile& file) {
file << galacticInfluence;
file << SenateLeader;
file << leaderCardTimer;
saveStack(file);
saveVotes(file);
saveEffects(file);
saveTreaties(file);
}
void load(SaveFile& file) {
file >> galacticInfluence;
file >> SenateLeader;
if(file >= SV_0141)
file >> leaderCardTimer;
loadStack(file);
loadVotes(file);
loadEffects(file);
loadTreaties(file);
}
void syncInitial(Message& msg) {
msg << galacticInfluence;
msg << SenateLeader;
writeStack(msg, true);
writeVotes(msg, true);
writeEffects(msg, true);
writeTreaties(msg, true);
}
bool sendPeriodic(Message& msg) {
msg << galacticInfluence;
msg << SenateLeader;
writeStack(msg);
writeVotes(msg);
writeEffects(msg);
writeTreaties(msg);
return true;
}