function widget:GetInfo() return { name = "Hybrid Overhead camera", --v4 desc = "Hybrid Overhead camera mode", author = "Regret", date = "August 19, 2009", --last change August 28,2009 license = "GNU GPL, v2 or later", layer = 1000, enabled = true, handler = true, } end local maxpx,maxpz = Game.mapX*512,Game.mapY*512 --camera movement limits local maxrx = 89 /180*math.pi --max angle local minpx = -89 /180*math.pi --min angle local cameradistance = 2000 --start zoom distance local changesmoothness = 0.05 local mindistance = 100 local maxdistance = 20000 local zoom_to_cursor = true local draw_J_movement_crosshair = true local center_cursor_on_J_movement = true local use_order_cursor_for_J_movement = false local center_map_on_zoomout = false local use_midclick_to_rotate = false local allow_rotation_with_keys = false local midclickmaxtime = 0.5 --how long until a click gets dropped after mousepress, in seconds local sin = math.sin local cos = math.cos local sqrt = math.sqrt local min = math.min local max = math.max local clock = os.clock local sGetCameraState = Spring.GetCameraState local sSendCommands = Spring.SendCommands local sSetCameraState = Spring.SetCameraState local sGetConfigInt = Spring.GetConfigInt local sIsAboveMiniMap = Spring.IsAboveMiniMap local sGetGroundHeight = Spring.GetGroundHeight local sGetModKeyState = Spring.GetModKeyState local sSetMouseCursor = Spring.SetMouseCursor local sTraceScreenRay = Spring.TraceScreenRay --warning: mousey is vsy-mousey in this widget, i.e. 0,0 top left local vsx,vsy = widgetHandler:GetViewSizes() if (vsx == 1) then --hax for windowed mode vsx,vsy = Spring.GetWindowGeometry() end function widget:ViewResize(viewSizeX, viewSizeY) vsx,vsy = widgetHandler:GetViewSizes() end local _sWarpMouse = Spring.WarpMouse local sWarpMouse = function(x,y) _sWarpMouse(x,vsy-y) end local _sGetMouseState = Spring.GetMouseState local sGetMouseState = function() local x,y = _sGetMouseState() return x,vsy-y end local track = false local originalcamera = false local campx,campy,campz = 0,0,0 --camera pointed coords local lastcx,lastcy,lastcz = 0,0,0 local warponce = true local unwarpedx,unwarpedy = nil,nil local edgemove = false local rotateactive = false local smoothscrollenabled = false local updatecounter = 0 local midclicktime = 0 local function forceFreeCam() if (camera.name ~= "free") then --fixme: add check for fps mode sSendCommands("viewfree") camera = sGetCameraState() camera.gndOffset = 0 camera.gravity = 0 camera.slide = 0 camera.scrollSpeed = 0 camera.tiltSpeed = 0 camera.velTime = 0 camera.avelTime = 0 camera.autoTilt = 0 camera.goForward = -1 camera.invertAlt = -1 camera.gndLock = -1 sSetCameraState(camera,0) end end local function warpMouse() local dx = (mx-unwarpedx) local dz = (my-unwarpedy) sWarpMouse(unwarpedx,unwarpedy) mx,my = sGetMouseState() return dx,dz end local function adjustCam(czoomx,czoomz) lastcy = sin(camera.rx)*cameradistance local grounddistance = sqrt(cameradistance*cameradistance-lastcy*lastcy) lastcx = (grounddistance*sin(camera.ry)) lastcz = (grounddistance*cos(camera.ry)) if (czoomx) then campx = campx + czoomx campz = campz + czoomz end camera.px = campx - lastcx camera.pz = campz - lastcz camera.py = campy + lastcy end local function rotateCamera(conditiononly) local alt,ctrl,meta,shift = sGetModKeyState() local condition = meta if (use_midclick_to_rotate) then condition = ((not rotateactive) and meta) or rotateactive end if (conditiononly) then if (condition) then return true else return end else if (not rotateCamera(true)) then return end end local speedfactor = -1/250 if (shift) then speedfactor = speedfactor * 3 end local dx,dz = 0,0 if ((not rotateactive) and allow_rotation_with_keys and (edgemoveup or edgemovedown or edgemoveright or edgemoveleft)) then --keyboard local spd = 750 if (edgemoveleft) then dx = -spd*speedfactor elseif (edgemoveright) then dx = spd*speedfactor end if (edgemoveup) then dz = -spd*speedfactor elseif (edgemovedown) then dz = spd*speedfactor end else if (warponce) then mx,my = sGetMouseState() if (unwarpedx == nil) then unwarpedx,unwarpedy = mx,my end warponce = nil end dx,dz = warpMouse() end dx = dx*speedfactor dz = dz*speedfactor camera.ry = camera.ry + dx camera.rx = camera.rx + dz camera.rx = min(max(camera.rx,minpx),maxrx) adjustCam() end local function trackCamera(conditiononly) if (conditiononly) then if (track and ((lastmx~=mx) or (lastmy~=my))) then return true else return end else if (not trackCamera(true)) then return end end local speedfactor = cameradistance/250 if (shift) then speedfactor = speedfactor * 3 end local dx,dz = 0,0 if (warponce) then sWarpMouse(vsx*0.5,vsy*0.5) mx,my = sGetMouseState() unwarpedx,unwarpedy = mx,my warponce = nil end if (center_cursor_on_J_movement) then local pmx,pmy = mx,my sWarpMouse(vsx*0.5,vsy*0.5) mx,my = sGetMouseState() unwarpedx,unwarpedy = mx,my dx = pmx-mx dz = pmy-my else dx,dz = warpMouse() end dx = dx*speedfactor dz = dz*speedfactor local c = cos(camera.ry) local s = sin(camera.ry) campx = campx +dx*c +dz*s campz = campz -dx*s +dz*c end local function edgemoveCamera(conditiononly) local et = 1 --edgemove treshhold if (conditiononly) then if ((not (allow_rotation_with_keys and rotateCamera(true))) and((edgemoveup or edgemovedown or edgemoveright or edgemoveleft) or ((edgemove==1) and ((mx <= et) or (mx >= vsx-et) or (my <= et) or (my >= vsy-et))))) then return true else return end else if (not edgemoveCamera(true)) then return end end local dx,dz = 0,0 local speedfactor = cameradistance/250 local alt,ctrl,meta,shift = sGetModKeyState() if (shift) then speedfactor = speedfactor * 3 end if (edgemoveup or edgemovedown or edgemoveright or edgemoveleft) then --keyboard if (edgemoveleft) then dx = -keymovespeed*speedfactor elseif (edgemoveright) then dx = keymovespeed*speedfactor end if (edgemoveup) then dz = -keymovespeed*speedfactor elseif (edgemovedown) then dz = keymovespeed*speedfactor end else if (mx <= et) then --mouse dx = -edgemovespeed*speedfactor elseif (mx >= vsx-et) then dx = edgemovespeed*speedfactor end if (my <= et) then dz = -edgemovespeed*speedfactor elseif (my >= vsy-et) then dz = edgemovespeed*speedfactor end end local c = cos(camera.ry) local s = sin(camera.ry) campx = campx +dx*c +dz*s campz = campz -dx*s +dz*c end function widget:Update() --for smoothcamera compatibility if (updatecounter == 200) then updatecounter = 0 smoothscrollenabled = false for i,w in ipairs(widgetHandler.widgets) do if (w:GetInfo().name == "SmoothScroll") then smoothscrollenabled = true end end end updatecounter=updatecounter+1 ---------- forceFreeCam() mx,my = sGetMouseState() if ((not use_order_cursor_for_J_movement) and center_cursor_on_J_movement and track) then sSetMouseCursor("none") end local process = rotateCamera(true) or trackCamera(true) or edgemoveCamera(true) --for F3 (playerpoint) compatibility local tempcamera = sGetCameraState() if (lastrealcampx == nil) then lastrealcampx = tempcamera.px lastrealcampz = tempcamera.pz end if (not lastprocess and (adjustingcamera or (lastrealcampx ~= tempcamera.px) or (lastrealcampz ~= tempcamera.pz))) then local _,coords = sTraceScreenRay(vsx*0.5,vsy*0.5,true) if (coords) then campx = coords[1] campz = coords[3] adjustCam() if (not adjustingcamera) then adjustingcamera = true else if ((lasttracex == campx) and (lasttracez == campz)) then adjustingcamera = nil end end if (adjustingcamera) then lasttracex = campx lasttracez = campz else lasttracex = nil lasttracez = nil end end else adjustingcamera = nil end ---------- if (process) then rotateCamera() trackCamera() edgemoveCamera() campx = min(max(campx,0),maxpx) campz = min(max(campz,0),maxpz) campy = sGetGroundHeight(campx,campz) camera.px = campx - lastcx camera.pz = campz - lastcz camera.py = campy + lastcy sSetCameraState(camera,changesmoothness) else unwarpedx,unwarpedy = nil,nil warponce = true end lastmx,lastmy = mx,my --for F3 (playerpoint) compatibility lastprocess = process if (process) then lastrealcampx = camera.px lastrealcampz = camera.pz else lastrealcampx = tempcamera.px lastrealcampz = tempcamera.pz end ---------- end function widget:MouseWheel(up,value) lastprocess = true --for F3 (playerpoint) compatibility local alt,ctrl,meta,shift = sGetModKeyState() local zoomdirection = (zoomspeed > 0) if (alt) then if (up == zoomdirection) then cameradistance = mindistance*4 --zoom in a lot else cameradistance = maxdistance*0.75 --zoom out a lot camera.rx = minpx camera.ry = math.pi end adjustCam() sSetCameraState(camera,changesmoothness) return end local change = zoomspeed*value*cameradistance/250 if (shift) then -- shift accelerates zoom speed 3 times change = change * 3 end cameradistance = max(min(cameradistance+change,mindistance),maxdistance) local zoominscale = 1/4 local zoomoutscale = 1/64 if (up == zoomdirection) then if (center_map_on_zoomout) then adjustCam((Game.mapSizeX/2-campx)*zoomoutscale,(Game.mapSizeZ/2-campz)*zoomoutscale) else adjustCam() end else mx,my = sGetMouseState() local _, coords = sTraceScreenRay(mx,vsy-my,true) if (coords and zoom_to_cursor) then adjustCam((coords[1]-campx)*zoominscale,(coords[3]-campz)*zoominscale) else adjustCam() end end sSetCameraState(camera,changesmoothness) end function widget:MousePress(mx,my,mb) if (mb == 2) then --mid mouse button midclicktime = clock() return true end return false end function widget:MouseRelease(_,_,mb) if ((clock()-midclicktime) > midclickmaxtime) then return false end if (mb ~= 2) then --mid mouse button return false end if (sIsAboveMiniMap(mx,my)) then return false end if (use_midclick_to_rotate) then rotateactive = not rotateactive return true elseif (smoothscrollenabled) then --for smoothcamera widget compatibility return false else track = not track if (track) then if (center_cursor_on_J_movement) then sWarpMouse(vsx*0.5,vsy*0.5) mx,my = sGetMouseState() else mx,my = sGetMouseState() unwarpedx,unwarpedy = mx,my end sSendCommands("trackoff") end return true end end function widget:KeyPress(key,isRepeat) if (track) then if (key == 27) then --ESC track = false return true end end if (key == 273) then --uparrow edgemoveup = true elseif (key == 274) then --downarrow edgemovedown = true elseif (key == 275) then --rightarrow edgemoveright = true elseif (key == 276) then --leftarrow edgemoveleft = true elseif (key == 9) then --TAB track = true end end function widget:KeyRelease(key) if (key == 273) then --uparrow edgemoveup = nil elseif (key == 274) then --downarrow edgemovedown = nil elseif (key == 275) then --rightarrow edgemoveright = nil elseif (key == 276) then --leftarrow edgemoveleft = nil end if (key == 9) then --TAB track = false end end local glColor = gl.Color local glRect = gl.Rect local glResetState = gl.ResetState local glResetMatrices = gl.ResetMatrices function widget:DrawScreen() if (draw_J_movement_crosshair and center_cursor_on_J_movement and track) then glResetState() glResetMatrices() glColor(1,1,1,0.5) glRect(vsx*0.5,vsy*0.5-10,vsx*0.5+1,vsy*0.5+11) -- | glRect(vsx*0.5-10,vsy*0.5,vsx*0.5+11,vsy*0.5+1) -- _ glResetState() glResetMatrices() end end function widget:Initialize() cameradistance = -cameradistance mindistance = -mindistance maxdistance = -maxdistance mx,my = sGetMouseState() sSendCommands("trackoff") originalcamera = sGetCameraState() camera = sGetCameraState() forceFreeCam() campx = originalcamera.px campz = originalcamera.pz campy = sGetGroundHeight(campx,campz) adjustCam() sSetCameraState(camera,0) zoomspeed = sGetConfigInt("ScrollWheelSpeed",0) edgemovespeed = sGetConfigInt("OverheadScrollSpeed",10)*0.75 keymovespeed = edgemovespeed if (sGetConfigInt("Fullscreen",1) == 1) then edgemove = sGetConfigInt("FullscreenEdgeMove",1) else edgemove = sGetConfigInt("WindowedEdgeMove",1) end end function widget:Shutdown() originalcamera.px = campx originalcamera.py = camera.py originalcamera.pz = campz sSetCameraState(originalcamera,0) end