Files
2018-07-17 14:15:37 +02:00

527 lines
11 KiB
ActionScript

import research;
import saving;
import unlock_tags;
import achievements;
import unlock_tags;
tidy class ResearchGrid : Component_ResearchGrid, Savable {
ReadWriteMutex mtx;
TechnologyGrid@ grid;
double researchRate = 0;
double points = 0;
double totalGenerated = 0;
bool delta = false;
bool gridDelta = false;
Mutex unlockMtx;
array<int> tagUnlocks;
bool unlockDelta = false;
double StatRecordDelay = 5.0;
ResearchGrid() {}
void save(SaveFile& file) {
file << researchRate;
file << points;
file << grid;
file << totalGenerated;
uint cnt = tagUnlocks.length;
file << cnt;
for(uint i = 0; i < cnt; ++i) {
file.writeIdentifier(SI_UnlockTag, i);
file << tagUnlocks[i];
}
}
void load(SaveFile& file) {
if(file < SV_0085) {
loadOld(file);
return;
}
file >> researchRate;
file >> points;
@grid = TechnologyGrid();
file >> grid;
file >> totalGenerated;
tagUnlocks.length = getUnlockTagCount();
for(uint i = 0, cnt = tagUnlocks.length; i < cnt; ++i)
tagUnlocks[i] = 0;
if(file >= SV_0091) {
uint cnt = 0;
file >> cnt;
for(uint i = 0; i < cnt; ++i) {
int id = file.readIdentifier(SI_UnlockTag);
int val = 0;
file >> val;
if(id >= 0 && uint(id) < tagUnlocks.length)
tagUnlocks[id] = val;
}
}
}
double get_ResearchRate(Empire& emp) {
return researchRate * ResearchEfficiency * emp.ResearchGenerationFactor;
}
double get_ResearchPoints() {
return points;
}
double get_ResearchEfficiency() {
return 2000.0 / (2000.0 + totalGenerated);
}
void modResearchRate(double mod) {
WriteLock lock(mtx);
researchRate += mod;
}
bool gaveAchievement = false;
void researchTick(Empire& emp, double time) {
{
WriteLock lock(mtx);
double genPts = researchRate * time * ResearchEfficiency * emp.ResearchGenerationFactor;
totalGenerated += genPts;
points += genPts;
for(uint i = 0; i < grid.nodes.length; ++i) {
if(grid.nodes[i].timer >= 0)
delta = true;
grid.nodes[i].tick(emp, grid, time);
}
}
StatRecordDelay -= time;
bool recordStats = StatRecordDelay <= 0;
if(recordStats) {
emp.recordStat(stat::ResearchIncome, float(researchRate * ResearchEfficiency * emp.ResearchGenerationFactor));
emp.recordStat(stat::ResearchTotal, totalGenerated);
StatRecordDelay += 5.0;
if(!gaveAchievement && totalGenerated >= 25000.0) {
gaveAchievement = true;
giveAchievement(emp, "ACH_MAX_TECH");
}
}
}
void generatePoints(Empire& emp, double pts, bool modified = true, bool penalized = true) {
WriteLock lock(mtx);
double genPts = pts;
if(modified)
genPts *= ResearchEfficiency;
points += genPts;
if(penalized)
totalGenerated += genPts;
}
bool consumeResearchPoints(int amount) {
WriteLock lock(mtx);
if(points < amount)
return false;
points -= amount;
return true;
}
void freeResearchPoints(int amount) {
WriteLock lock(mtx);
points += amount;
}
void reduceResearchPenalty(int points) {
WriteLock lock(mtx);
totalGenerated = max(0.0, totalGenerated - points);
}
void initResearch(Empire& emp) {
WriteLock lock(mtx);
if(hasDLC("Heralds"))
@grid = getTechnologyGridSpec("Heralds").create();
else
@grid = getTechnologyGridSpec("Base").create();
tagUnlocks.length = getUnlockTagCount();
for(uint i = 0, cnt = tagUnlocks.length; i < cnt; ++i)
tagUnlocks[i] = 0;
//DLC unlock tags
for(uint i = 0, cnt = dlcs.length; i < cnt; ++i) {
if(!hasDLC(dlcs[i]))
continue;
int tag = getUnlockTag(dlcs[i]+"DLC");
tagUnlocks[tag] = 1;
}
//Pick some secret projects
for(uint n = 0; n < uint(config::PICK_SECRET_PROJECTS); ++n) {
TechnologyNode@ node;
double total = 0.0;
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ other = grid.nodes[i];
if(!other.secret || other.type.secretFrequency <= 0.0)
continue;
if(other.secretPicked)
continue;
if(!other.canBeSecret(emp))
continue;
total += other.type.secretFrequency;
if(randomd() < other.type.secretFrequency / total)
@node = other;
}
if(node is null)
break;
node.secretPicked = true;
}
}
void getTechnologyNodes() {
if(grid is null)
return;
ReadLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i)
yield(grid.nodes[i]);
}
void getResearchingNodes() {
if(grid is null)
return;
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(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;
}
}
}
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;
}
}
}
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) {
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) {
WriteLock lock(mtx);
auto@ node = getNode(id);
if(node is null)
return;
if(node.bought)
return;
if(!node.canUnlock(emp)) {
if(queue && !secondary) {
node.queued = true;
delta = true;
}
return;
}
auto cost = node.getPointCost(emp);
if(secondary) {
if(emp.ForbidSecondaryUnlock != 0 && cost != 0)
return;
if(!node.canSecondaryUnlock(emp))
return;
if(!node.consumeSecondary(emp))
return;
node.secondaryUnlock = true;
totalGenerated += node.getPointCost(emp);
}
else {
if(cost == 0)
return;
if(cost > points) {
if(queue) {
node.queued = true;
delta = true;
}
return;
}
points -= cost;
}
node.buy(emp);
grid.markBought(node.position, emp);
delta = true;
}
bool isTagUnlocked(int id) {
if(id < 0 || uint(id) >= tagUnlocks.length)
return false;
return tagUnlocks[id] > 0;
}
void setTagUnlocked(int id, bool unlocked) {
if(id < 0 || uint(id) >= tagUnlocks.length) {
error("Error: cannot set unlocked for out-of-range tag "+id+" - "+getUnlockTagIdent(id));
return;
}
Lock lck(unlockMtx);
if(unlocked)
tagUnlocks[id] += 1;
else
tagUnlocks[id] -= 1;
unlockDelta = true;
}
void removeResearchOfType(int typeId) {
WriteLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ node = grid.nodes[i];
if(int(node.type.id) == typeId) {
if(!node.unlocked) {
grid.nodes.removeAt(i);
--i;
--cnt;
}
}
}
grid.regenGrid();
gridDelta = true;
}
void replaceResearchAt(vec2i pos, int replaceWith) {
auto@ otherType = getTechnology(replaceWith);
if(otherType is null)
return;
WriteLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ node = grid.nodes[i];
if(node.position == pos) {
if(!node.unlocked) {
@node.type = otherType;
if(otherType.defaultUnlock)
grid.markUnlocked(node.position);
}
}
}
gridDelta = true;
}
void replaceResearchOfType(int typeId, int replaceWith) {
auto@ otherType = getTechnology(replaceWith);
if(otherType is null)
return;
WriteLock lock(mtx);
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ node = grid.nodes[i];
if(int(node.type.id) == typeId) {
if(!node.unlocked) {
@node.type = otherType;
if(otherType.defaultUnlock)
grid.markUnlocked(node.position);
}
}
}
gridDelta = true;
}
void replaceResearchGrid(string name) {
auto@ gridType = getTechnologyGridSpec(name);
if(gridType is null)
return;
WriteLock lock(mtx);
@grid = gridType.create();
gridDelta = true;
}
void overlayResearchGrid(string name) {
auto@ gridType = getTechnologyGridSpec(name);
if(gridType is null)
return;
WriteLock lock(mtx);
@grid = gridType.create();
gridDelta = true;
}
void revealSecretProject(Empire& emp, bool pickedOnly) {
WriteLock lock(mtx);
TechnologyNode@ pick;
double count = 0;
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
auto@ node = grid.nodes[i];
if(!node.secret)
continue;
if(pickedOnly && !node.secretPicked)
continue;
if(node.available)
continue;
if(!node.canBeSecret(emp))
continue;
count += 1.0;
if(randomd() < 1.0 / count)
@pick = node;
}
if(pick !is null) {
pick.secretPicked = true;
pick.secret = false;
}
}
//Networking
void writeResearch(Message& msg, bool initial) {
ReadLock lock(mtx);
msg << researchRate;
msg << points;
msg << totalGenerated;
if(initial) {
msg.write1();
msg.write1();
}
else {
msg.writeBit(delta);
msg.writeBit(gridDelta);
}
if(initial || unlockDelta) {
msg.write1();
uint cnt = tagUnlocks.length;
msg.writeSmall(cnt);
for(uint i = 0; i < cnt; ++i)
msg.writeBit(tagUnlocks[i] > 0);
if(!initial)
unlockDelta = false;
}
else {
msg.write0();
}
if(initial || delta || gridDelta) {
if(initial || gridDelta) {
msg << grid.minPos;
msg << grid.maxPos;
msg << grid.nodes.length;
}
for(uint i = 0, cnt = grid.nodes.length; i < cnt; ++i) {
if(initial || gridDelta)
grid.nodes[i].write(msg);
else
grid.nodes[i].writeStatus(msg);
}
if(!initial) {
delta = false;
gridDelta = false;
}
}
}
//Skip over data from old savegames
void loadOld(SaveFile& file) {
int tmp = 0;
int64 tmp64 = 0;
double tmpD = 0;
bool tmpB = false;
Object@ tmpO;
uint cnt = 0;
file >> cnt;
for(uint i = 0; i < cnt; ++i) {
//load field
file >> tmp;
file >> tmp;
if(file < SV_0072)
file >> tmp;
file >> tmpD;
file >> tmpD;
}
if(file.readBit())
file >> tmp;
file >> researchRate;
file >> tmpD;
file >> tmp;
file >> tmp;
file >> cnt;
for(uint i = 0; i < cnt; ++i) {
//load project
file >> tmp;
uint type = 0;
file >> type;
for(uint n = 0; n < 7; ++n)
file >> tmp;
file >> tmpB;
//load hooks
if(tmpB) {
if(type == 24 || type == 25) {
file >> tmpD;
}
else if(type == 27) {
uint sub = 0;
file >> sub;
for(uint j = 0; j < sub; ++j) {
file >> tmpO;
file >> tmpD;
file >> tmpB;
if(tmpB && file >= SV_0013)
file >> tmp64;
}
}
}
}
file >> tmp;
for(uint i = 0; i < 7; ++i)
file >> tmp;
}
};