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

327 lines
7.1 KiB
ActionScript

#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();
}
};