Files
starruler-linux/scripts/gui/tabs/StatsTab.as
T
2018-07-17 14:15:37 +02:00

284 lines
6.7 KiB
ActionScript

import tabs.Tab;
from tabs.tabbar import newTab, switchToTab;
Tab@ createStatsTab() {
return StatsTab();
}
class StatsTabCommand : ConsoleCommand {
void execute(const string& args) {
Tab@ editor = createStatsTab();
newTab(editor);
switchToTab(editor);
}
}
void init() {
addConsoleCommand("stats_tab", StatsTabCommand());
}
array<int> valuePoints;
int findNearest(int start, int bound) {
int above = start;
bool validAbove = false;
while(!validAbove && above > 0) {
validAbove = true;
for(uint i = 0, cnt = valuePoints.length; i < cnt; ++i) {
int pt = valuePoints[i];
if(above > pt - 24 && above < pt + 24) {
validAbove = false;
above = pt - 24;
}
}
}
int below = start;
bool validBelow = false;
while(!validBelow && below < bound) {
validBelow = true;
for(uint i = 0, cnt = valuePoints.length; i < cnt; ++i) {
int pt = valuePoints[i];
if(below > pt - 24 && below < pt + 24) {
validBelow = false;
below = pt + 24;
}
}
}
if(validAbove) {
if(!validBelow) {
start = above;
}
else {
int aDiff = abs(above - start);
int bDiff = abs(below - start);
if(aDiff < bDiff)
start = above;
else
start = below;
}
}
else if(validBelow) {
start = below;
}
valuePoints.insertLast(start);
return start;
}
interface StatTracker {
void update();
void draw(const Skin@ skin, recti bound);
};
class StatsTab : Tab {
uint endTime = 0;
array<StatTracker@> stats;
double nextUpdate = 0;
StatsTab() {
super();
title = locale::STATS_TAB;
stats.insertLast(IntegerStat(stat::Planets, Color(0xff9955ff), 15, " Planets"));
stats.insertLast(IntegerStat(stat::Ships, Color(0x4422ffff), 15, " Ships"));
stats.insertLast(IntegerStat(stat::ShipsDestroyed, Color(0xff2222ff), 15, " Killed"));
stats.insertLast(FloatStat(stat::Budget, Color(0x22ff22ff), 60, " $"));
stats.insertLast(FloatStat(stat::Energy, Color(0xffff22ff), 60, " Energy"));
stats.insertLast(FloatStat(stat::FTL, Color(0xff22ffff), 60, " FTL"));
stats.insertLast(IntegerStat(stat::Influence, Color(0xffffffff), 60, " Influence"));
}
void updateStats() {
for(uint i = 0, end = stats.length; i < end; ++i)
stats[i].update();
}
void draw() {
if(gameTime > nextUpdate) {
nextUpdate = gameTime + 1.0;
updateStats();
}
endTime = uint(gameTime);
valuePoints.length = 0;
skin.draw(SS_DiplomacyBG, SF_Normal, AbsolutePosition);
recti statBound = recti_centered(AbsolutePosition, AbsolutePosition.size - vec2i(64,16)) - vec2i(60,0);
skin.draw(SS_Panel, SF_Normal, statBound);
statBound = recti_centered(statBound, statBound.size - vec2i(1,1));
for(uint i = 0, end = stats.length; i < end; ++i)
stats[i].draw(skin, statBound);
Tab::draw();
}
}
final class IntegerStat : StatTracker {
int minVal = 0, maxVal = 100;
stat::EmpireStat stat;
Color color;
int filterDuration = 1;
array<vec2i> data;
string suffix;
IntegerStat(stat::EmpireStat Stat, const Color& col, int FilterDuration, string Suffix) {
stat = Stat;
color = col;
filterDuration = FilterDuration;
suffix = Suffix;
}
void update() {
minVal = 0;
maxVal = 100;
data.length = 0;
StatHistory history(playerEmpire, stat);
while(history.advance(1)) {
int val = history.intVal;
if(val > maxVal)
maxVal = val;
if(val < minVal)
minVal = val;
data.insertLast(vec2i(int(history.time), val));
}
}
void draw(const Skin@ skin, recti bound) {
if(data.length == 0) {
drawLine(bound.topLeft + vec2i(0, bound.height), bound.botRight, color);
return;
}
vec2i size = bound.size;
float endTime = float(gameTime);
vec2f factor(1.f / endTime, -1.f / float(maxVal));
int secondStep = int(max(float(size.x) / endTime, 1.f));
int timeStep = int(max(float(size.x * filterDuration) / endTime, float(filterDuration)));
vec2i corner = bound.topLeft + vec2i(0,size.y);
vec2i prev = corner;
//Draw segments
for(uint i = 0, cnt = data.length; i < cnt; ++i) {
vec2f ptf = vec2f(data[i]);
vec2i pt = corner + vec2i(float(size.x) * ptf.x * factor.x, float(size.y) * ptf.y * factor.y);
if(i != 0 && prev.x >= pt.x - timeStep) {
drawLine(prev, pt, color);
}
else {
vec2i med = vec2i(pt.x - secondStep, prev.y);
drawLine(prev, med, color);
drawLine(med, pt, color);
}
prev = pt;
}
//Draw final segment until current moment if nothing has changed
if(data[data.length - 1].x < int(endTime)) {
vec2i end = vec2i(corner.x + size.x, prev.y);
drawLine(prev, end, color);
prev = end;
}
vec2i around = prev + vec2i(6, 0);
around.y = findNearest(around.y - bound.topLeft.y, size.height) + bound.topLeft.y - 8;
skin.draw(FT_Normal, around, string(data[data.length - 1].y) + suffix, color);
}
};
final class FloatStat : StatTracker {
float minVal = 0, maxVal = 100.f;
stat::EmpireStat stat;
Color color;
int filterDuration = 1;
array<vec2f> data;
string suffix;
FloatStat(stat::EmpireStat Stat, const Color& col, int FilterDuration, string Suffix) {
stat = Stat;
color = col;
filterDuration = FilterDuration;
suffix = Suffix;
}
void update() {
minVal = 0;
maxVal = 100;
data.length = 0;
StatHistory history(playerEmpire, stat);
while(history.advance(1)) {
float val = history.floatVal;
if(val > maxVal)
maxVal = val;
if(val < minVal)
minVal = val;
data.insertLast(vec2f(history.time, val));
}
}
void draw(const Skin@ skin, recti bound) {
if(data.length == 0) {
drawLine(bound.topLeft + vec2i(0, bound.height), bound.botRight, color);
return;
}
vec2i size = bound.size;
float endTime = float(gameTime);
vec2f factor(1.f / endTime, -1.f / float(maxVal));
int secondStep = int(max(float(size.x) / endTime, 1.f));
int timeStep = int(max(float(size.x * filterDuration) / endTime, float(filterDuration)));
vec2i corner = bound.topLeft + vec2i(0,size.y);
vec2i prev = corner;
//Draw segments
for(uint i = 0, cnt = data.length; i < cnt; ++i) {
vec2f ptf = data[i];
vec2i pt = corner + vec2i(float(size.x) * ptf.x * factor.x, float(size.y) * ptf.y * factor.y);
if(i != 0 && prev.x >= pt.x - timeStep) {
drawLine(prev, pt, color);
}
else {
vec2i med = vec2i(pt.x - secondStep, prev.y);
drawLine(prev, med, color);
drawLine(med, pt, color);
}
prev = pt;
}
//Draw final segment until current moment if nothing has changed
if(data[data.length - 1].x < int(endTime)) {
vec2i end = vec2i(corner.x + size.x, prev.y);
drawLine(prev, end, color);
prev = end;
}
vec2i around = prev + vec2i(6, 0);
around.y = findNearest(around.y - bound.topLeft.y, size.height) + bound.topLeft.y - 8;
skin.draw(FT_Normal, around, standardize(data[data.length - 1].y) + suffix, color);
}
};