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