Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -1243,25 +1243,37 @@ return true; } -function handleMinimapEvent(target) +function handleMinimapEvent(data) { - // Partly duplicated from handleInputAfterGui(), but with the input being - // world coordinates instead of screen coordinates. + switch(data.type) + { + case "mouserightrelease": + case "mouserightdoubleclick": + // Partly duplicated from handleInputAfterGui(), but with the input being + // world coordinates instead of screen coordinates. - if (inputState != INPUT_NORMAL) - return false; + if (inputState != INPUT_NORMAL) + return false; - var fromMinimap = true; - var action = determineAction(undefined, undefined, fromMinimap); - if (!action) - return false; + var fromMinimap = true; + var action = determineAction(undefined, undefined, fromMinimap); + if (!action) + return false; - var selection = g_Selection.toList(); + var selection = g_Selection.toList(); - var queued = Engine.HotkeyIsPressed("session.queue"); - if (g_UnitActions[action.type] && g_UnitActions[action.type].execute) - return g_UnitActions[action.type].execute(target, action, selection, queued); - error("Invalid action.type " + action.type); + var queued = Engine.HotkeyIsPressed("session.queue"); + if (g_UnitActions[action.type] && g_UnitActions[action.type].execute) + return g_UnitActions[action.type].execute(data.target, action, selection, queued); + error("Invalid action.type " + action.type); + break; + case "mousemiddlerelease": + case "mousemiddledoubleclick": + // TODO: should probably instantly display the ping for myself, instead of delaying it through the server + if (g_IsNetworked) + Engine.SendNetworkChat("/map-ping " + JSON.stringify(data.target)); + break; + } return false; } Index: binaries/data/mods/public/gui/session/messages.js =================================================================== --- binaries/data/mods/public/gui/session/messages.js +++ binaries/data/mods/public/gui/session/messages.js @@ -196,6 +196,11 @@ g_Players[Engine.GetPlayerID()] && g_Players[senderID].isMutualAlly[Engine.GetPlayerID()], + "/map-ping": senderID => + g_Players[senderID] && + g_Players[Engine.GetPlayerID()] && + g_Players[senderID].isMutualAlly[Engine.GetPlayerID()], + "/enemies": senderID => g_Players[senderID] && g_Players[Engine.GetPlayerID()] && @@ -470,7 +475,7 @@ g_Selection.reset(); g_Selection.addList(selection, false, cmd.type == "gather"); }, - "play-tracks": function (notification, player) + "play-tracks": function(notification, player) { if (notification.lock) { @@ -1172,6 +1177,15 @@ msg.guid == Engine.GetPlayerGUID() : senderID == Engine.GetPlayerID(); + if (msg.cmd == "/map-ping" && g_IsChatAddressee[msg.cmd](senderID)) + { + let pos = JSON.parse(msg.text); + let minimap = Engine.GetGUIObjectByName("minimap"); + minimap.ping_x = pos.x; + minimap.ping_z = pos.z; + minimap.ping = true; + return false; + } // Parse private message let isPM = msg.cmd == "/msg"; let addresseeGUID; Index: binaries/data/mods/public/gui/session/minimap_panel.xml =================================================================== --- binaries/data/mods/public/gui/session/minimap_panel.xml +++ binaries/data/mods/public/gui/session/minimap_panel.xml @@ -6,7 +6,7 @@ sprite="mapPanel" > - + handleMinimapEvent(arguments[0]); SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress"); break; + case SDL_BUTTON_MIDDLE: + if (pNearest) + ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_MIDDLE, "mousemiddlepress"); + break; + default: break; } @@ -198,6 +203,17 @@ ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease"); } break; + case SDL_BUTTON_MIDDLE: + if (pNearest) + { + double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_MIDDLE]; + pNearest->m_LastClickTime[SDL_BUTTON_MIDDLE] = timer_Time(); + + if (timeElapsed < SELECT_DBLCLICK_RATE) + ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_MIDDLE, "mousemiddledoubleclick"); + else + ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_MIDDLE, "mousemiddlerelease"); + } } // Reset all states on all visible objects Index: source/gui/GUIbase.h =================================================================== --- source/gui/GUIbase.h +++ source/gui/GUIbase.h @@ -63,13 +63,16 @@ GUIM_MOUSE_PRESS_LEFT, GUIM_MOUSE_PRESS_LEFT_ITEM, GUIM_MOUSE_PRESS_RIGHT, + GUIM_MOUSE_PRESS_MIDDLE, GUIM_MOUSE_DOWN_LEFT, GUIM_MOUSE_DOWN_RIGHT, GUIM_MOUSE_DBLCLICK_LEFT, GUIM_MOUSE_DBLCLICK_LEFT_ITEM, // Triggered when doubleclicking on a list item GUIM_MOUSE_DBLCLICK_RIGHT, + GUIM_MOUSE_DBLCLICK_MIDDLE, GUIM_MOUSE_RELEASE_LEFT, GUIM_MOUSE_RELEASE_RIGHT, + GUIM_MOUSE_RELEASE_MIDDLE, GUIM_MOUSE_WHEEL_UP, GUIM_MOUSE_WHEEL_DOWN, GUIM_SETTINGS_UPDATED, // SGUIMessage.m_Value = name of setting Index: source/gui/MiniMap.h =================================================================== --- source/gui/MiniMap.h +++ source/gui/MiniMap.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -55,7 +55,7 @@ void SetCameraPos(); - void FireWorldClickEvent(int button, int clicks); + void FireWorldClickEvent(const CStr& eventType); // the terrain we are mini-mapping const CTerrain* m_Terrain; @@ -65,6 +65,8 @@ //Whether or not the mouse is currently down bool m_Clicking; + CPos m_MapPing; + // minimap texture handles GLuint m_TerrainTexture; @@ -94,8 +96,12 @@ void DrawViewRect(CMatrix3D transform); + void DrawPing(CMatrix3D transform); + void GetMouseWorldCoordinates(float& x, float& z); + CPos GetMapCoordinates(float x, float z); + float GetAngle(); VertexIndexArray m_IndexArray; Index: source/gui/MiniMap.cpp =================================================================== --- source/gui/MiniMap.cpp +++ source/gui/MiniMap.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -65,11 +65,14 @@ CMiniMap::CMiniMap() : m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f), m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), - m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false), m_WaterHeight(0.0) + m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false), m_WaterHeight(0.0), m_MapPing(0.0,0.0) { AddSetting(GUIST_CColor, "fov_wedge_color"); AddSetting(GUIST_CStrW, "tooltip"); AddSetting(GUIST_CStr, "tooltip_style"); + AddSetting(GUIST_float, "ping_x"); + AddSetting(GUIST_float, "ping_z"); + AddSetting(GUIST_bool, "ping"); m_Clicking = false; m_MouseHovering = false; @@ -168,11 +171,16 @@ m_MouseHovering = false; break; case GUIM_MOUSE_RELEASE_RIGHT: - CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1); + CMiniMap::FireWorldClickEvent("mouserightrelease"); break; case GUIM_MOUSE_DBLCLICK_RIGHT: - CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2); + CMiniMap::FireWorldClickEvent("mouserightdoubleclick"); break; + case GUIM_MOUSE_RELEASE_MIDDLE: + CMiniMap::FireWorldClickEvent("mousemiddlerelease"); + break; + case GUIM_MOUSE_DBLCLICK_MIDDLE: + CMiniMap::FireWorldClickEvent("mousemiddledoubleclick"); case GUIM_MOUSE_MOTION: if (m_MouseHovering && m_Clicking) SetCameraPos(); @@ -182,6 +190,24 @@ Message.Skip(); break; + //TODO: is there a better way to call the ping function of the minimap from js? + case GUIM_SETTINGS_UPDATED: + if (Message.value == "ping") + { + bool doPing = false; + GUI::GetSetting(this, "ping", doPing); + if (doPing) + { + GUI::SetSetting(this, "ping", false); + float pingX; + GUI::GetSetting(this, "ping_x", pingX); + float pingZ; + GUI::GetSetting(this, "ping_z", pingZ); + CPos pos = GetMapCoordinates(pingX, pingZ); + m_MapPing = pos; + } + } + break; default: break; } @@ -218,6 +244,14 @@ z = TERRAIN_TILE_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(py-0.5) + sin(angle)*(px-0.5)) + 0.5); } +CPos CMiniMap::GetMapCoordinates(float x, float z) +{ + const float width = m_CachedActualSize.GetWidth(); + const float height = m_CachedActualSize.GetHeight(); + const float invTileMapSize = 1.0f / float(TERRAIN_TILE_SIZE * m_MapSize); + return CPos(width * x * invTileMapSize, height * z * invTileMapSize); +} + void CMiniMap::SetCameraPos() { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); @@ -234,7 +268,7 @@ return -atan2(cameraIn.X, cameraIn.Z); } -void CMiniMap::FireWorldClickEvent(int UNUSED(button), int UNUSED(clicks)) +void CMiniMap::FireWorldClickEvent(const CStr& eventType) { JSContext* cx = g_GUI->GetActiveGUI()->GetScriptInterface()->GetContext(); JSAutoRequest rq(cx); @@ -242,11 +276,15 @@ float x, z; GetMouseWorldCoordinates(x, z); - JS::RootedValue coords(cx); - g_GUI->GetActiveGUI()->GetScriptInterface()->Eval("({})", &coords); - g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(coords, "x", x, false); - g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(coords, "z", z, false); - ScriptEvent("worldclick", coords); + JS::RootedValue target(cx); + g_GUI->GetActiveGUI()->GetScriptInterface()->Eval("({})", &target); + g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(target, "x", x, false); + g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(target, "z", z, false); + JS::RootedValue data(cx); + g_GUI->GetActiveGUI()->GetScriptInterface()->Eval("({})", &data); + g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(data, "target", target, false); + g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(data, "type", eventType, false); + ScriptEvent("worldclick", data); } // This sets up and draws the rectangle on the minimap @@ -258,7 +296,6 @@ float h = g_Renderer.GetWaterManager()->m_WaterHeight; const float width = m_CachedActualSize.GetWidth(); const float height = m_CachedActualSize.GetHeight(); - const float invTileMapSize = 1.0f / float(TERRAIN_TILE_SIZE * m_MapSize); CVector3D hitPt[4]; hitPt[0] = m_Camera->GetWorldCoordinates(0, g_Renderer.GetHeight(), h); @@ -266,19 +303,17 @@ hitPt[2] = m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), 0, h); hitPt[3] = m_Camera->GetWorldCoordinates(0, 0, h); - float ViewRect[4][2]; + CPos ViewRect[4]; for (int i = 0; i < 4; ++i) - { // convert to minimap space - ViewRect[i][0] = (width * hitPt[i].X * invTileMapSize); - ViewRect[i][1] = (height * hitPt[i].Z * invTileMapSize); - } + ViewRect[i] = GetMapCoordinates(hitPt[i].X, hitPt[i].Z); + float viewVerts[] = { - ViewRect[0][0], -ViewRect[0][1], - ViewRect[1][0], -ViewRect[1][1], - ViewRect[2][0], -ViewRect[2][1], - ViewRect[3][0], -ViewRect[3][1] + ViewRect[0].x, -ViewRect[0].y, + ViewRect[1].x, -ViewRect[1].y, + ViewRect[2].x, -ViewRect[2].y, + ViewRect[3].x, -ViewRect[3].y }; // Enable Scissoring to restrict the rectangle to only the minimap. @@ -310,6 +345,46 @@ glDisable(GL_SCISSOR_TEST); } +// TODO: Hacky and ugly copypasta +void CMiniMap::DrawPing(CMatrix3D transform) +{ + const float width = m_CachedActualSize.GetWidth(); + const float height = m_CachedActualSize.GetHeight(); +float viewVerts[] = { + m_MapPing.x + 3, -m_MapPing.y-3, + m_MapPing.x - 3, -m_MapPing.y-3, + m_MapPing.x - 3, -m_MapPing.y+3, + m_MapPing.x + 3, -m_MapPing.y+3 + }; + // Enable Scissoring to restrict the rectangle to only the minimap. + glScissor( + m_CachedActualSize.left * g_GuiScale, + g_Renderer.GetHeight() - m_CachedActualSize.bottom * g_GuiScale, + width * g_GuiScale, + height * g_GuiScale); + glEnable(GL_SCISSOR_TEST); + glLineWidth(2.0f); + + CShaderDefines lineDefines; + lineDefines.Add(str_MINIMAP_LINE, str_1); + CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), lineDefines); + tech->BeginPass(); + CShaderProgramPtr shader = tech->GetShader(); + shader->Uniform(str_transform, transform); + shader->Uniform(str_color, 1.0f, 0.3f, 1.0f, 1.0f); + + shader->VertexPointer(2, GL_FLOAT, 0, viewVerts); + shader->AssertPointersBound(); + + if (!g_Renderer.m_SkipSubmit) + glDrawArrays(GL_LINE_LOOP, 0, 4); + + tech->EndPass(); + + glLineWidth(1.0f); + glDisable(GL_SCISSOR_TEST); +} + struct MinimapUnitVertex { u8 r, g, b, a; @@ -598,6 +673,8 @@ tech->EndPass(); DrawViewRect(unitMatrix); + if (m_MapPing.x != 0.0 || m_MapPing.y != 0.0) + DrawPing(unitMatrix); PROFILE_END("minimap units");