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 reservations; int nextReservationId = 1; double inflFactor = 1.0; double inflFactorMod = 0.0; //Available cards to use array cards; int nextCardId = 1; bool cardDelta = false; array@ 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(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(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(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; } };