586 lines
14 KiB
ActionScript
586 lines
14 KiB
ActionScript
//input.as
|
|
//--------
|
|
//Various script callbacks for global input handling.
|
|
//Also handles passing events to the GUI
|
|
|
|
#priority init 1
|
|
import elements.IGuiElement;
|
|
import targeting.targeting;
|
|
import navigation.SmartCamera;
|
|
import util.convar;
|
|
from navigation.elevation import getElevationIntersect;
|
|
|
|
import bool onMouseEvent(const MouseEvent&) from "gui";
|
|
import bool onKeyboardEvent(const KeyboardEvent&) from "gui";
|
|
import bool onGuiEvent(const GuiEvent&) from "gui";
|
|
import void onGuiNavigate(vec2d direction) from "gui";
|
|
import IGuiElement@ getGuiFocus() from "gui";
|
|
import bool isGuiHovered() from "gui";
|
|
import void selectionClick(uint button, bool pressed) from "obj_selection";
|
|
import void dragSelect(const recti&) from "obj_selection";
|
|
import Object@ get_hoveredObject() from "obj_selection";
|
|
import Object@ get_selectedObject() from "obj_selection";
|
|
import Object@ get_uiObject() from "obj_selection";
|
|
import void updateHoveredObject() from "obj_selection";
|
|
import bool objectKeyEvent(int key, bool pressed) from "commands";
|
|
import void switchToTab(int pos) from "tabs.tabbar";
|
|
import bool tabEscape() from "tabs.tabbar";
|
|
import double getAverageElevation() from "navigation.elevation";
|
|
|
|
ConVar FollowAI("follow_ai", 0);
|
|
|
|
//TODO: Anything directly using the engine camera should go through the camera module
|
|
|
|
const double tickZoom = 1.2;
|
|
const double zoomPerPixel = 0.01;
|
|
const double camAnglesPerPixel = 0.22 * twopi / 360.0;
|
|
const double movePerPixel = 0.001;
|
|
const double zoomPerSecond = 0.45;
|
|
const double rollBorder = 0.4;
|
|
const double camMotionPerSecond = 80.0;
|
|
|
|
vec2i dragArea, dragFrom;
|
|
array<double> edgePanDelay = {0.0, 0.0, 0.0, 0.0};
|
|
array<double> edgePanRampup = {0.3, 0.3, 0.3, 0.3};
|
|
array<double> edgePanTimers = {0.0, 0.0, 0.0, 0.0};
|
|
bool doingBoxSelect = false;
|
|
|
|
Joystick joystick(1);
|
|
bool joystickConnected = false;
|
|
bool joystickActive = false;
|
|
|
|
SmartCamera@ ActiveCamera;
|
|
SmartCamera PreviousCamera;
|
|
|
|
SmartCamera@ get_activeCamera() {
|
|
return ActiveCamera;
|
|
}
|
|
|
|
SmartCamera@ get_lastCamera() {
|
|
if(ActiveCamera !is null)
|
|
return ActiveCamera;
|
|
return PreviousCamera;
|
|
}
|
|
|
|
void set_activeCamera(SmartCamera@ cam) {
|
|
if(ActiveCamera !is null)
|
|
PreviousCamera = ActiveCamera;
|
|
|
|
@ActiveCamera = cam;
|
|
}
|
|
|
|
void copyPreviousCamera(SmartCamera@ cam) {
|
|
if(ActiveCamera !is null)
|
|
cam = ActiveCamera;
|
|
else
|
|
cam = PreviousCamera;
|
|
}
|
|
|
|
vec3d mouseToGrid(vec2i mpos) {
|
|
double plane = getAverageElevation();
|
|
vec3d dest;
|
|
line3dd ray = ActiveCamera.screenToRay(mpos);
|
|
if(!getElevationIntersect(ray, dest))
|
|
ray.intersectY(dest, plane, false);
|
|
return dest;
|
|
}
|
|
|
|
vec3d mouseToGrid(vec2i mpos, double plane) {
|
|
vec3d dest;
|
|
line3dd ray = ActiveCamera.screenToRay(mpos);
|
|
if(!getElevationIntersect(ray, dest))
|
|
ray.intersectY(dest, plane, false);
|
|
return dest;
|
|
}
|
|
|
|
void take_screenshot(bool pressed) {
|
|
if(!pressed)
|
|
takeScreenshot("screenshot");
|
|
}
|
|
|
|
//Called when the platform overlay (e.g. Steam Overlay) is opened or closed
|
|
double prevGameSpeed = 1.0;
|
|
void onOverlayChanged(bool overlayOpen) {
|
|
if(!mpServer && !mpClient) {
|
|
if(overlayOpen) {
|
|
prevGameSpeed = gameSpeed;
|
|
gameSpeed = 0.0;
|
|
}
|
|
else {
|
|
gameSpeed = prevGameSpeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Called whenever the mouse wheel is used on the 3D view.
|
|
// x: Amount scrolled horizontally.
|
|
// y: Amount scrolled vertically.
|
|
void onMouseWheel(double x, double y) {
|
|
if(isGuiHovered() || ActiveCamera is null) {
|
|
MouseEvent evt;
|
|
evt.type = MET_Scrolled;
|
|
evt.x = int(floor(x));
|
|
evt.y = int(floor(y));
|
|
onMouseEvent(evt);
|
|
return;
|
|
}
|
|
|
|
if(targetMouseWheel(x, y))
|
|
return;
|
|
|
|
activeCamera.zoom(y);
|
|
}
|
|
|
|
//Called when the mouse is dragged with one or more buttons
|
|
//pressed on the 3D view.
|
|
// buttons: Bitmask of pressed buttons (0x1 << button_num)
|
|
// x: Mouse x position.
|
|
// y: Mouse y position.
|
|
// dx: Movement in the x dimension since last event.
|
|
// dy: Movement in the y dimension since last event.
|
|
void onMouseDragged(int buttons, int x, int y, int dx, int dy) {
|
|
if(activeCamera is null)
|
|
return;
|
|
if(targetMouseDragged(buttons, x, y, dx, dy))
|
|
return;
|
|
|
|
Camera@ cam = activeCamera.camera;
|
|
vec2i screen = screenSize;
|
|
|
|
if(buttons == 0x1) {
|
|
doingBoxSelect = true;
|
|
dragArea += vec2i(dx,-dy);
|
|
dragFrom = vec2i(x,y);
|
|
}
|
|
else if(buttons == 0x2) {
|
|
double dist = sqrt(sqr(double(x)/double(screen.x) - 0.5)
|
|
+ sqr(double(y)/double(screen.y) - 0.5));
|
|
|
|
if(settings::bEdgeRoll && rollBorder < dist) {
|
|
if(x < screen.x / 2)
|
|
activeCamera.roll(dy);
|
|
else
|
|
activeCamera.roll(-dy);
|
|
}
|
|
else {
|
|
activeCamera.rotate(dx, dy);
|
|
}
|
|
}
|
|
else if(buttons == 0x3) {
|
|
//-- Left and right buttons
|
|
if(settings::bInvertHorizRot)
|
|
dx = -dx;
|
|
|
|
//Horizontal movement does an absolute yaw
|
|
if(cam.inverted)
|
|
cam.abs_yaw(double(-dx) * camAnglesPerPixel);
|
|
else
|
|
cam.abs_yaw(double(dx) * camAnglesPerPixel);
|
|
|
|
//Vertical movement zooms
|
|
cam.zoom(1.0 + zoomPerPixel * double(-dy));
|
|
CAM_ZOOMED = true;
|
|
}
|
|
else if(buttons == 0x4) {
|
|
//-- Only middle button
|
|
//Pan the world
|
|
activeCamera.pan(dx, dy);
|
|
}
|
|
}
|
|
|
|
//Called when a drag event thas ended
|
|
bool onMouseDragEnd(int button) {
|
|
if(targetMouseDragEnd(button))
|
|
return true;
|
|
if(doingBoxSelect) {
|
|
if(button != 0) {
|
|
doingBoxSelect = false;
|
|
return false;
|
|
}
|
|
recti box = recti_area(dragFrom, dragArea);
|
|
|
|
//Make sure the rect is oriented top-down, left-right
|
|
if(box.topLeft.x > box.botRight.x) {
|
|
int temp = box.topLeft.x;
|
|
box.topLeft.x = box.botRight.x;
|
|
box.botRight.x = temp;
|
|
}
|
|
if(box.topLeft.y > box.botRight.y) {
|
|
int temp = box.topLeft.y;
|
|
box.topLeft.y = box.botRight.y;
|
|
box.botRight.y = temp;
|
|
}
|
|
|
|
dragSelect(box);
|
|
|
|
//Place the cursor to the dragged destination
|
|
doingBoxSelect = false;
|
|
mousePos = dragFrom + dragArea;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//Called when the mouse is moved over the 3d view without
|
|
//any buttons being held.
|
|
// x: Mouse x position.
|
|
// y: Mouse y position.
|
|
void onMouseMoved(int x, int y) {
|
|
MouseEvent evt;
|
|
evt.type = MET_Moved;
|
|
evt.x = x;
|
|
evt.y = y;
|
|
if(onMouseEvent(evt))
|
|
return;
|
|
if(targetMouseMoved(x, y))
|
|
return;
|
|
}
|
|
|
|
//Called when a mouse button is clicked
|
|
// buttons: Button that was clicked.
|
|
//void onMouseClicked(int button) {
|
|
//}
|
|
|
|
//Called when a mouse button is double-clicked
|
|
// buttons: Button that was clicked.
|
|
//void onMouseDoubleClicked(int button) {
|
|
//}
|
|
|
|
//General handler for when a mouse button event occurs
|
|
// button: Button that the event pertains to.
|
|
// pressed: Whether the button was pressed or released.
|
|
bool onMouseButton(int button, bool pressed) {
|
|
MouseEvent evt;
|
|
if(pressed)
|
|
evt.type = MET_Button_Down;
|
|
else
|
|
evt.type = MET_Button_Up;
|
|
evt.button = button;
|
|
vec2i mpos = mousePos;
|
|
evt.x = mpos.x;
|
|
evt.y = mpos.y;
|
|
|
|
bool fromTarget = uiObject !is null;
|
|
if(fromTarget && targetMouseButton(button, pressed))
|
|
return false;
|
|
if(onMouseEvent(evt))
|
|
return true;
|
|
if(!fromTarget && targetMouseButton(button, pressed))
|
|
return false;
|
|
if(pressed)
|
|
dragArea = vec2i(0,0);
|
|
selectionClick(button, pressed);
|
|
return false;
|
|
}
|
|
|
|
//Called when a unicode char is typed into the main 3D view.
|
|
// chr: Unicode code point that was typed.
|
|
bool onCharTyped(int chr) {
|
|
KeyboardEvent evt;
|
|
evt.type = KET_Key_Typed;
|
|
evt.key = chr;
|
|
return onKeyboardEvent(evt);
|
|
}
|
|
|
|
//Called when a key event occurs when the 3D view has focus.
|
|
// key: Keyboard key that was pressed.
|
|
// pressed: Whether the key was pressed or released.
|
|
bool onKeyEvent(int key, int keyaction) {
|
|
KeyboardEvent evt;
|
|
bool pressed = (keyaction & KA_Pressed) != 0;
|
|
if(pressed)
|
|
evt.type = KET_Key_Down;
|
|
else
|
|
evt.type = KET_Key_Up;
|
|
evt.key = key;
|
|
if(onKeyboardEvent(evt))
|
|
return true;
|
|
if(objectKeyEvent(key, pressed))
|
|
return true;
|
|
if(targetKeyEvent(key, pressed))
|
|
return true;
|
|
if(pressed && key == KEY_ESC) {
|
|
if(tabEscape())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint[] worldMove = {0, 0, 0, 0};
|
|
void world_forward(bool pressed) {
|
|
worldMove[3] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void world_left(bool pressed) {
|
|
worldMove[0] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void world_right(bool pressed) {
|
|
worldMove[2] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void world_backward(bool pressed) {
|
|
worldMove[1] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void reset_cam(bool pressed) {
|
|
if(!pressed && activeCamera !is null) {
|
|
Camera@ cam = activeCamera.camera;
|
|
activeCamera.reset();
|
|
Object@ hw = playerEmpire.Homeworld;
|
|
if(hw is null)
|
|
@hw = selectedObject;
|
|
if(hw !is null)
|
|
activeCamera.zoomTo(hw);
|
|
else
|
|
activeCamera.zoomTo(vec3d());
|
|
cam.snap();
|
|
}
|
|
}
|
|
|
|
uint[] zoom = {0, 0};
|
|
void zoom_in(bool pressed) {
|
|
zoom[0] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void zoom_out(bool pressed) {
|
|
zoom[1] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void zoom_object(bool pressed) {
|
|
if(!pressed && ActiveCamera !is null) {
|
|
Object@ sel = selectedObject;
|
|
if(sel !is null && sel.visible)
|
|
ActiveCamera.zoomLockObject(sel);
|
|
}
|
|
}
|
|
|
|
double threshold(double x, double thresh) {
|
|
if(abs(x) < thresh)
|
|
return 0.0;
|
|
else
|
|
return x;
|
|
}
|
|
|
|
bool leftCannon = false;
|
|
bool movePressed = false;
|
|
|
|
void onControllerButton(int i, bool pressed) {
|
|
joystickActive = true;
|
|
if(pressed) {
|
|
if(i == GP_LB)
|
|
switchToTab(-1);
|
|
else if(i == GP_RB)
|
|
switchToTab(+1);
|
|
}
|
|
}
|
|
|
|
GuiEvent joyGuiEvent;
|
|
|
|
vec3d lastAIFocus;
|
|
|
|
void tick(double time) {
|
|
if(game_state != GS_Game)
|
|
return;
|
|
|
|
if(FollowAI.value != 0) {
|
|
vec3d to = playerEmpire.aiFocus;
|
|
if(to != lastAIFocus) {
|
|
activeCamera.zoomTo(to);
|
|
lastAIFocus = to;
|
|
}
|
|
}
|
|
|
|
double tickMotion = time * camMotionPerSecond * 60.0;
|
|
|
|
//Polling a disconnected joystick on Windows takes ~10ms, so avoid it
|
|
if(joystickConnected && joystick.poll()) {
|
|
//Send buttons to gui
|
|
for(uint i = 0, cnt = joystick.buttonCount; i < cnt; ++i) {
|
|
uint8 state = joystick.button[i];
|
|
if(state == JBS_Pressed) {
|
|
//GUI Event
|
|
joyGuiEvent.type = GUI_Controller_Down;
|
|
joyGuiEvent.value = i;
|
|
@joyGuiEvent.caller = getGuiFocus();
|
|
if(!onGuiEvent(joyGuiEvent))
|
|
onControllerButton(i, true);
|
|
}
|
|
else if(state == JBS_Released) {
|
|
//GUI Event
|
|
joyGuiEvent.type = GUI_Controller_Up;
|
|
joyGuiEvent.value = i;
|
|
@joyGuiEvent.caller = getGuiFocus();
|
|
if(!onGuiEvent(joyGuiEvent))
|
|
onControllerButton(i, false);
|
|
}
|
|
}
|
|
|
|
if(activeCamera !is null) {
|
|
//Panning with left analog
|
|
vec2d pan = vec2d(-joystick.axis[GP_AXIS_LX], joystick.axis[GP_AXIS_LY]);
|
|
if(pan.length > 0.05) {
|
|
pan.normalize(tickMotion * 0.7);
|
|
activeCamera.pan(pan);
|
|
mousePos = screenSize * 0.5;
|
|
joystickActive = true;
|
|
}
|
|
|
|
//Zoom and rotate with right analog
|
|
double zoom = -joystick.axis[GP_AXIS_RY];
|
|
if(abs(zoom) > 0)
|
|
activeCamera.camera.zoom(1.0 + 7.0 * zoomPerSecond * zoom * time * settings::dZoomSpeed);
|
|
|
|
double yaw = -joystick.axis[GP_AXIS_RX];
|
|
if(abs(yaw) > 0)
|
|
activeCamera.rotate(600.0 * yaw * time, 0);
|
|
}
|
|
else {
|
|
//Navigate gui with left stick
|
|
vec2d dir = vec2d(joystick.axis[GP_AXIS_LX], -joystick.axis[GP_AXIS_LY]);
|
|
if(dir.length > 0.25) {
|
|
if(!movePressed)
|
|
onGuiNavigate(dir);
|
|
movePressed = true;
|
|
}
|
|
else {
|
|
movePressed = false;
|
|
}
|
|
}
|
|
|
|
/*for(uint i = 0; i < joystick.buttonCount; ++i) {
|
|
uint8 state = joystick.button[i];
|
|
if(state == JBS_Pressed)
|
|
print("Joystick 1, key " + i + " pressed");
|
|
else if(state == JBS_Released)
|
|
print("Joystick 1, key " + i + " released");
|
|
}
|
|
|
|
for(uint i = 0; i < joystick.axisCount; ++i) {
|
|
double axis = joystick.axis[i];
|
|
if(abs(axis) > 0.01)
|
|
print("Joystick axis " + i + ": " + axis);
|
|
}*/
|
|
|
|
if(joystickActive)
|
|
updateHoveredObject();
|
|
}
|
|
|
|
if(activeCamera is null)
|
|
return;
|
|
|
|
Camera@ cam = activeCamera.camera;
|
|
bool inverted = cam.inverted;
|
|
|
|
vec2d move;
|
|
|
|
//Handle edge panning
|
|
vec2i mouse = mousePos;
|
|
if(settings::bEdgePan && windowFocused && mouseOverWindow) {
|
|
vec2i screen = screenSize;
|
|
|
|
if(mouse.x <= 0) {
|
|
edgePanTimers[0] += time;
|
|
if(edgePanTimers[0] >= edgePanDelay[0]) {
|
|
move.x += 1;
|
|
if(edgePanRampup[0] > 0)
|
|
tickMotion *= clamp((edgePanTimers[0] - edgePanDelay[0]) / edgePanRampup[0], 0.0, 1.0);
|
|
}
|
|
}
|
|
else {
|
|
edgePanTimers[0] = 0.0;
|
|
}
|
|
|
|
if(mouse.x >= screen.x - 2) {
|
|
edgePanTimers[1] += time;
|
|
if(edgePanTimers[1] >= edgePanDelay[1]) {
|
|
move.x -= 1;
|
|
if(edgePanRampup[1] > 0)
|
|
tickMotion *= clamp((edgePanTimers[1] - edgePanDelay[1]) / edgePanRampup[1], 0.0, 1.0);
|
|
}
|
|
}
|
|
else {
|
|
edgePanTimers[1] = 0.0;
|
|
}
|
|
|
|
if(mouse.y <= 0) {
|
|
edgePanTimers[2] += time;
|
|
if(edgePanTimers[2] >= edgePanDelay[2]) {
|
|
move.y += 1;
|
|
if(edgePanRampup[2] > 0)
|
|
tickMotion *= clamp((edgePanTimers[2] - edgePanDelay[2]) / edgePanRampup[2], 0.0, 1.0);
|
|
}
|
|
}
|
|
else {
|
|
edgePanTimers[2] = 0.0;
|
|
}
|
|
|
|
if(mouse.y >= screen.y - 2) {
|
|
edgePanTimers[3] += time;
|
|
if(edgePanTimers[3] >= edgePanDelay[3]) {
|
|
move.y -= 1;
|
|
if(edgePanRampup[0] > 0)
|
|
tickMotion *= clamp((edgePanTimers[3] - edgePanDelay[3]) / edgePanRampup[3], 0.0, 1.0);
|
|
}
|
|
}
|
|
else {
|
|
edgePanTimers[3] = 0.0;
|
|
}
|
|
}
|
|
|
|
//Move the world in the direction the keys were pressed
|
|
if(worldMove[0] ^ worldMove[2] != 0) {
|
|
if(worldMove[0] != 0)
|
|
move.x += 1;
|
|
else
|
|
move.x += -1;
|
|
}
|
|
if(worldMove[1] ^ worldMove[3] != 0) {
|
|
if(worldMove[1] != 0)
|
|
move.y += -1;
|
|
else
|
|
move.y += 1;
|
|
}
|
|
|
|
if(move.x != 0 || move.y != 0) {
|
|
move.normalize(tickMotion);
|
|
activeCamera.pan(move);
|
|
}
|
|
|
|
//Zoom the camera appropriately
|
|
if(zoom[0] ^ zoom[1] != 0) {
|
|
if(zoom[0] != 0)
|
|
cam.zoom(1.0 - zoomPerSecond * time * settings::dZoomSpeed);
|
|
else
|
|
cam.zoom(1.0 + zoomPerSecond * time * settings::dZoomSpeed);
|
|
}
|
|
}
|
|
|
|
void init() {
|
|
keybinds::Global.addBind(KB_WORLD_FORWARD, "world_forward");
|
|
keybinds::Global.addBind(KB_WORLD_LEFT, "world_left");
|
|
keybinds::Global.addBind(KB_WORLD_RIGHT, "world_right");
|
|
keybinds::Global.addBind(KB_WORLD_BACKWARD, "world_backward");
|
|
|
|
keybinds::Global.addBind(KB_ZOOM_IN, "zoom_in");
|
|
keybinds::Global.addBind(KB_ZOOM_OUT, "zoom_out");
|
|
|
|
keybinds::Global.addBind(KB_RESET_CAM, "reset_cam");
|
|
keybinds::Global.addBind(KB_TAKE_SCREENSHOT, "take_screenshot");
|
|
|
|
keybinds::Global.addBind(KB_ZOOM_LOCK_OBJECT, "zoom_object");
|
|
|
|
//joystickConnected = joystick.connected();
|
|
PreviousCamera.reset();
|
|
|
|
if(settings::bDelayTopEdge) {
|
|
edgePanDelay[2] = 0.3;
|
|
edgePanRampup[2] = 0.0;
|
|
}
|
|
}
|
|
|
|
void draw() {
|
|
if(doingBoxSelect)
|
|
drawRectangle(recti_area(dragFrom, dragArea), Color(0x00ff0040));
|
|
}
|