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

644 lines
14 KiB
ActionScript

import influence;
import saving;
from influence import InfluenceStore;
from influence_global import influenceLock;
import void modGalacticInfluence(int mod) from "influence_global";
import double getInfluenceIncome(int stock, int stored, double factor) from "influence_global";
import double getInfluenceEfficiency(int stock, int stored) from "influence_global";
import double getInfluenceStorage(int stock) from "influence_global";
import double getInfluencePercentage(Empire& emp) from "influence_global";
const double INTELLIGENCE_TIMEOUT = 80.0;
tidy class InfluenceReservation : Savable {
int id = -1;
double timer = -1.0;
double factor = 1.0;
InfluenceReservation() {
}
InfluenceReservation(SaveFile& file) {
load(file);
}
void save(SaveFile& file) {
file << id << timer << factor;
}
void load(SaveFile& file) {
file >> id >> timer >> factor;
}
}
tidy class InfluenceManager : Component_InfluenceManager, InfluenceStore, Savable {
Mutex inflMtx;
Mutex cardMtx;
//Current influence stored
int influence = 0;
int influenceIncome = 0;
//Stored partial generation
double partialInfluence = 0.0;
double StatRecordDelay = 5.0;
//Reservations on influence income
array<InfluenceReservation@> reservations;
int nextReservationId = 1;
double inflFactor = 1.0;
double inflFactorMod = 0.0;
//Available cards to use
array<InfluenceCard@> cards;
int nextCardId = 1;
bool cardDelta = false;
array<double>@ intelligenceTimer;
//Edicts
DiplomacyEdict edict;
InfluenceManager() {
}
void load(SaveFile& file) {
file >> influence;
file >> influenceIncome;
file >> partialInfluence;
file >> StatRecordDelay;
if(partialInfluence != partialInfluence)
partialInfluence = 0.0;
uint cnt = 0;
file >> cnt;
cards.length = cnt;
for(uint i = 0; i < cnt; ++i)
@cards[i] = InfluenceCard(file);
file >> nextCardId;
file >> inflFactor;
file >> inflFactorMod;
file >> nextReservationId;
file >> cnt;
reservations.length = cnt;
for(uint i = 0; i < cnt; ++i)
@reservations[i] = InfluenceReservation(file);
if(file >= SV_0018) {
file >> cnt;
@intelligenceTimer = array<double>(cnt, -1.0);
for(uint i = 0; i < cnt; ++i)
file >> intelligenceTimer[i];
}
if(file >= SV_0070)
file >> edict;
}
void save(SaveFile& file) {
file << influence;
file << influenceIncome;
file << partialInfluence;
file << StatRecordDelay;
uint cnt = cards.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << cards[i];
file << nextCardId;
file << inflFactor;
file << inflFactorMod;
file << nextReservationId;
cnt = reservations.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << reservations[i];
cnt = intelligenceTimer.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
file << intelligenceTimer[i];
file << 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_InfluenceCap() {
return getInfluenceStorage(max(influenceIncome,0));
}
double get_InfluenceFactor() {
return inflFactor;
}
void modInfluenceFactor(double amount) {
inflFactorMod += amount;
_calcReservation();
}
double get_InfluencePercentage(Empire& emp) {
return getInfluencePercentage(emp);
}
uint getEdictType() {
return edict.type;
}
Empire@ getEdictEmpire() {
return edict.empTarget;
}
Object@ getEdictObject() {
return edict.objTarget;
}
void clearEdict(Empire& emp) {
Lock lck(inflMtx);
edict.clear();
}
void conquerEdict(Empire& emp, Empire@ onEmpire) {
Lock lck(inflMtx);
edict.type = DET_Conquer;
@edict.empTarget = onEmpire;
}
void addInfluence(double amount) {
Lock lock(inflMtx);
partialInfluence += amount;
int take = floor(partialInfluence);
if(take != 0) {
influence += take;
partialInfluence -= double(take);
}
}
void modInfluenceIncome(int amount) {
Lock lock(inflMtx);
modGalacticInfluence(amount);
influenceIncome += amount;
}
int reserveInfluence(double factor, double timer = -1.0) {
int id = 0;
{
Lock lock(inflMtx);
InfluenceReservation res;
res.factor = (1.0 - factor);
res.timer = timer;
res.id = nextReservationId++;
reservations.insertLast(res);
inflFactor *= (1.0 - factor);
id = res.id;
}
return id;
}
void _calcReservation() {
double factor = max(1.0 + inflFactorMod, 0.0);
for(uint i = 0, cnt = reservations.length; i < cnt; ++i)
factor *= reservations[i].factor;
inflFactor = factor;
}
void removeInfluenceReservation(int id) {
Lock lock(inflMtx);
for(uint i = 0, cnt = reservations.length; i < cnt; ++i) {
if(reservations[i].id == id) {
reservations.removeAt(i);
_calcReservation();
return;
}
}
}
bool consumeInfluence(int amount) {
//Consume an amount
Lock lock(inflMtx);
if(influence >= amount) {
influence -= amount;
return true;
}
return false;
}
void modInfluence(int amount) {
Lock lock(inflMtx);
influence = max(influence + amount, 0);
}
void influenceTick(Empire& emp, double time) {
StatRecordDelay -= time;
bool recordStats = StatRecordDelay <= 0;
if(recordStats)
StatRecordDelay += 5.0;
{
Lock lock(inflMtx);
//Tick down timed reservations
bool changed = false;
for(uint i = 0, cnt = reservations.length; i < cnt; ++i) {
InfluenceReservation@ res = reservations[i];
if(res.timer < 0)
continue;
res.timer -= time;
if(res.timer <= 0) {
reservations.removeAt(i);
changed = true;
--i; --cnt;
}
}
if(changed)
_calcReservation();
//Generate passive influence
double income = getInfluenceIncome(max(influenceIncome,0), influence, inflFactor);
double generate = time * income;
if(generate != 0)
addInfluence(generate);
if(recordStats) {
emp.recordStat(stat::Influence, influence);
emp.recordStat(stat::InfluenceIncome, income);
}
//Edicts
if(edict.type == DET_Conquer) {
if(!emp.isHostile(edict.empTarget)) {
emp.clearEdict();
}
}
}
{
Lock glock(influenceLock);
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].tick(time))
cardDelta = true;
}
}
{
if(intelligenceTimer is null)
@intelligenceTimer = array<double>(getEmpireCount(), -1.0);
for(uint i = 0, cnt = intelligenceTimer.length; i < cnt; ++i) {
if(intelligenceTimer[i] < 0)
continue;
auto@ other = getEmpire(i);
intelligenceTimer[i] -= time;
if(intelligenceTimer[i] <= 0.0) {
//Find the intelligence card
auto@ type = ::getInfluenceCardType("Intelligence");
if(type is null)
break;
auto@ card = type.create(uses=1);
@card.owner = emp;
auto@ targ = card.targets.fill("onEmpire");
if(targ is null)
break;
@targ.emp = other;
for(uint j = 0, cnt = cards.length; j < cnt; ++j) {
auto@ check = cards[j];
if(check.canCollapseUses(card)) {
check.uses -= 1;
check.lose(1, false);
if(check.uses == 0) {
cards.removeAt(j);
cards.sortAsc();
intelligenceTimer[i] = -1.0;
break;
}
intelligenceTimer[i] = INTELLIGENCE_TIMEOUT;
cardDelta = true;
break;
}
}
}
}
}
}
void gainCard(Empire& emp, uint typeId, int uses = 1, int quality = 0) {
auto@ type = ::getInfluenceCardType(typeId);
if(type is null)
return;
InfluenceCard@ card = type.create(uses=uses, quality=1+quality);
addCard(emp, card);
}
int addCard(Empire& emp, InfluenceCard@ fromCard, bool wasBuy = true) {
InfluenceCard card = fromCard;
@card.owner = emp;
Lock glock(influenceLock);
Lock lock(cardMtx);
if(card.type.collapseUses && card.uses > 0) {
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].canCollapseUses(card)) {
cardDelta = true;
cards[i].uses += card.uses;
cards[i].gain(card.uses, wasBuy);
if(card.uses == 0) {
cards.removeAt(i);
cards.sortAsc();
return -1;
}
return cards[i].id;
}
}
}
card.id = nextCardId++;
cards.insertLast(card);
cards.sortAsc();
cardDelta = true;
card.gain(card.uses, wasBuy);
if(card.uses == 0) {
cards.remove(card);
cards.sortAsc();
return -1;
}
return card.id;
}
void playCard(Empire& emp, int id, Targets@ targets, bool pay = true, InfluenceVote@ vote = null) {
Lock glock(influenceLock);
Lock lock(cardMtx);
InfluenceCard@ card;
uint index = 0;
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id) {
index = i;
@card = cards[i];
break;
}
}
if(card is null)
return;
if(vote !is null) {
if(!card.canPlay(vote, targets))
return;
}
else {
if(!card.canPlay(targets))
return;
}
if(pay) {
if(!card.playConsume(targets, vote))
return;
int cost = card.getPlayCost(vote, targets);
if(cost > 0 && !emp.consumeInfluence(cost))
return;
}
if(vote !is null)
card.play(vote, targets);
else
card.play(targets);
if(card.uses == 0) {
cards.remove(card);
cards.sortAsc();
}
cardDelta = true;
}
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;
}
uint getUsesOfCardType(uint id) {
Lock lock(cardMtx);
uint uses = 0;
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].type.id == id && cards[i].uses != -1)
uses += uint(cards[i].uses);
}
return uses;
}
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;
}
int getCostOfCard(Empire& emp, int id) {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
if(cards[i].id == id)
return cards[i].getPurchaseCost(emp);
}
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]);
}
}
uint getInfluenceCardCount() {
return cards.length;
}
void getInfluenceCards() {
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i)
yield(cards[i]);
}
void takeCardUse(int id, uint amount = 1) {
Lock glock(influenceLock);
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
auto@ card = cards[i];
if(card.id == id) {
if(amount == uint(-1)) {
card.lose(card.uses, false);
card.uses = 0;
}
else if(card.uses > 0) {
card.uses = max(0, card.uses - amount);
card.lose(amount, false);
}
if(card.uses == 0) {
cards.remove(card);
cards.sortAsc();
cardDelta = true;
}
return;
}
}
}
void copyCardTo(int id, Empire@ otherEmp, int uses = 0, bool maxQuality = false, int addQuality = 0) {
InfluenceCard copy;
bool found = false;
{
Lock glock(influenceLock);
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
auto@ card = cards[i];
if(card.id == id) {
copy = card;
found = true;
break;
}
}
}
if(!found)
return;
if(maxQuality)
copy.quality = copy.type.maxQuality;
if(addQuality != 0) {
copy.quality += addQuality;
if(!copy.type.canOverquality)
copy.quality = clamp(copy.quality, copy.type.minQuality, copy.type.maxQuality);
else
copy.quality = max(copy.quality, copy.type.minQuality);
}
if(uses != 0)
copy.uses = uses;
cast<InfluenceStore>(otherEmp.InfluenceManager).addCard(otherEmp, copy);
}
void gainRandomLeverage(Empire& emp, Empire@ towards, double qualityFactor = 1.0) {
if(!emp.valid || !emp.major)
return;
int cardAmount = 1;
if(qualityFactor > 3.0) {
cardAmount = randomi(1, ceil(qualityFactor / 3.0));
qualityFactor /= double(cardAmount);
}
auto@ type = ::getInfluenceCardType("Leverage");
if(type is null)
return;
qualityFactor = pow(randomd(), (2.0 / qualityFactor));
int quality = type.minQuality + floor(double(type.maxQuality - type.minQuality + 1) * qualityFactor);
auto@ card = type.create(uses=cardAmount, quality=quality);
auto@ targ = card.targets.fill("onEmpire");
if(targ is null)
return;
@targ.emp = towards;
if(card !is null)
addCard(emp, card);
}
bool gainIntelligence(Empire& emp, Empire@ towards, uint amount = 1) {
if(!emp.valid || !emp.major)
return false;
auto@ type = ::getInfluenceCardType("Intelligence");
if(type is null)
return true;
auto@ card = type.create(uses=amount);
@card.owner = emp;
auto@ targ = card.targets.fill("onEmpire");
if(targ is null)
return true;
@targ.emp = towards;
intelligenceTimer[towards.index] = INTELLIGENCE_TIMEOUT;
Lock glock(influenceLock);
Lock lock(cardMtx);
for(uint i = 0, cnt = cards.length; i < cnt; ++i) {
auto@ other = cards[i];
if(other.canCollapseUses(card)) {
if(other.uses < 3) {
auto prevUses = other.uses;
other.uses = min(3, other.uses + amount);
other.gain(other.uses - prevUses, false);
cardDelta = true;
return true;
}
else {
return false;
}
}
}
addCard(emp, card);
return true;
}
void writeInfluenceManager(Message& msg, bool initial) {
if(initial || cardDelta) {
msg.write1();
Lock lock(cardMtx);
uint cnt = cards.length;
msg.writeSmall(cnt);
for(uint i = 0; i < cnt; ++i)
msg << cards[i];
if(!initial)
cardDelta = false;
}
else {
msg.write0();
}
msg << inflFactor;
msg.writeSignedSmall(influence);
msg.writeSignedSmall(influenceIncome);
msg << edict;
}
};