Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+306
View File
@@ -0,0 +1,306 @@
//Adds functionality to the normal engine camera to make it
//more intelligent and context-sensitive
import double getElevation(double x, double z) from "navigation.elevation";
import double getAverageElevation() from "navigation.elevation";
import bool getElevationIntersect(const line3dd& line, vec3d& point) from "navigation.elevation";
import Object@ get_hoveredObject() from "obj_selection";
import saving;
bool CAM_PANNED = false;
bool CAM_ZOOMED = false;
bool CAM_ROTATED = false;
double OBJLOCK_ZOOMDIST = 5.0;
class SmartCamera : Savable {
//Main engine camera
Camera camera;
//Configuration
double camAnglesPerPixel;
double movePerPixel;
double tickZoom;
//Object locks
Object@ lockedObject;
vec3d lockOffset;
SmartCamera() {
camAnglesPerPixel = 0.22 * twopi / 360.0;
movePerPixel = 8.0;
tickZoom = 1.2;
camera.setPositionBound(vec3d(-200000), vec3d(200000));
}
void reset() {
//Set the camera to the normal angle
camera.resetRotation();
camera.resetZoom();
camera.zoom(5.0);
camera.pitch(0.7);
camera.snap();
}
vec3d get_lookAt() {
return camera.lookAt;
}
vec3d get_finalLookAt() {
return camera.finalLookAt;
}
double screenAngle(const vec3d& pos) {
return camera.screenAngle(pos);
}
double get_distance() {
return camera.distance;
}
//Rotate by a certain amount of screen pixels dragged
void rotate(int dx, int dy) {
CAM_ROTATED = true;
if(settings::bInvertHorizRot)
dx = -dx;
if(settings::bInvertVertRot)
dy = -dy;
if(settings::bFreeCamera) {
camera.yaw(clamp(double(dx) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
camera.pitch(clamp(double(dy) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
}
else {
camera.abs_yaw(clamp(double(dx) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
camera.pitch(clamp(double(dy) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
}
}
//Roll based on dragged script pixels
void roll(int delta) {
CAM_ROTATED = true;
camera.roll(double(delta) * camAnglesPerPixel);
}
//Zoom with world plane
void zoom(int delta) {
//Get zoom speed
double factor;
if(settings::bInvertZoom)
factor = pow(tickZoom, delta * settings::dZoomSpeed);
else
factor = pow(tickZoom, -delta * settings::dZoomSpeed);
CAM_ZOOMED = true;
if(lockedObject !is null) {
camera.zoom(factor);
return;
}
if(factor < 1.0 ? settings::bZoomToCursor : settings::bZoomFromCursor) {
if(hoveredObject !is null) {
double minDist = hoveredObject.radius * getMinZoomDist(hoveredObject);
vec3d point = hoveredObject.position;
camera.zoomTo(factor, point, minDist);
}
else {
//Interpolate plane position
line3dd ray = screenToRay(mousePos);
if(factor > 1.0)
ray.start -= ray.direction * 1.0;
vec3d point;
if(!getElevationIntersect(ray, point))
return;
double minDist = 0.0;
camera.zoomTo(factor, point, minDist);
}
}
else {
camera.zoom(factor);
}
}
void zoomTo(Object@ obj) {
clearObjectLock();
zoomTo(obj.position);
}
void zoomTo(const vec3d& pos) {
clearObjectLock();
vec3d finalPos = camera.finalLookAt;
vec3d movement;
movement.x = pos.x - finalPos.x;
movement.z = pos.z - finalPos.z;
movement.y = getElevation(pos.x, pos.z) - finalPos.y;
camera.move_abs(movement);
}
double getMinZoomDist(Object@ obj) {
switch(obj.type) {
case OT_Star:
return 1.4;
case OT_Planet:
return 1.6;
}
return 1.15;
}
//Pan on top of the world plane
void pan(int dx, int dy) {
if(!settings::bInvertPanX)
dx = -dx;
if(settings::bInvertPanY)
dy = -dy;
//Normal world plane movement
vec2d movement;
movement.x = movePerPixel * double(dx);
movement.y = movePerPixel * double(dy);
pan(movement);
}
void pan(vec2d movement) {
clearObjectLock();
//Speed based on zoom and modifiers
double speed = camera.distance / 2500.0;
if(settings::bModSpeedPan) {
if(shiftKey)
speed *= 3.0;
else if(altKey)
speed *= 0.333;
}
movement *= settings::dPanSpeed * speed;
//Normal world plane movement
vec3d prevPos = camera.finalLookAt;
camera.move_world_abs(vec3d(movement.x, 0, movement.y));
//Change in elevation
vec3d nextPos = camera.finalLookAt;
camera.move_abs(vec3d(0, getElevation(nextPos.x, nextPos.z) - prevPos.y, 0));
CAM_PANNED = true;
}
bool panTo(const vec3d& pos, double speed, double threshold = 10.0) {
clearObjectLock();
//Modify speed
speed *= max(camera.distance / 2500.0, 0.2);
if(settings::bModSpeedPan) {
if(shiftKey)
speed *= 3.0;
else if(altKey)
speed *= 0.333;
}
speed *= settings::dPanSpeed;
//Pan on a flat plane
vec3d cur = lookAt;
vec3d final = finalLookAt;
vec3d flatPos = pos;
flatPos.y = 0;
cur.y = 0;
final.y = 0;
vec3d panDir = flatPos - final;
double len = panDir.length;
if(len <= threshold) {
return true;
}
else {
if(len > speed)
panDir.length = speed;
vec3d prevPos = camera.finalLookAt;
camera.move_abs(panDir);
vec3d nextPos = camera.finalLookAt;
camera.move_abs(vec3d(0, getElevation(nextPos.x, nextPos.z) - prevPos.y, 0));
}
return false;
}
line3dd screenToRay(const vec2i& pos) {
vec2i size = screenSize;
return camera.screenToRay(double(pos.x) / double(size.x),
double(pos.y) / double(size.y));
}
vec3d screenToPoint(const vec2i& pos) {
line3dd ray = screenToRay(mousePos);
vec3d point;
if(!getElevationIntersect(ray, point))
ray.intersectY(point, getAverageElevation(), false);
return point;
}
void clearObjectLock() {
@lockedObject = null;
}
void zoomLockObject(Object& obj) {
zoomTo(obj.position);
camera.move_abs(obj.node_position - camera.finalLookAt);
if(!shiftKey)
camera.zoom((obj.radius * OBJLOCK_ZOOMDIST) / camera.distance);
setLocked(obj);
}
void setLocked(Object& obj) {
@lockedObject = obj;
lockOffset = camera.finalLookAt - obj.node_position;
}
void animateLocked(double time) {
if(lockedObject is null)
return;
if(!lockedObject.visible) {
@lockedObject = null;
return;
}
vec3d finalPos = camera.finalLookAt;
double tDiff = frameGameTime - lockedObject.lastTick;
vec3d objPos = lockedObject.position + (lockedObject.velocity + lockedObject.acceleration * (tDiff * 0.5)) * tDiff;
if(finalPos.distanceToSQ(objPos) > sqr(lockedObject.radius * 2.0 + lockedObject.velocity.length)) {
@lockedObject = null;
return;
}
objPos += lockOffset;
vec3d movement = objPos - finalPos;
bool snap = camera.lookAt.distanceTo(finalPos) < 15.0;
camera.move_abs(movement);
if(snap)
camera.snapTranslation();
}
void animate(double time) {
camera.animate(time);
if(lockedObject !is null)
animateLocked(time);
}
void save(SaveFile& file) {
file << camera.finalLookAt;
file << camera.radius;
}
void load(SaveFile& file) {
vec3d position;
file >> position;
if(file >= SV_0149) {
double radius = 0;
file >> radius;
camera.radius = radius;
}
camera.move_abs(position - camera.finalLookAt);
camera.snap();
}
}
+101
View File
@@ -0,0 +1,101 @@
#priority init 50
import systems;
ElevationMap emap;
double epower = 4.0;
double egrid = 1000.0;
double vgrid = 1000.0;
bool drawGrid = false;
void init() {
recalculateElevation();
addConsoleCommand("grid_smoothing", GridPowerCommand());
addConsoleCommand("grid_spacing", GridSpacingCommand());
addConsoleCommand("grid_visual", GridVisualCommand());
addConsoleCommand("show_grid", ShowGridCommand());
}
void recalculateElevation() {
//Elevation map
emap.clear();
vec2d minPoint, maxPoint;
for(uint i = 0, cnt = systemCount; i < cnt; ++i) {
auto@ sys = getSystem(i);
vec3d pos = sys.position;
emap.addPoint(pos, sys.radius);
minPoint.x = min(minPoint.x, pos.x);
minPoint.y = min(minPoint.y, pos.y);
maxPoint.x = max(maxPoint.x, pos.x);
maxPoint.y = max(maxPoint.y, pos.y);
}
emap.generate(vec2d(max(egrid, (maxPoint.x-minPoint.x)/100.0), max(egrid, (maxPoint.y-minPoint.y)/100.0)), epower);
}
double getElevation(double x, double z) {
return emap.get(x, z);
}
double getAverageElevation() {
return emap.gridStart.y;
}
bool getElevationIntersect(const line3dd& line, vec3d&out point) {
return emap.getClosestPoint(line, point);
}
class GridPowerCommand : ConsoleCommand {
void execute(const string& args) {
epower = toDouble(args);
emap.generate(vec2d(egrid, egrid), epower);
}
};
class GridSpacingCommand : ConsoleCommand {
void execute(const string& args) {
egrid = toDouble(args);
emap.generate(vec2d(egrid, egrid), epower);
}
};
class GridVisualCommand : ConsoleCommand {
void execute(const string& args) {
vgrid = toDouble(args);
}
};
class ShowGridCommand : ConsoleCommand {
void execute(const string& args) {
drawGrid = args.length == 0 || toBool(args);
}
};
void render(double time) {
if(!drawGrid)
return;
//Draw the elevation grid
for(int x = -10, xcnt = emap.gridSize.x / vgrid + 10; x < xcnt; ++x) {
for(int y = -10, ycnt = emap.gridSize.y / vgrid + 10; y < ycnt; ++y) {
vec3d tlpos = emap.gridStart + vec3d(vgrid * x, 0, vgrid * y);
vec3d brpos = tlpos + vec3d(vgrid, 0.0, vgrid);
vec3d tl = vec3d(tlpos.x, emap.get(tlpos.x, tlpos.z), tlpos.z);
vec3d tr = vec3d(brpos.x, emap.get(brpos.x, tlpos.z), tlpos.z);
vec3d bl = vec3d(tlpos.x, emap.get(tlpos.x, brpos.z), brpos.z);
vec3d br = vec3d(brpos.x, emap.get(brpos.x, brpos.z), brpos.z);
drawPolygonStart(2, material::TestGrid);
drawPolygonPoint(tl, vec2f(0.f, 0.f));
drawPolygonPoint(tr, vec2f(1.f, 0.f));
drawPolygonPoint(br, vec2f(1.f, 1.f));
drawPolygonPoint(tl, vec2f(0.f, 0.f));
drawPolygonPoint(bl, vec2f(0.f, 1.f));
drawPolygonPoint(br, vec2f(1.f, 1.f));
drawPolygonEnd();
}
}
}
+108
View File
@@ -0,0 +1,108 @@
from obj_selection import selectedObjects, selectedObject;
import ftl;
import void targetHyperdrive() from "targeting.Hyperdrive";
import void targetJumpdrive() from "targeting.Jumpdrive";
import void targetFling() from "targeting.Fling";
import void targetSlipstream() from "targeting.Slipstream";
bool canMove() {
Object@[]@ selected = selectedObjects;
if(selected.length == 0)
return false;
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
if(!selected[i].hasMover)
return false;
if(!selected[i].owner.controlled)
return false;
}
return true;
}
bool canHyperdrive() {
Object@[]@ selected = selectedObjects;
if(selected.length == 0)
return false;
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
if(!canHyperdrive(selected[i]))
return false;
}
return true;
}
bool canJumpdrive() {
Object@[]@ selected = selectedObjects;
if(selected.length == 0)
return false;
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
if(!canJumpdrive(selected[i]))
return false;
}
return true;
}
bool canFling() {
Object@[]@ selected = selectedObjects;
if(selected.length == 0 || !playerEmpire.hasFlingBeacons)
return false;
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
Object@ obj = selected[i];
if(obj.owner is null || !obj.owner.valid)
return false;
if(!canFling(obj))
return false;
}
return true;
}
bool canSlipstream() {
Object@[]@ selected = selectedObjects;
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
Object@ obj = selected[i];
if(canSlipstream(obj))
return true;
}
return false;
}
bool targetFTL() {
if(!canMove())
return false;
//Check for fling
if(canFling()) {
targetFling();
return true;
}
//Check for hyperdrive
if(canHyperdrive()) {
targetHyperdrive();
return true;
}
//Check for jumpdrive
if(canJumpdrive()) {
targetJumpdrive();
return true;
}
//Check for slipstream
if(canSlipstream()) {
targetSlipstream();
return true;
}
return false;
}
void FTLBind(bool pressed) {
if(!pressed) {
if(!targetFTL())
sound::error.play(priority=true);
}
}
void init() {
keybinds::Global.addBind(KB_FTL, "FTLBind");
}
+857
View File
@@ -0,0 +1,857 @@
#priority init -1
import tabs.Tab;
import elements.GuiTextbox;
import systems;
/*import research;*/
import resources;
from input import ActiveCamera;
import uint get_tabCount() from "tabs.tabbar";
import Tab@ get_Tabs(uint i) from "tabs.tabbar";
import Tab@ get_ActiveTab() from "tabs.tabbar";
import void switchToTab(Tab@) from "tabs.tabbar";
import Tab@ newTab(Tab@) from "tabs.tabbar";
import void browseTab(Tab@,Tab@,bool) from "tabs.tabbar";
import void browseTab(Tab@,bool) from "tabs.tabbar";
import Tab@ findTab(int cat) from "tabs.tabbar";
import Tab@ createResearchTab() from "tabs.ResearchTab";
import Tab@ createDesignOverviewTab() from "tabs.DesignOverviewTab";
import Tab@ createDesignEditorTab(const Design@) from "tabs.DesignEditorTab";
import Tab@ createGalaxyTab() from "tabs.GalaxyTab";
import Tab@ createGalaxyTab(Object@) from "tabs.GalaxyTab";
import Tab@ createGalaxyTab(vec3d) from "tabs.GalaxyTab";
/*import Tab@ createResearchTab(ResearchNode@ node) from "tabs.ResearchTab";*/
/*import void researchTabView(Tab@ tab, ResearchNode@ node) from "tabs.ResearchTab";*/
import void zoomTabTo(vec3d pos) from "tabs.GalaxyTab";
import void zoomTo(Object@ obj) from "tabs.GalaxyTab";
const uint ITEM_COUNT = 5;
class GoItem {
Sprite get_icon() {
return Sprite();
}
SkinStyle get_skinIcon() {
return SS_NULL;
}
int match(const string&) {
return 0;
}
string get_text() {
return "(null)";
}
string get_type() {
return "";
}
void go() {
}
bool go(TabCategory cat) {
Tab@ found = findTab(cat);
if(shiftKey || found is null) {
return false;
}
else {
switchToTab(found);
return true;
}
}
void go(Tab@ tab, TabCategory cat) {
if(shiftKey) {
newTab(tab);
switchToTab(tab);
}
else {
Tab@ found = findTab(cat);
if(ctrlKey || found is null) {
browseTab(tab, false); //TODO: History
}
else {
browseTab(found, tab, false); //TODO: History
switchToTab(tab);
}
}
}
void go(Tab@ tab) {
if(shiftKey) {
newTab(tab);
switchToTab(tab);
}
else {
browseTab(tab, false); //TODO: History
}
}
};
string str_galaxy = locale::GALAXY_CAMERA;
class GoGalaxy : GoItem {
int match(const string& str) {
if(str_galaxy.startswith_nocase(str))
return 5;
return 0;
}
SkinStyle get_skinIcon() {
return SS_GalaxyIcon;
}
string get_text() {
return str_galaxy;
}
void go() {
go(createGalaxyTab());
}
};
string str_research = locale::RESEARCH_OVERVIEW;
class GoResearch : GoItem {
int match(const string& str) {
if(str_research.startswith_nocase(str))
return 3;
return 0;
}
SkinStyle get_skinIcon() {
return SS_ResearchIcon;
}
string get_text() {
return str_research;
}
void go() {
go(createResearchTab());
}
};
string str_designs = locale::DESIGN_OVERVIEW;
class GoDesigns : GoItem {
int match(const string& str) {
if(str_designs.startswith_nocase(str))
return 5;
return 0;
}
SkinStyle get_skinIcon() {
return SS_DesignsIcon;
}
string get_text() {
return str_designs;
}
void go() {
go(createDesignOverviewTab());
}
};
class GoConsole : GoItem {
int match(const string& str) {
if(locale::CONSOLE.startswith_nocase(str))
return 5;
return 0;
}
string get_text() {
return locale::CONSOLE;
}
void go() {
toggleConsole();
}
};
class GoSystem : GoItem {
SystemDesc@ sys;
GoSystem(SystemDesc@ _sys) {
@sys = _sys;
}
int match(const string& str) {
if(playerEmpire.valid && sys.object.ExploredMask & playerEmpire.mask == 0)
return 0;
if(sys.object.name.contains_nocase(str))
return 5;
return 0;
}
SkinStyle get_skinIcon() {
return SS_GalaxyIcon;
}
string get_text() {
return sys.object.name;
}
string get_type() {
return locale::SYSTEM;
}
void go() {
if(go(TC_Galaxy))
zoomTabTo(sys.position);
else
go(createGalaxyTab(sys.position));
}
}
string str_tab = locale::TAB;
class GoTab : GoItem {
Tab@ tab;
GoTab(Tab@ _tab) {
@tab = _tab;
}
Sprite get_icon() {
return tab.icon;
}
string get_text() {
return tab.title;
}
string get_type() {
return str_tab;
}
void go() {
switchToTab(tab);
}
}
string str_design = locale::DESIGN;
class GoDesign : GoItem {
const Design@ dsg;
GoDesign(const Design@ _dsg) {
@dsg = _dsg;
}
SkinStyle get_skinIcon() {
return SS_DesignsIcon;
}
string get_text() {
return dsg.name;
}
string get_type() {
return str_design;
}
void go() {
go(createDesignEditorTab(dsg), TC_Designs);
}
}
string str_planet = locale::PLANET;
class GoPlanet : GoItem {
Object@ obj;
GoPlanet(Object@ _obj) {
@obj = _obj;
}
SkinStyle get_skinIcon() {
return SS_GalaxyIcon;
}
string get_text() {
return obj.name;
}
string get_type() {
return str_planet;
}
void go() {
go(createGalaxyTab(obj), TC_Galaxy);
}
}
string str_ship = locale::SHIP;
class GoShip : GoItem {
Object@ obj;
GoShip(Object@ _obj) {
@obj = _obj;
}
SkinStyle get_skinIcon() {
return SS_GalaxyIcon;
}
string get_text() {
return obj.name;
}
string get_type() {
return str_ship;
}
void go() {
go(createGalaxyTab(obj), TC_Galaxy);
}
}
/*string str_technology = locale::TECHNOLOGY;*/
/*class GoTechnology : GoItem {*/
/* ResearchNode@ node;*/
/* GoTechnology(ResearchNode@ _node) {*/
/* @node = _node;*/
/* }*/
/* SkinStyle get_skinIcon() {*/
/* return SS_ResearchIcon;*/
/* }*/
/* string get_text() {*/
/* return node.tech.name;*/
/* }*/
/* string get_type() {*/
/* return str_technology;*/
/* }*/
/* void go() {*/
/* go(createResearchTab(node), TC_Research);*/
/* }*/
/*}*/
class GoResource : GoItem {
const ResourceType@ resType;
GoResource(const ResourceType@ type) {
@this.resType = type;
}
int match(const string& str) {
if(resType.name.contains_nocase(str) && str.length >= 3) {
if(str.equals_nocase(resType.name))
return 10;
return 4;
}
return 0;
}
Sprite get_icon() {
return resType.smallIcon;
}
string get_text() {
return format(locale::GO_RESOURCE, resType.name);
}
string get_type() {
return format(locale::GO_RESOURCE_TYPE, resType.name);
}
void go() {
uint sysCnt = systemCount;
Planet@ best;
double closestDist = INFINITY;
bool foundOwned = false;
vec3d camPos = ActiveCamera.lookAt;
for(uint i = 0; i < sysCnt; ++i) {
auto@ sys = getSystem(i);
uint plCnt = sys.object.planetCount;
for(uint n = 0; n < plCnt; ++n) {
Planet@ pl = sys.object.planets[n];
if(pl.known) {
auto@ type = getResource(pl.nativeResourceType[0]);
if(type !is null && type is resType) {
Empire@ owner = pl.visibleOwner;
if(owner is playerEmpire) {
Object@ dest = pl.nativeResourceDestination[0];
if(dest !is null)
continue;
if(type.isMaterial(pl.level))
continue;
double dist = camPos.distanceToSQ(pl.position);
if(dist < closestDist || !foundOwned) {
@best = pl;
closestDist = dist;
foundOwned = true;
}
}
else if(owner is null || !owner.valid) {
if(!foundOwned) {
double dist = camPos.distanceToSQ(pl.position);
if(dist < closestDist) {
@best = pl;
closestDist = dist;
}
}
}
}
}
}
}
if(best !is null)
zoomTo(best);
}
};
class GoResourceClass : GoItem {
const ResourceClass@ resType;
GoResourceClass(const ResourceClass@ type) {
@this.resType = type;
}
int match(const string& str) {
if(resType.name.contains_nocase(str) && str.length >= 3) {
if(str.equals_nocase(resType.name))
return 9;
return 4;
}
return 0;
}
string get_text() {
return format(locale::GO_RESOURCE, resType.name);
}
string get_type() {
return format(locale::GO_RESOURCE_TYPE, resType.name);
}
void go() {
uint sysCnt = systemCount;
Planet@ best;
double closestDist = INFINITY;
bool foundOwned = false;
vec3d camPos = ActiveCamera.lookAt;
for(uint i = 0; i < sysCnt; ++i) {
auto@ sys = getSystem(i);
uint plCnt = sys.object.planetCount;
for(uint n = 0; n < plCnt; ++n) {
Planet@ pl = sys.object.planets[n];
if(pl.known) {
auto@ type = getResource(pl.nativeResourceType[0]);
if(type !is null && type.cls is resType) {
Empire@ owner = pl.visibleOwner;
if(owner is playerEmpire) {
Object@ dest = pl.nativeResourceDestination[0];
if(dest !is null)
continue;
if(type.isMaterial(pl.level))
continue;
double dist = camPos.distanceToSQ(pl.position);
if(dist < closestDist || !foundOwned) {
@best = pl;
closestDist = dist;
foundOwned = true;
}
}
else if(owner is null || !owner.valid) {
if(!foundOwned) {
double dist = camPos.distanceToSQ(pl.position);
if(dist < closestDist) {
@best = pl;
closestDist = dist;
}
}
}
}
}
}
}
if(best !is null)
zoomTo(best);
}
};
GoItem@[] staticItems;
bool WouldInsert(uint amount, int match, int[]& priorities) {
if(match < 0)
return false;
if(priorities[amount - 1] == -1)
return true;
if(priorities[amount - 1] >= match)
return false;
return true;
}
void GoInsert(GoItem@ item, uint amount, int match, GoItem@[]& list, int[]& priorities) {
if(list[amount - 1] !is null && priorities[amount - 1] >= match)
return;
for(int j = amount - 1; j >= 0; --j) {
//Push it up
if(j < int(amount) - 1) {
priorities[j + 1] = priorities[j];
@list[j + 1] = list[j];
}
//Check if we should insert here
if(j == 0 || match < priorities[j - 1]) {
//Insert here
priorities[j] = match;
@list[j] = item;
return;
}
}
}
void GoSearch(const string& text, GoItem@[]& list, uint amount = 5) {
//Cache of priorities
int[] priorities;
priorities.length = amount;
//Clear the list
list.length = amount;
for(uint i = 0; i < amount; ++i) {
@list[i] = null;
priorities[i] = -1;
}
//Empty text matches nothing
if(text.length == 0)
return;
//Search static items
uint cnt = staticItems.length;
for(uint i = 0; i < cnt; ++i) {
int match = staticItems[i].match(text);
if(match > 0)
GoInsert(staticItems[i], amount, match, list, priorities);
}
//Search tabs
cnt = tabCount;
for(uint i = 0; i < cnt; ++i) {
Tab@ tab = Tabs[i];
if(tab.title.contains_nocase(text)) {
if(WouldInsert(amount, 50, priorities))
GoInsert(GoTab(tab), amount, 50, list, priorities);
}
}
//Search designs
{
ReadLock lock(playerEmpire.designMutex);
cnt = playerEmpire.designCount;
for(uint i = 0; i < cnt; ++i) {
const Design@ dsg = playerEmpire.designs[i];
if(dsg.name.contains_nocase(text)) {
int p = text.length > 3 ? 10 : 2;
if(dsg.name.equals_nocase(text))
p += 100;
if(WouldInsert(amount, p, priorities))
GoInsert(GoDesign(dsg), amount, p, list, priorities);
}
}
}
//Search empire objects
//cnt = playerEmpire.objectCount;
//for(uint i = 0; i < cnt; ++i) {
//Object@ obj = playerEmpire.objects[i];
//Ship@ ship = cast<Ship@>(obj);
//Planet@ pl = cast<Planet@>(obj);
//int prior = -1;
//if(ship !is null || pl !is null) {
//if(obj.name.contains_nocase(text)) {
//if(pl !is null)
//prior = text.length > 3 ? 16 : 4;
//else
//prior = text.length > 3 ? 6 : 4;
//}
//if(obj.name.equals_nocase(text))
//prior += 100;
//}
//if(WouldInsert(amount, prior, priorities)) {
//GoItem@ it;
//if(pl !is null)
//@it = GoPlanet(obj);
//else if(ship !is null)
//@it = GoShip(obj);
//if(it !is null)
//GoInsert(it, amount, prior, list, priorities);
//}
//}
//Search research
/*cnt = getResearchNodeCount();*/
/*for(uint i = 0; i < cnt; ++i) {*/
/* ResearchNode@ node = getResearchNode(i);*/
/* if(node.tech.name.contains_nocase(text)) {*/
/* int p = text.length > 3 ? 7 : 1;*/
/* if(node.tech.name.equals_nocase(text))*/
/* p += 100;*/
/* if(!node.unlocked)*/
/* p += 1;*/
/* if(WouldInsert(amount, p, priorities))*/
/* GoInsert(GoTechnology(node), amount, p, list, priorities);*/
/* }*/
/*}*/
}
class GoDialog : BaseGuiElement {
GuiTextbox@ box;
GoItem@[] items;
uint selected;
int hovered;
GoDialog() {
super(null, recti());
alignment.left.set(AS_Left, 0.1f, 0);
alignment.right.set(AS_Right, 0.1f, 0);
alignment.top.set(AS_Top, 0.5f, -32 - int(ITEM_COUNT * 15));
alignment.bottom.set(AS_Top, 0.5f, int(ITEM_COUNT * 15) + 40);
@box = GuiTextbox(this, recti(0, 0, 100, 48), "");
box.font = FT_Big;
@box.alignment = Alignment(Left+8, Top+8, Right-8, Top+56);
selected = 0;
hovered = -1;
items.length = ITEM_COUNT;
updateAbsolutePosition();
}
void clear() {
box.text = "";
for(uint i = 0; i < ITEM_COUNT; ++i)
@items[i] = null;
selected = 0;
hovered = -1;
}
void hide() {
visible = false;
clear();
}
void next() {
if(selected < ITEM_COUNT - 1) {
if(items[selected + 1] !is null)
selected += 1;
}
}
void prev() {
if(selected > 0)
selected -= 1;
}
bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) {
switch(event.type) {
case KET_Key_Down:
if(event.key == KEY_UP) {
prev();
return true;
}
else if(event.key == KEY_DOWN) {
next();
return true;
}
else if(event.key == KEY_TAB) {
if(shiftKey)
prev();
else
next();
return true;
}
else if(event.key == KEY_ESC) {
return true;
}
break;
case KET_Key_Up:
if(event.key == KEY_ESC) {
hide();
setGuiFocus(ActiveTab);
return true;
}
else if(event.key == KEY_UP) {
return true;
}
else if(event.key == KEY_DOWN) {
return true;
}
else if(event.key == KEY_TAB) {
return true;
}
break;
}
return BaseGuiElement::onKeyEvent(event, source);
}
void updateHovered() {
vec2i rel = mousePos - AbsolutePosition.topLeft;
if(rel.x < 4 || rel.x > size.width - 4) {
hovered = -1;
return;
}
hovered = (rel.y - 64) / 30;
if(hovered < 0 || hovered >= ITEM_COUNT || items[hovered] is null) {
hovered = -1;
return;
}
}
bool onGuiEvent(const GuiEvent& event) {
switch(event.type) {
case GUI_Focus_Lost:
if(event.other is null || !event.other.isChildOf(this)) {
hide();
return false;
}
break;
case GUI_Changed:
search();
break;
case GUI_Confirmed:
if(box.text != "" && items[selected] !is null)
items[selected].go();
hide();
if(getGuiFocus() !is null && getGuiFocus().isChildOf(this))
setGuiFocus(ActiveTab);
return true;
}
return BaseGuiElement::onGuiEvent(event);
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
if(source is this) {
switch(event.type) {
case MET_Button_Up:
if(hovered >= 0)
items[hovered].go();
hide();
return true;
case MET_Moved:
updateHovered();
break;
}
}
return BaseGuiElement::onMouseEvent(event, source);
}
void search() {
GoSearch(box.text, items);
selected = 0;
hovered = -1;
}
void drawItem(uint i, const recti& absPos) {
GoItem@ item = items[i];
uint flags = SF_Normal;
if(i == selected)
flags |= SF_Active;
if(int(i) == hovered)
flags |= SF_Hovered;
skin.draw(SS_GoItem, flags, absPos);
recti iconPos = recti_area(
absPos.topLeft + vec2i(4, 6),
vec2i(20, 18));
Sprite sprt = item.icon;
if(sprt.valid)
sprt.draw(iconPos);
else if(item.skinIcon != SS_NULL)
skin.draw(item.skinIcon, SF_Normal, iconPos);
skin.draw(FT_Medium, absPos.topLeft + vec2i(28, 4), item.text);
skin.draw(FT_Medium, absPos.botRight - vec2i(204, 26), item.type);
}
void draw() {
//Draw the background
skin.draw(SS_GoDialog, SF_Normal, AbsolutePosition);
//Draw the items
for(uint i = 0; i < ITEM_COUNT; ++i) {
if(items[i] is null)
break;
drawItem(i, recti_area(
AbsolutePosition.topLeft + vec2i(8, 64 + i * 30),
vec2i(size.width - 16, 30)));
}
BaseGuiElement::draw();
}
};
void gogogo(bool pressed) {
if(pressed) {
dialog.visible = true;
dialog.bringToFront();
setGuiFocus(dialog.box);
}
}
void toggleGoDialog() {
if(!dialog.visible) {
dialog.visible = true;
dialog.bringToFront();
setGuiFocus(dialog.box);
}
else {
dialog.hide();
}
}
GoDialog@ dialog;
IGuiElement@ getGoDialog() {
return dialog;
}
void init() {
//Initialize dialog
@dialog = GoDialog();
dialog.visible = false;
//Initialize static items
staticItems.insertLast(GoResearch());
staticItems.insertLast(GoDesigns());
staticItems.insertLast(GoGalaxy());
staticItems.insertLast(GoConsole());
//Add systems to static items
for(uint i = 0, cnt = systemCount; i < cnt; ++i)
staticItems.insertLast(GoSystem(getSystem(i)));
//Add resource finds
for(uint i = 0, cnt = getResourceCount(); i < cnt; ++i) {
auto@ type = getResource(i);
if(!type.artificial)
staticItems.insertLast(GoResource(type));
}
//Add resource classes
for(uint i = 0, cnt = getResourceClassCount(); i < cnt; ++i) {
auto@ type = getResourceClass(i);
staticItems.insertLast(GoResourceClass(type));
}
keybinds::Global.addBind(KB_GO_MENU, "gogogo");
}
+178
View File
@@ -0,0 +1,178 @@
#priority init 90
import systems;
enum ScopeType {
ST_Invalid,
ST_System,
ST_COUNT
};
class Scope {
int id;
Scope() {
id = -1;
}
ScopeType get_type() {
return ST_Invalid;
}
vec3d get_position() {
return vec3d();
}
string get_name() {
return "---";
}
double get_radius() {
return 0.0;
}
int get_priority() {
return 0;
}
};
class SystemScope : Scope {
SystemDesc@ sys;
SystemScope(SystemDesc@ system) {
@sys = system;
id = -1;
}
ScopeType get_type() {
return ST_System;
}
vec3d get_position() {
return sys.position;
}
double get_radius() {
return sys.radius;
}
int get_priority() {
return 0;
}
string get_name() {
return sys.object.name;
}
};
class ScopeSearch {
int minPriority;
int maxPriority;
double priorityFactor;
double minDistance;
double maxDistance;
bool[] allowedTypes;
vec3d position;
Scope@[] results;
double[] factors;
ScopeSearch() {
minPriority = INT_MIN;
maxPriority = INT_MAX;
priorityFactor = 1.0;
minDistance = -INFINITY;
maxDistance = INFINITY;
allowedTypes.length = ST_COUNT;
for(int i = 0; i < ST_COUNT; ++i)
allowedTypes[i] = true;
}
};
namespace scopes {
::map scopes;
int nextScopeId = 1;
};
void init() {
//Add systems as scopes
for(uint i = 0, cnt = systemCount; i < cnt; ++i)
addScope(SystemScope(getSystem(i)));
}
int addScope(Scope@ scope) {
if(scope.id <= 0)
scope.id = scopes::nextScopeId++;
scopes::scopes.set(scope.id, @scope);
return scope.id;
}
void removeScope(Scope@ scope) {
removeScope(scope.id);
}
void removeScope(int id) {
if(id > 0)
scopes::scopes.delete(id);
}
uint searchScopes(ScopeSearch@ s, uint amount) {
uint found = 0;
s.results.length = amount;
s.factors.length = amount;
double maxFactor = 0.0;
map_iterator it = scopes::scopes.iterator();
Scope@ scope;
while(it.iterate(@scope)) {
int priority = scope.priority;
if(priority < s.minPriority || priority > s.maxPriority)
continue;
double factor = s.position.distanceTo(scope.position);
factor -= scope.radius;
if(factor < s.minDistance || factor > s.maxDistance)
continue;
if(priority != 0)
factor /= double(priority) * s.priorityFactor;
if(found < amount) {
if(found == 0) {
@s.results[0] = scope;
s.factors[0] = factor;
}
else {
for(int i = found; i >= 0; --i) {
if(i == 0 || factor > s.factors[i-1]) {
@s.results[i] = scope;
s.factors[i] = factor;
break;
}
@s.results[i] = s.results[i-1];
s.factors[i] = s.factors[i-1];
}
}
maxFactor = s.factors[found];
++found;
}
else if(factor < maxFactor) {
for(int i = amount - 1; i >= 0; --i) {
if(i == 0 || factor > s.factors[i-1]) {
@s.results[i] = scope;
s.factors[i] = factor;
break;
}
@s.results[i] = s.results[i-1];
s.factors[i] = s.factors[i-1];
}
maxFactor = s.factors[amount-1];
}
}
if(found != amount) {
s.results.length = found;
s.factors.length = found;
}
return found;
}
+326
View File
@@ -0,0 +1,326 @@
#priority init 10
import navigation.scopes;
import navigation.SmartCamera;
from input import activeCamera;
import elements.BaseGuiElement;
const int TOPBAR_OFFSET = 77;
const int SCOPE_SPACING = 4;
const int SCOPE_WIDTH = 50;
const int SCOPE_HEIGHT = 50;
const int SCOPE_PADDING = 2;
const double SEARCH_INTERVAL = 1.0;
const uint MAX_SCOPES = 16;
Color scopeColor(0xffffff40);
Color scopeHoverColor(0x00ff0040);
Color scopeActiveColor(0xff000040);
Color scopePanningColor(0x0000ff40);
string ellipsis = "-";
const double smartPanSpeed = 10000.0;
const double fadeDistanceMin = 10000.0;
const double fadeDistanceMax = 16000.0;
ScopeSearch search;
double searchTimer = 0.0;
ScopeButton@ panningButton;
ScopeButton@[] buttons;
bool shown = false;
uint displayedScopes = 0;
void init() {
search.maxDistance = 16000.0;
@panningButton = ScopeButton();
panningButton.visible = false;
buttons.length = MAX_SCOPES;
for(uint i = 0; i < MAX_SCOPES; ++i) {
@buttons[i] = ScopeButton();
buttons[i].visible = false;
}
}
void showSmartPan() {
shown = true;
update();
searchTimer = SEARCH_INTERVAL;
}
void hideSmartPan() {
shown = false;
for(uint i = 0; i < MAX_SCOPES; ++i)
buttons[i].visible = false;
}
void toggleSmartPan() {
if(shown)
hideSmartPan();
else
showSmartPan();
}
void update() {
search.position = activeCamera.lookAt;
displayedScopes = searchScopes(search, MAX_SCOPES);
for(uint i = 0; i < displayedScopes; ++i) {
Scope@ scope = search.results[i];
//Ignore current scope
if(search.position.distanceTo(scope.position) < scope.radius
|| (panningButton.visible && panningButton.scope is scope)) {
buttons[i].visible = false;
continue;
}
//Ready the scope button
buttons[i].visible = true;
buttons[i].set(scope);
buttons[i].updatePosition();
}
//Hide extra buttons
for(uint i = displayedScopes; i < MAX_SCOPES; ++i)
buttons[i].visible = false;
}
void updatePositions() {
for(uint i = 0; i < displayedScopes; ++i) {
Scope@ scope = buttons[i].scope;
if(scope is null || search.position.distanceTo(scope.position) < scope.radius
|| (panningButton.visible && panningButton.scope is scope)) {
buttons[i].visible = false;
}
else {
buttons[i].visible = true;
buttons[i].updatePosition();
}
}
}
bool waitForMove = true;
int buttonAlpha = 80;
void tick(double time) {
if(!shown)
return;
//Fade the buttons out at large distances
double dist = activeCamera.distance;
if(dist > fadeDistanceMin) {
if(dist >= fadeDistanceMax) {
buttonAlpha = 0;
return;
}
else {
buttonAlpha = 80 - ceil(80.0 * (dist - fadeDistanceMin) / (fadeDistanceMax - fadeDistanceMin));
}
}
else {
buttonAlpha = 80;
}
//Check if we're panning with a button
vec2i mpos = mousePos;
if(buttonAlpha > 0 && mpos.x < 6 || mpos.x >= screenSize.width - 6) {
if(!waitForMove) {
bool found = false;
if(panningButton.visible) {
if(!panningButton.AbsolutePosition.isWithin(mpos)) {
panningButton.visible = false;
}
else {
double tickPan = time * smartPanSpeed;
if(activeCamera.panTo(panningButton.scope.position, tickPan)) {
panningButton.visible = false;
waitForMove = true;
}
}
}
else {
for(uint i = 0; i < displayedScopes; ++i) {
if(buttons[i].visible && buttons[i].AbsolutePosition.isWithin(mpos)) {
buttons[i].visible = false;
panningButton.set(buttons[i].scope);
panningButton.AbsolutePosition = buttons[i].AbsolutePosition;
panningButton.AbsoluteClipRect = buttons[i].AbsoluteClipRect;
panningButton.visible = true;
}
}
}
}
}
else {
panningButton.visible = false;
waitForMove = false;
}
//Update the buttons
//if(searchTimer <= 0.0) {
//searchTimer = SEARCH_INTERVAL;
update();
//}
//else {
//updatePositions();
//searchTimer -= time;
//}
}
class ScopeButton : BaseGuiElement {
Scope@ scope;
string name;
bool left;
bool Hovered;
bool Pressed;
ScopeButton() {
Hovered = false;
Pressed = false;
super(null, recti(0, 0, SCOPE_WIDTH, SCOPE_HEIGHT));
}
void updatePosition() {
vec3d camPos = activeCamera.lookAt;
vec3d scopePos = scope.position;
double flatAngle = activeCamera.screenAngle(scopePos);
double percentage = 0.0;
vec2i pos();
if(flatAngle >= 0 && flatAngle <= 0.5) {
left = false;
percentage = 0.5 - flatAngle;
}
else if(flatAngle > 0.5 && flatAngle <= 1.0) {
left = true;
percentage = flatAngle - 0.5;
}
else if(flatAngle < 0 && flatAngle >= -0.5) {
left = false;
percentage = 0.5 + flatAngle * -1.0;
}
else if(flatAngle < -0.5 && flatAngle >= -1.0) {
left = true;
percentage = 1.5 + flatAngle;
}
if(left)
pos.x = 0;
else
pos.x = screenSize.width - size.width;
pos.y = TOPBAR_OFFSET;
pos.y += double(screenSize.height - TOPBAR_OFFSET - SCOPE_HEIGHT) * percentage;
bool overlap;
do {
overlap = false;
recti box = recti_area(pos, size);
if(panningButton.visible && panningButton.AbsolutePosition.overlaps(box)) {
overlap = true;
pos.y = panningButton.AbsolutePosition.topLeft.y;
if(percentage < 0.5)
pos.y += SCOPE_HEIGHT + SCOPE_SPACING;
else
pos.y -= SCOPE_HEIGHT + SCOPE_SPACING;
continue;
}
for(uint i = 0; i < MAX_SCOPES; ++i) {
if(buttons[i] is this)
break;
if(buttons[i].left == left && buttons[i].AbsolutePosition.overlaps(box)) {
overlap = true;
pos.y = buttons[i].AbsolutePosition.topLeft.y;
if(percentage < 0.5)
pos.y += SCOPE_HEIGHT + SCOPE_SPACING;
else
pos.y -= SCOPE_HEIGHT + SCOPE_SPACING;
break;
}
}
if(pos.y < 0 || pos.y + SCOPE_HEIGHT > screenSize.height) {
visible = false;
return;
}
} while(overlap);
position = pos;
}
void set(Scope@ s) {
@scope = s;
name = scope.name;
}
void trigger() {
activeCamera.zoomTo(scope.position);
}
bool onGuiEvent(const GuiEvent& event) {
if(event.caller is this && buttonAlpha > 0) {
switch(event.type) {
case GUI_Mouse_Entered:
Hovered = true;
break;
case GUI_Mouse_Left:
Hovered = false;
Pressed = false;
break;
}
}
return BaseGuiElement::onGuiEvent(event);
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
if(source is this && buttonAlpha > 0) {
switch(event.type) {
case MET_Button_Down:
if(event.button == 0) {
Pressed = true;
}
return true;
case MET_Button_Up:
if(event.button == 0 && Pressed) {
Pressed = false;
trigger();
}
return true;
}
}
return BaseGuiElement::onMouseEvent(event, source);
}
void draw() {
if(buttonAlpha <= 0)
return;
Color col = scopeColor;
if(panningButton is this)
col = scopePanningColor;
else if(Pressed)
col = scopeActiveColor;
else if(Hovered && !panningButton.visible)
col = scopeHoverColor;
col.a = buttonAlpha;
drawRectangle(AbsolutePosition, col);
const Font@ fnt = skin.getFont(FT_Normal);
recti textPos = AbsolutePosition;
textPos.topLeft.x += SCOPE_PADDING;
textPos.botRight.x -= SCOPE_PADDING;
int vpadd = (textPos.height - fnt.getLineHeight()) / 2;
textPos.topLeft.y += vpadd;
textPos.botRight.y -= vpadd;
fnt.draw(textPos, name, ellipsis);
BaseGuiElement::draw();
}
};
+77
View File
@@ -0,0 +1,77 @@
#priority init 100
from settings.map_lib import SystemDesc;
import void recalculateElevation() from "navigation.elevation";
import void promptExtentRefresh() from "tabs.GalaxyTab";
SystemDesc[] Systems;
uint get_systemCount() {
return Systems.length;
}
SystemDesc@ getSystem(uint index) {
return Systems[index];
}
SystemDesc@ getSystem(Region@ region) {
if(region is null || region.SystemId == -1)
return null;
return Systems[region.SystemId];
}
SystemDesc@ getSystem(const string& name) {
//TODO: Use dictionary
uint cnt = systemCount;
for(uint i = 0; i < cnt; ++i) {
if(getSystem(i).name == name)
return getSystem(i);
}
return null;
}
SystemDesc@ getSystem(const vec3d& point) {
//TODO: This really should not be O(n), implement a better
//structure for these lookups
for(uint i = 0, cnt = Systems.length; i < cnt; ++i) {
SystemDesc@ sys = Systems[i];
if(point.distanceToSQ(sys.position) < sys.radius * sys.radius)
return sys;
}
return null;
}
bool loaded = false, doRefresh = false;
void init() {
if(loaded)
return;
Systems.syncFrom(getSystems());
}
void tick(double time) {
if(doRefresh) {
Systems.length = 0;
Systems.syncFrom(getSystems());
doRefresh = false;
promptExtentRefresh();
recalculateElevation();
}
}
void save(SaveFile& data) {
data << uint(Systems.length);
for(uint i = 0; i < Systems.length; ++i)
Systems[i].save(data);
}
void load(SaveFile& data) {
loaded = true;
uint count = 0;
data >> count;
Systems.length = count;
for(uint i = 0; i < Systems.length; ++i)
Systems[i].load(data);
}
void refreshClientSystems() {
doRefresh = true;
}