Ticket #1492: hotkeys_total.2.patch

File hotkeys_total.2.patch, 35.3 KB (added by picobyte, 12 years ago)
  • binaries/data/config/default.cfg

    diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg
    index 7a7ff2e..6d9bfaa 100644
    a b hotkey.pause = Pause ; Pause/unpause game  
    119119hotkey.screenshot = F2                      ; Take PNG screenshot
    120120hotkey.bigscreenshot = "Shift+F2"           ; Take large BMP screenshot
    121121hotkey.togglefullscreen = "Alt+Return"      ; Toggle fullscreen/windowed mode
    122 hotkey.screenshot.watermark = "K"           ; Toggle product/company watermark for official screenshots
     122hotkey.screenshot.watermark = "R"           ; Toggle product/company watermark for official screenshots
    123123hotkey.wireframe = "Alt+W"                  ; Toggle wireframe mode
    124124hotkey.silhouettes = "Alt+S"                ; Toggle unit silhouettes
    125125
    126126; > CAMERA SETTINGS
    127 hotkey.camera.reset = "H"                                 ; Reset camera rotation to default.
     127hotkey.camera.reset = "X"                                 ; Reset camera rotation to default.
    128128hotkey.camera.follow = "F"                                ; Follow the first unit in the selection
    129129hotkey.camera.zoom.in = Plus, Equals, NumPlus             ; Zoom camera in (continuous control)
    130130hotkey.camera.zoom.out = Minus, NumMinus                  ; Zoom camera out (continuous control)
    hotkey.selection.add = Shift ; Add units to selection  
    155155hotkey.selection.milonly = Alt              ; Add only military units to selection
    156156hotkey.selection.remove = Ctrl              ; Remove units from selection
    157157hotkey.selection.idleworker = Period        ; Select next idle worker
     158hotkey.selection.structure = Y              ; Select structure
     159hotkey.selection.civilcenter = U            ; Select towncenter
     160hotkey.selection.dropsiteres = I            ; Select dropsite stone/wood/metal
     161hotkey.selection.dropsitefood = O           ; Select dropsite food
     162hotkey.selection.market = P                 ; Select market
    158163hotkey.selection.idlewarrior = Comma        ; Select next idle warrior
    159164hotkey.selection.offscreen = Alt            ; Include offscreen units in selection
    160165hotkey.selection.group.select.0 = 0
    hotkey.selection.group.select.9 = 9  
    188193hotkey.selection.group.save.9 = "Ctrl+9"
    189194hotkey.selection.group.add.9 = "Shift+9"
    190195
     196hotkey.selection.group.action.0 = G
     197hotkey.selection.group.action.1 = H
     198hotkey.selection.group.action.2 = J
     199hotkey.selection.group.action.3 = K
     200hotkey.selection.group.action.4 = L
     201hotkey.selection.group.action.5 = Semicolon
     202hotkey.selection.group.action.6 = SingleQuote
     203hotkey.selection.group.action.7 = "Ctrl+G"
     204hotkey.selection.group.action.8 = "Ctrl+H"
     205hotkey.selection.group.action.9 = "Ctrl+J"
     206hotkey.selection.group.action.10 = "Ctrl+K"
     207hotkey.selection.group.action.11 = "Ctrl+L"
     208hotkey.selection.group.action.12 = "Ctrl+Semicolon"
     209hotkey.selection.group.action.13 = "Ctrl+SingleQuote"
     210hotkey.selection.group.reset.0 = ForwardSlash
     211hotkey.selection.group.stance.0 = V
     212hotkey.selection.group.formation.0 = B
     213hotkey.selection.group.garrison.0 = N
     214hotkey.selection.group.ungarrison.0 = M
     215
    191216; > SESSION CONTROLS
    192217hotkey.session.kill = Delete                ; Destroy selected units
    193218hotkey.session.garrison = Ctrl              ; Modifier to garrison when clicking on building
  • binaries/data/mods/public/gui/session/input.js

    diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js
    index ff39b74..a2cdc0a 100644
    a b const SDLK_LALT = 308;  
    1414
    1515const ACTION_NONE = 0;
    1616const ACTION_GARRISON = 1;
    17 const ACTION_REPAIR = 2;
     17const ACTION_BUILD = 2;
     18const ACTION_TRAIN = 4;
     19const ACTION_UNGARRISON = 8;
     20const ACTION_FORMATION = 16;
     21const ACTION_STANCE = 32;
     22const ACTION_BARTER = 64;
     23const ACTION_TRADER =128;
    1824var preSelectedAction = ACTION_NONE;
    1925
     26var followQueue = [];
     27
    2028const INPUT_NORMAL = 0;
    2129const INPUT_SELECTING = 1;
    2230const INPUT_BANDBOXING = 2;
    const INPUT_BATCHTRAINING = 6;  
    2735const INPUT_PRESELECTEDACTION = 7;
    2836const INPUT_BUILDING_WALL_CLICK = 8;
    2937const INPUT_BUILDING_WALL_PATHING = 9;
     38const INPUT_ACTION_STATE = 10;
    3039
    3140var inputState = INPUT_NORMAL;
    3241var placementSupport = new PlacementSupport();
    function updateBuildingPlacementPreview()  
    137146    return false;
    138147}
    139148
     149function initBuildingPlacementView()
     150{
     151    if (placementSupport.mode === "wall")
     152    {
     153        // Including only the on-screen towers in the next snap candidate list is sufficient here, since the user is
     154        // still selecting a starting point (which must necessarily be on-screen). (The update of the snap entities
     155        // itself happens in the call to updateBuildingPlacementPreview below).
     156        placementSupport.wallSnapEntitiesIncludeOffscreen = false;
     157    }
     158    else
     159    {
     160        var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
     161            "template": placementSupport.template,
     162            "x": placementSupport.position.x,
     163            "z": placementSupport.position.z,
     164        });
     165        if (snapData)
     166        {
     167            placementSupport.angle = snapData.angle;
     168            placementSupport.position.x = snapData.x;
     169            placementSupport.position.z = snapData.z;
     170        }
     171    }
     172    updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
     173}
     174
    140175function findGatherType(gatherer, supply)
    141176{
    142177    if (!gatherer || !supply)
    function findGatherType(gatherer, supply)  
    148183    return undefined;
    149184}
    150185
     186function resetPreselectedAction()
     187{
     188    preSelectedAction = ACTION_NONE;
     189    inputState = INPUT_NORMAL;
     190    placementSupport.Reset(); // or a hotkey may leave a building snapshot
     191    updateCursorAndTooltip();
     192
     193}
     194
    151195function getActionInfo(action, target)
    152196{
    153197    var selection = g_Selection.toList();
    function determineAction(x, y, fromMinimap)  
    423467    {
    424468        target = targets[0];
    425469    }
     470    if (Engine.HotkeyIsPressed("session.garrison"))
     471        preSelectedAction = ACTION_GARRISON;
    426472
    427     if (preSelectedAction != ACTION_NONE)
     473    if (preSelectedAction & (ACTION_GARRISON|ACTION_BUILD))
    428474    {
    429475        switch (preSelectedAction)
    430476        {
    function determineAction(x, y, fromMinimap)  
    434480            else
    435481                return  {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
    436482            break;
    437         case ACTION_REPAIR:
     483        case ACTION_BUILD:
    438484            if (getActionInfo("repair", target).possible)
    439485                return {"type": "repair", "cursor": "action-repair", "target": target};
    440486            else
    function determineAction(x, y, fromMinimap)  
    442488            break;
    443489        }
    444490    }
    445     else if (Engine.HotkeyIsPressed("session.garrison"))
    446     {
    447         if (getActionInfo("garrison", target).possible)
    448             return {"type": "garrison", "cursor": "action-garrison", "target": target};
    449         else
    450             return  {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
    451     }
    452491    else
    453492    {
    454493        var actionInfo = undefined;
    function handleInputBeforeGui(ev, hoveredObject)  
    623662        mouseY = ev.y;
    624663        break;
    625664    }
    626 
     665    if (followQueue.length)
     666    {
     667        var gents = followQueue;
     668        var state = GetEntityState(gents[0]);
     669        if (state.visibility == "hidden")
     670            return true;
     671        Engine.CameraFollow(0);
     672        Engine.CameraFollow(gents[0]);
     673        g_Selection.addList(gents);
     674        resetPreselectedAction();
     675        followQueue = []; // for next time.
     676    }
    627677    // Remember whether the mouse is over a GUI object or not
    628678    mouseIsOverObject = (hoveredObject != null);
    629679
    function handleInputBeforeGui(ev, hoveredObject)  
    751801                    if (queued)
    752802                        inputState = INPUT_BUILDING_PLACEMENT;
    753803                    else
    754                         inputState = INPUT_NORMAL;
     804                        resetPreselectedAction();
    755805                }
    756806                else
    757807                {
    function handleInputBeforeGui(ev, hoveredObject)  
    765815            if (ev.button == SDL_BUTTON_RIGHT)
    766816            {
    767817                // Cancel building
    768                 placementSupport.Reset();
    769                 inputState = INPUT_NORMAL;
     818                resetPreselectedAction();
    770819                return true;
    771820            }
    772821            break;
    function handleInputAfterGui(ev)  
    9751024    switch (inputState)
    9761025    {
    9771026    case INPUT_NORMAL:
     1027    case INPUT_ACTION_STATE:
    9781028        switch (ev.type)
    9791029        {
    9801030        case "mousemotion":
    function handleInputAfterGui(ev)  
    10041054            break;
    10051055
    10061056        case "hotkeydown":
    1007                 if (ev.hotkey.indexOf("selection.group.") == 0)
    1008                 {
    1009                     var now = new Date();
    1010                     if ((now.getTime() - doublePressTimer < doublePressTime) && (ev.hotkey == prevHotkey))
    1011                     {
    1012                         if (ev.hotkey.indexOf("selection.group.select.") == 0)
    1013                         {
    1014                             var sptr = ev.hotkey.split(".");
    1015                             performGroup("snap", sptr[3]);
    1016                         }
    1017                     }
    1018                     else
    1019                     {
    1020                         var sptr = ev.hotkey.split(".");
    1021                         performGroup(sptr[2], sptr[3]);
    1022 
    1023                         doublePressTimer = now.getTime();
    1024                         prevHotkey = ev.hotkey;
    1025                     }
    1026                 }
    1027                 break;
     1057            handleHotkey(ev);
     1058            break;
    10281059        }
    10291060        break;
    10301061       
    function handleInputAfterGui(ev)  
    10351066            if (ev.button == SDL_BUTTON_LEFT && preSelectedAction != ACTION_NONE)
    10361067            {
    10371068                var action = determineAction(ev.x, ev.y);
     1069                resetPreselectedAction();
    10381070                if (!action)
    10391071                    break;
    1040                 preSelectedAction = ACTION_NONE;
    1041                 inputState = INPUT_NORMAL;
    10421072                return doAction(action, ev);
    10431073            }
    1044             else if (ev.button == SDL_BUTTON_RIGHT && preSelectedAction != ACTION_NONE)
     1074            else if (ev.button == SDL_BUTTON_RIGHT)
    10451075            {
    1046                 preSelectedAction = ACTION_NONE;
    1047                 inputState = INPUT_NORMAL;
     1076                resetPreselectedAction();
    10481077                break;
    10491078            }
    1050             // else
     1079        case "hotkeydown":
     1080            handleHotkey(ev);
     1081            break;
    10511082        default:
    10521083            // Slight hack: If selection is empty, reset the input state
    10531084            if (g_Selection.toList().length == 0)
    10541085            {
    1055                 preSelectedAction = ACTION_NONE;
    1056                 inputState = INPUT_NORMAL;
     1086                resetPreselectedAction();
    10571087                break;
    10581088            }
    10591089        }
    function handleInputAfterGui(ev)  
    10841114                if (!ents.length)
    10851115                {
    10861116                    g_Selection.reset();
    1087                     resetIdleUnit();
    1088                     inputState = INPUT_NORMAL;
     1117                    resetEntitySearch();
     1118                    resetPreselectedAction();
    10891119                    return true;
    10901120                }
    1091 
    10921121                var selectedEntity = ents[0];
    1093                 var now = new Date();
    10941122
    10951123                // If camera following and we select different unit, stop
    10961124                if (Engine.GetFollowedEntity() != selectedEntity)
    10971125                {
    10981126                    Engine.CameraFollow(0);
    10991127                }
    1100 
    1101                 if ((now.getTime() - doubleClickTimer < doubleClickTime) && (selectedEntity == prevClickedEntity))
    1102                 {
    1103                     // Double click or triple click has occurred
    1104                     var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
    1105                     var matchRank = true;
    1106                     var templateToMatch;
    1107 
    1108                     // Check for double click or triple click
    1109                     if (!doubleClicked)
    1110                     {
    1111                         // If double click hasn't already occurred, this is a double click.
    1112                         // Select similar units regardless of rank
    1113                         templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).identity.selectionGroupName;
    1114                         if (templateToMatch)
    1115                         {
    1116                             matchRank = false;
    1117                         }
    1118                         else
    1119                         {   // No selection group name defined, so fall back to exact match
    1120                             templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
    1121                         }
    1122 
    1123                         doubleClicked = true;
    1124                         // Reset the timer so the user has an extra period 'doubleClickTimer' to do a triple-click
    1125                         doubleClickTimer = now.getTime();
    1126                     }
    1127                     else
    1128                     {
    1129                         // Double click has already occurred, so this is a triple click.
    1130                         // Select units matching exact template name (same rank)
    1131                         templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
    1132                     }
    1133 
    1134                     // TODO: Should we handle "control all units" here as well?
    1135                     ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
    1136                 }
    1137                 else
    1138                 {
    1139                     // It's single click right now but it may become double or triple click
    1140                     doubleClicked = false;
    1141                     doubleClickTimer = now.getTime();
    1142                     prevClickedEntity = selectedEntity;
    1143 
    1144                     // We only want to include the first picked unit in the selection
    1145                     ents = [ents[0]];
    1146                 }
    1147 
    1148                 // Update the list of selected units
    1149                 if (Engine.HotkeyIsPressed("selection.add"))
    1150                 {
    1151                     g_Selection.addList(ents);
    1152                 }
    1153                 else if (Engine.HotkeyIsPressed("selection.remove"))
    1154                 {
    1155                     g_Selection.removeList(ents);
    1156                 }
    1157                 else
    1158                 {
    1159                     g_Selection.reset();
    1160                     g_Selection.addList(ents);
    1161                 }
    1162 
    1163                 inputState = INPUT_NORMAL;
     1128                ClickSelected(ents, (selectedEntity == prevClickedEntity));
    11641129                return true;
    11651130            }
    11661131            break;
    function handleInputAfterGui(ev)  
    11711136        switch (ev.type)
    11721137        {
    11731138        case "mousemotion":
    1174            
    11751139            placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
    1176            
    1177             if (placementSupport.mode === "wall")
    1178             {
    1179                 // Including only the on-screen towers in the next snap candidate list is sufficient here, since the user is
    1180                 // still selecting a starting point (which must necessarily be on-screen). (The update of the snap entities
    1181                 // itself happens in the call to updateBuildingPlacementPreview below).
    1182                 placementSupport.wallSnapEntitiesIncludeOffscreen = false;
    1183             }
    1184             else
    1185             {
    1186                 var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
    1187                     "template": placementSupport.template,
    1188                     "x": placementSupport.position.x,
    1189                     "z": placementSupport.position.z,
    1190                 });
    1191                 if (snapData)
    1192                 {
    1193                     placementSupport.angle = snapData.angle;
    1194                     placementSupport.position.x = snapData.x;
    1195                     placementSupport.position.z = snapData.z;
    1196                 }
    1197             }
    1198 
    1199             updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
     1140            initBuildingPlacementView();
    12001141            return false; // continue processing mouse motion
    12011142
    12021143        case "mousebuttondown":
    function handleInputAfterGui(ev)  
    12131154                else
    12141155                {
    12151156                    placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
    1216                     dragStart = [ ev.x, ev.y ];
    1217                     inputState = INPUT_BUILDING_CLICK;
     1157                    dragStart = [ ev.x, ev.y ];
     1158                    inputState = INPUT_BUILDING_CLICK;
    12181159                }
    12191160                return true;
    12201161            }
    function handleInputAfterGui(ev)  
    12221163            {
    12231164                // Cancel building
    12241165                placementSupport.Reset();
    1225                 inputState = INPUT_NORMAL;
     1166                resetPreselectedAction();
    12261167                return true;
    12271168            }
    12281169            break;
    function handleInputAfterGui(ev)  
    12411182                placementSupport.angle -= rotation_step;
    12421183                updateBuildingPlacementPreview();
    12431184                break;
     1185            default:
     1186                handleHotkey(ev); // to switch buildings
    12441187            }
    12451188            break;
    12461189
    function handleInputAfterGui(ev)  
    12501193    return false;
    12511194}
    12521195
     1196function ClickSelected(ents, is_same)
     1197{
     1198    var selectedEntity = ents[0];
     1199    var now = new Date();
     1200
     1201    if ((now.getTime() - doubleClickTimer < doubleClickTime) && is_same)
     1202    {
     1203        // Double click or triple click has occurred
     1204        var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
     1205        var matchRank = true;
     1206        var templateToMatch;
     1207
     1208        // Check for double click or triple click
     1209        if (!doubleClicked)
     1210        {
     1211            // If double click hasn't already occurred, this is a double click.
     1212            // Select similar units regardless of rank
     1213            templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).identity.selectionGroupName;
     1214            if (templateToMatch)
     1215            {
     1216                matchRank = false;
     1217            }
     1218            else
     1219            {   // No selection group name defined, so fall back to exact match
     1220                templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
     1221            }
     1222
     1223            doubleClicked = true;
     1224            // Reset the timer so the user has an extra period 'doubleClickTimer' to do a triple-click
     1225            doubleClickTimer = now.getTime();
     1226        }
     1227        else
     1228        {
     1229            // Double click has already occurred, so this is a triple click.
     1230            // Select units matching exact template name (same rank)
     1231            templateToMatch = Engine.GuiInterfaceCall("GetEntityState", selectedEntity).template;
     1232        }
     1233
     1234        // TODO: Should we handle "control all units" here as well?
     1235        ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
     1236    }
     1237    else
     1238    {
     1239        // It's single click right now but it may become double or triple click
     1240        doubleClicked = false;
     1241        doubleClickTimer = now.getTime();
     1242        prevClickedEntity = selectedEntity;
     1243
     1244        // We only want to include the first picked unit in the selection
     1245        ents = [ents[0]];
     1246    }
     1247
     1248    // Update the list of selected units
     1249    if (Engine.HotkeyIsPressed("selection.add"))
     1250    {
     1251        g_Selection.addList(ents);
     1252    }
     1253    else if (Engine.HotkeyIsPressed("selection.remove"))
     1254    {
     1255        g_Selection.removeList(ents);
     1256    }
     1257    else
     1258    {
     1259        g_Selection.reset();
     1260        g_Selection.addList(ents);
     1261    }
     1262    resetPreselectedAction();
     1263}
     1264
    12531265function doAction(action, ev)
    12541266{
    12551267    var selection = g_Selection.toList();
    function performCommand(entity, commandName)  
    15331545                break;
    15341546            case "repair":
    15351547                inputState = INPUT_PRESELECTEDACTION;
    1536                 preSelectedAction = ACTION_REPAIR;
     1548                preSelectedAction = ACTION_BUILD;
    15371549                break;
    15381550            case "unload-all":
    15391551                unloadAll(entity);
    function performFormation(entity, formationName)  
    15771589    }
    15781590}
    15791591
     1592function handleHotkey(ev)
     1593{
     1594    if (ev.hotkey && ev.hotkey.indexOf("selection.group.") == 0)
     1595    {
     1596        var sptr = ev.hotkey.split(".");
     1597        var now = new Date();
     1598        if ((inputState == INPUT_NORMAL) && (ev.hotkey == prevHotkey) &&
     1599                ev.hotkey.indexOf("selection.group.select.") == 0 &&
     1600                (now.getTime() - doublePressTimer < doublePressTime)) {
     1601            performGroup("snap", sptr[3]);
     1602        }
     1603        else
     1604        {
     1605            performGroup(sptr[2], sptr[3]);
     1606            doublePressTimer = now.getTime();
     1607            prevHotkey = ev.hotkey;
     1608        }
     1609    }
     1610}
     1611
    15801612// Performs the specified group
    1581 function performGroup(action, groupId)
     1613function performGroup(action, x)
    15821614{
    15831615    switch (action)
    15841616    {
    function performGroup(action, groupId)  
    15871619    case "add":
    15881620        var toSelect = [];
    15891621        g_Groups.update();
    1590         for (var ent in g_Groups.groups[groupId].ents)
     1622        for (var ent in g_Groups.groups[x].ents)
    15911623            toSelect.push(+ent);
    15921624
    15931625        if (action != "add")
    function performGroup(action, groupId)  
    15971629
    15981630        if (action == "snap" && toSelect.length)
    15991631            Engine.CameraFollow(toSelect[0]);
    1600         break;
     1632        return;
    16011633    case "save":
    16021634        var selection = g_Selection.toList();
    1603         g_Groups.groups[groupId].reset();
    1604         g_Groups.addEntities(groupId, selection);
     1635        g_Groups.groups[x].reset();
     1636        g_Groups.addEntities(x, selection);
    16051637        updateGroups();
     1638        return;
     1639    case "action":
     1640        var selection = g_Selection.toList();
     1641        var player = Engine.GetPlayerID();
     1642        if (inputState != INPUT_ACTION_STATE)
     1643            preSelectedAction = getDefaultActionForSelection(player, selection);
    16061644        break;
     1645    case "reset":
     1646        resetPreselectedAction();
     1647        return;
     1648    case "garrison":
     1649        preSelectedAction = ACTION_GARRISON;
     1650        inputState = INPUT_PRESELECTEDACTION;
     1651        return;
     1652    case "ungarrison":
     1653        preSelectedAction = ACTION_UNGARRISON;
     1654        inputState = INPUT_ACTION_STATE;
     1655        return;
     1656    case "stance":
     1657        preSelectedAction = ACTION_STANCE;
     1658        inputState = INPUT_ACTION_STATE;
     1659        return;
     1660    case "formation":
     1661        preSelectedAction = ACTION_FORMATION;
     1662        inputState = INPUT_ACTION_STATE;
     1663        return;
     1664    }
     1665    if (inputState == INPUT_ACTION_STATE)
     1666        inputState = INPUT_NORMAL;
     1667    if (preSelectedAction == ACTION_NONE)
     1668        return;
     1669
     1670    var lst = selectAction(player, selection);
     1671    if (!lst) // shouldn't happen
     1672        return;
     1673
     1674    if (preSelectedAction & ACTION_GARRISON)
     1675    {
     1676        if (++x > lst.length)
     1677            x = lst.length;
     1678    } else if (preSelectedAction != ACTION_UNGARRISON) {
     1679        if (x >= lst.length)
     1680            return;
     1681        lst = lst[x];
    16071682    }
     1683
     1684    switch (preSelectedAction)
     1685    {
     1686    case ACTION_BUILD:
     1687        var tmpl = GetTemplateData(lst);
     1688        if (!tmpl.requiredTechnology || Engine.GuiInterfaceCall("IsTechnologyResearched", tmpl.requiredTechnology)) {
     1689            // N.B. sets inputState = INPUT_BUILDING_PLACEMENT:
     1690            startBuildingPlacement(lst);
     1691            placementSupport.position = Engine.GetTerrainAtScreenPoint(mouseX, mouseY);
     1692            initBuildingPlacementView();
     1693        }
     1694        break;
     1695    case ACTION_TRAIN:
     1696        var tmpl = GetTemplateData(lst);
     1697        if (!tmpl.requiredTechnology || Engine.GuiInterfaceCall("IsTechnologyResearched", tmpl.requiredTechnology)) {
     1698            var ents = g_Selection.toList();
     1699            addTrainingToQueue(ents, lst);
     1700        }
     1701        break;
     1702    case ACTION_GARRISON: // we garison x+1 count
     1703        g_Selection.reset();
     1704        lst.splice(x, lst.length - x)
     1705        g_Selection.addList(lst);
     1706        inputState = INPUT_PRESELECTEDACTION;
     1707        break;
     1708    case ACTION_UNGARRISON:
     1709        if (lst.length > 1 || x == 0) {
     1710            if (x >= lst.length)
     1711                x = lst.length - 1;
     1712            if (lst.length != 1 && x == 0) {
     1713                // unload all from all buildings
     1714                for each (var gh in lst)
     1715                    unloadAll(gh);
     1716            } else {
     1717                if (lst.length != 1)
     1718                    x--;
     1719                unloadAll(lst[x]);
     1720            }
     1721            g_Selection.reset();
     1722        } else {
     1723            lst = lst[0];
     1724            var state = GetEntityState(lst);
     1725            var gents = state.garrisonHolder.entities;
     1726            if (x > gents.length)
     1727                x = gents.length;
     1728            while (x--)
     1729                Engine.PostNetworkCommand({"type": "unload", "entities": [gents[x]], "garrisonHolder": lst});
     1730            // we could assign a key to follow them here
     1731        }
     1732        break;
     1733    case ACTION_FORMATION:
     1734        ents = g_Selection.toList();
     1735        var formationOk = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", {
     1736            "ents": g_Selection.toList(),
     1737            "formationName": lst
     1738        });
     1739        if (formationOk)
     1740            performFormation(ents, lst);
     1741        break
     1742    case ACTION_STANCE:
     1743        ents = g_Selection.toList();
     1744        performStance(ents, lst);
     1745        break;
     1746    case ACTION_BARTER: //TODO
     1747        break;
     1748    case ACTION_TRADER: //TODO
     1749        break;
     1750    }
     1751}
     1752
     1753// This returns a list of actions. Caller still has to check Technology availability
     1754function selectAction(player, selection)
     1755{
     1756    if (preSelectedAction == ACTION_FORMATION)
     1757        return Engine.GuiInterfaceCall("GetAvailableFormations");
     1758    else if (preSelectedAction == ACTION_STANCE)
     1759        return ["violent", "aggressive", "passive", "defensive", "standground"];
     1760
     1761    var ret = [];
     1762    for each (var ent in selection)
     1763    {
     1764        var state = GetEntityState(ent);
     1765        if (!state || (state.player != player && !g_DevSettings.controlAll))
     1766            continue;
     1767
     1768        switch (preSelectedAction)
     1769        {
     1770        case ACTION_GARRISON:
     1771            if (state.buildEntities || (hasClass(state, "Unit") && !hasClass(state, "Animal") && !state.garrisonHolder)) {
     1772                inputState = INPUT_PRESELECTEDACTION;
     1773                ret.push(ent);
     1774            }
     1775            break;
     1776        case ACTION_BUILD:
     1777            if (state.buildEntities && state.buildEntities.length) {
     1778                if (inputState != INPUT_BUILDING_PLACEMENT)
     1779                    inputState = INPUT_PRESELECTEDACTION;
     1780                return state.buildEntities;
     1781            }
     1782            break;
     1783        case ACTION_TRAIN:
     1784            if (state.production && state.production.entities.length)
     1785                return state.production.entities;
     1786            break;
     1787        case ACTION_UNGARRISON:
     1788            if (state.garrisonHolder && state.garrisonHolder.entities && state.garrisonHolder.entities.length)
     1789                ret.push(ent);
     1790            break;
     1791        case ACTION_BARTER:
     1792            if (state.barterMarket)
     1793                ret.push(ent);
     1794            break;
     1795        case ACTION_TRADER:
     1796            if (state.trader)
     1797                ret.push([ent, state]);
     1798            break;
     1799        }
     1800    }
     1801    return ret;
     1802}
     1803
     1804function getDefaultActionForSelection(player, selection)
     1805{
     1806    var action;
     1807    var count = 0;
     1808    for each (var ent in selection)
     1809    {
     1810        var state = GetEntityState(ent);
     1811        if (!state || (state.player != player && !g_DevSettings.controlAll))
     1812            continue;
     1813
     1814        if (state.buildEntities && state.buildEntities.length)
     1815            return ACTION_BUILD; // by default
     1816
     1817        if (state.production && state.production.entities.length)
     1818            action |= ACTION_TRAIN;
     1819        else if (!hasClass(state, "Unit") || hasClass(state, "Animal") ||
     1820                !state.garrisonHolder) {
     1821            if (++count > 1) //only works with at least two units
     1822                action |= ACTION_FORMATION;
     1823        } else if (state.barterMarket)
     1824            action |= ACTION_BARTER;
     1825        else if (state.trader)
     1826            action |= ACTION_TRADER;
     1827    }
     1828    if (action & ACTION_TRAIN)
     1829        return ACTION_TRAIN;
     1830    else if (action & ACTION_FORMATION)
     1831        return ACTION_FORMATION;
     1832    else if (action & ACTION_BARTER) {
     1833        setupUnitBarterPanel(state);
     1834        return ACTION_BARTER;
     1835    } else if (action & ACTION_TRADER) {
     1836        setupUnitTradingPanel(state, selection);
     1837        return ACTION_TRADER;
     1838    }
     1839    return 0; //shouldn't happen
    16081840}
    16091841
    16101842// Performs the specified stance
    function setCameraFollow(entity)  
    16391871    Engine.CameraFollow(0);
    16401872}
    16411873
    1642 var lastIdleUnit = 0;
    1643 var currIdleClass = 0;
     1874var lastEntity = 0;
     1875var currEntClass = 0;
     1876
     1877function resetEntitySearch()
     1878{
     1879    lastEntity = 0;
     1880    currEntClass = 0;
     1881}
    16441882
    1645 function resetIdleUnit()
     1883function findAllVisibleEntities(classes, mode)
    16461884{
    1647     lastIdleUnit = 0;
    1648     currIdleClass = 0;
     1885    var player = Engine.GetPlayerID();
     1886    var plEnts = Engine.PickFriendlyEntitiesOnScreen(player);
     1887    // Or we could do somthing alike the below, but this is resolution dependent
     1888    //var plEnts = Engine.PickFriendlyEntitiesInRect(250, 250, 1400, 750, player);
     1889    if (!plEnts.length)
     1890        return;
     1891    var visibleEnts = [];
     1892    for each (cls in classes)
     1893    {
     1894        var data = {
     1895            entClass: cls,
     1896            searchMode: mode,
     1897            playerEnts: plEnts,
     1898        };
     1899        var entities = Engine.GuiInterfaceCall("SubsetMatchedEnts", data);
     1900        if (entities.length)
     1901            visibleEnts = visibleEnts.concat(entities);
     1902    }
     1903
     1904    if (visibleEnts.length) {
     1905        if (!getDefaultActionForSelection(player, visibleEnts).length)
     1906            resetPreselectedAction();
     1907        g_Selection.reset();
     1908        g_Selection.addList(visibleEnts);
     1909        Engine.CameraFollow(visibleEnts[0]);
     1910    }
    16491911}
    16501912
    1651 function findIdleUnit(classes)
     1913// mode 0 is to search for idle, 1 to search for any
     1914function findEntity(classes, mode)
    16521915{
     1916    var queued = Engine.HotkeyIsPressed("session.queue");
     1917    if (queued) {
     1918        findAllVisibleEntities(classes, mode)
     1919        return;
     1920    }
     1921    var player = Engine.GetPlayerID();
     1922
    16531923    // Cycle through idling classes before giving up
    16541924    for (var i = 0; i <= classes.length; ++i)
    16551925    {
    1656         var data = { prevUnit: lastIdleUnit, idleClass: classes[currIdleClass] };
    1657         var newIdleUnit = Engine.GuiInterfaceCall("FindIdleUnit", data);
    1658 
    1659         // Check if we have new valid entity
    1660         if (newIdleUnit && newIdleUnit != lastIdleUnit)
     1926        var data = { prevEntity: lastEntity, entClass: classes[currEntClass], searchMode: mode};
     1927        var newEntity = Engine.GuiInterfaceCall("FindEntity", data);
     1928        if (newEntity && newEntity != lastEntity)
    16611929        {
    1662             lastIdleUnit = newIdleUnit;
    1663             g_Selection.reset()
    1664             g_Selection.addList([lastIdleUnit]);
    1665             Engine.CameraFollow(lastIdleUnit);
    1666 
     1930            if (!selectAction(player, [newEntity]).length)
     1931                resetPreselectedAction();
     1932            else if (preSelectedAction == ACTION_BUILD)
     1933                updateBuildingPlacementPreview();
     1934            lastEntity = newEntity;
     1935            ClickSelected([newEntity], 1);
    16671936            return;
    16681937        }
    1669 
    1670         lastIdleUnit = 0;
    1671         currIdleClass = (currIdleClass + 1) % classes.length;
     1938        lastEntity = 0;
     1939        currEntClass = (currEntClass + 1) % classes.length;
    16721940    }
    1673 
    16741941    // TODO: display a message or play a sound to indicate no more idle units, or something
    16751942    // Reset for next cycle
    1676     resetIdleUnit();
     1943    resetEntitySearch();
    16771944}
    16781945
    16791946function unload(garrisonHolder, entities)
    function unload(garrisonHolder, entities)  
    16861953
    16871954function unloadAll(garrisonHolder)
    16881955{
     1956    var state = GetEntityState(garrisonHolder);
     1957    var gents = state.garrisonHolder.entities;
     1958    followQueue = followQueue.concat(gents);
    16891959    Engine.PostNetworkCommand({"type": "unload-all", "garrisonHolder": garrisonHolder});
    16901960}
  • binaries/data/mods/public/gui/session/session.xml

    diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
    index 3ff530a..a55a7bd 100644
    a b  
    8585    </object>
    8686
    8787        <!-- Find idle warrior - TODO: Potentially move this to own UI button? -->
    88     <object hotkey="selection.idlewarrior">
    89         <action on="Press">findIdleUnit(["Hero", "Champion", "CitizenSoldier", "Siege", "Warship"]);</action>
    90     </object>
     88        <object hotkey="selection.idlewarrior">
     89            <action on="Press">findEntity(["Hero", "Champion", "CitizenSoldier", "Siege", "Warship"], 1);</action>
     90        </object>
     91
     92        <!-- Select civilcenter -->
     93        <object hotkey="selection.civilcenter">
     94            <action on="Press">findEntity(["CivCentre"], 0);</action>
     95        </object>
     96        <!-- Select market -->
     97        <object hotkey="selection.market">
     98            <action on="Press">findEntity(["BarterMarket", "Market", "NavalMarket"], 0);</action>
     99        </object>
     100        <!-- Select dropsitefood -->
     101        <object hotkey="selection.dropsitefood">
     102            <action on="Press">findEntity(["CivCentre", "DropsiteFood"], 0);</action>
     103        </object>
     104        <!-- Select dropsitewood -->
     105        <object hotkey="selection.dropsiteres">
     106            <action on="Press">findEntity(["CivCentre", "DropsiteWood", "DropsiteStone", "DropsiteMetal"], 0);</action>
     107        </object>
     108        <!-- Select structure -->
     109        <object hotkey="selection.structure">
     110            <action on="Press">findEntity(["Structure"], 0);</action>
     111        </object>
    91112
    92113    <!-- ================================  ================================ -->
    93114    <!-- Developer / Debug items -->
     
    527548            >
    528549            <!-- TODO: should highlight the button if there's non-zero idle workers -->
    529550            <object size="0 0 100% 100%" type="image" sprite="idleWorker" ghost="true" />
    530             <action on="Press">findIdleUnit(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"]);</action>
     551            <action on="Press">findEntity(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"], 1);</action>
    531552            </object>
    532553        </object>
    533554        </object>
     
    586607            </repeat>
    587608            </object>
    588609        </object>
    589 
    590610        <!-- Stance Selection -->
    591611        <object name="unitStancePanel"
    592612            style="TranslucentPanel"
     
    756776        <object name="unitCommandPanel"
    757777            size="0 100%-36 100% 100%-4"
    758778            type="image"
    759             z="30"
     779            z="50"
    760780        >
    761781            <object size="0 0 100% 100%">
    762782            <repeat count="6">
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
    index 4ce8c71..6fd71d9 100644
    a b GuiInterface.prototype.PlaySound = function(player, data)  
    14261426    PlaySound(data.name, data.entity);
    14271427};
    14281428
    1429 function isIdleUnit(ent, idleClass)
     1429GuiInterface.prototype.MatchEnt = function(ent, data)
    14301430{
    1431     var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
    1432     var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
    1433    
    1434     // TODO: Do something with garrisoned idle units
    1435     return (cmpUnitAI && cmpIdentity && cmpUnitAI.IsIdle() && !cmpUnitAI.IsGarrisoned() && idleClass && cmpIdentity.HasClass(idleClass));
     1431    var ret;
     1432    var cmpId = Engine.QueryInterface(ent, IID_Identity);
     1433    if (cmpId && cmpId.HasClass(data.entClass)) {
     1434        if (data.searchMode == 0) {
     1435            ret = ent;
     1436        }  else if (data.searchMode == 1) { // Search idle villager/soldier
     1437            var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     1438            if (cmpUnitAI && cmpUnitAI.IsIdle() && !cmpUnitAI.IsGarrisoned())
     1439                ret = ent;
     1440        }
     1441    }
     1442    return ret;
     1443}
     1444
     1445GuiInterface.prototype.SubsetMatchedEnts = function(player, data)
     1446{
     1447    var ret = [];
     1448    for each (var ent in data.playerEnts)
     1449    {
     1450        var match = this.MatchEnt(ent, data);
     1451        if (match)
     1452            ret.push(match);
     1453    }
     1454    return ret;
    14361455}
    14371456
    1438 GuiInterface.prototype.FindIdleUnit = function(player, data)
     1457GuiInterface.prototype.FindEntity = function(player, data)
    14391458{
    14401459    var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    14411460    var playerEntities = rangeMan.GetEntitiesByPlayer(player);
    GuiInterface.prototype.FindIdleUnit = function(player, data)  
    14441463    // so that we cycle around in a predictable order
    14451464    for each (var ent in playerEntities)
    14461465    {
    1447         if (ent > data.prevUnit && isIdleUnit(ent, data.idleClass))
    1448             return ent;
     1466        if (ent > data.prevEntity)
     1467        {
     1468            var match = this.MatchEnt(ent, data);
     1469            if (match)
     1470                return match;
     1471        }
    14491472    }
    14501473
    1451     // No idle entities left in the class
     1474    // No entities left in the class
    14521475    return 0;
    14531476};
    14541477
    var exposedFunctions = {  
    15711594    "SetWallPlacementPreview": 1,
    15721595    "GetFoundationSnapData": 1,
    15731596    "PlaySound": 1,
    1574     "FindIdleUnit": 1,
     1597    "SubsetMatchedEnts": 1,
     1598    "FindEntity": 1,
    15751599    "GetTradingDetails": 1,
    15761600    "CanAttack": 1,
    15771601
  • libraries/spidermonkey/include-win32/js/jstracer.h

    diff --git a/libraries/spidermonkey/include-win32/js/jstracer.h b/libraries/spidermonkey/include-win32/js/jstracer.h
    index 76921cc..bd992c2 100644
    a b public:  
    106106
    107107    void add(T a) {
    108108        ensure(_len + 1);
    109         JS_ASSERT(_len <= _max);
     109        JS_ASSERT(_len < _max);
    110110        _data[_len++] = a;
    111111    }
    112112
    113113    void add(T* chunk, unsigned size) {
    114114        ensure(_len + size);
    115         JS_ASSERT(_len <= _max);
     115        JS_ASSERT(_len < _max);
    116116        memcpy(&_data[_len], chunk, size * sizeof(T));
    117117        _len += size;
    118118    }
  • source/graphics/GameView.cpp

    diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp
    index 0062b44..c69df27 100644
    a b void CGameView::Update(const float deltaRealTime)  
    728728            moveForward += m->ViewScrollSpeed * deltaRealTime;
    729729    }
    730730
    731     if (HotkeyIsPressed("camera.right"))
    732         moveRightward += m->ViewScrollSpeed * deltaRealTime;
    733     if (HotkeyIsPressed("camera.left"))
    734         moveRightward -= m->ViewScrollSpeed * deltaRealTime;
    735     if (HotkeyIsPressed("camera.up"))
    736         moveForward += m->ViewScrollSpeed * deltaRealTime;
    737     if (HotkeyIsPressed("camera.down"))
    738         moveForward -= m->ViewScrollSpeed * deltaRealTime;
     731    if (HotkeyIsPressed("camera.right")) {
     732        if (HotkeyIsPressed("session.queue") && 0 /*building placement*/)
     733            /* move building placement right*/;
     734        else
     735            moveRightward += m->ViewScrollSpeed * deltaRealTime;
     736    }
     737    if (HotkeyIsPressed("camera.left")) {
     738        if (HotkeyIsPressed("session.queue") && 0 /*building placement*/)
     739            /* move building placement left*/;
     740        else
     741            moveRightward -= m->ViewScrollSpeed * deltaRealTime;
     742    }
     743    if (HotkeyIsPressed("camera.up")) {
     744        if (HotkeyIsPressed("session.queue") && 0 /*building placement*/)
     745            /* move building placement up*/;
     746        else
     747            moveForward += m->ViewScrollSpeed * deltaRealTime;
     748    }
     749    if (HotkeyIsPressed("camera.down")) {
     750        if (HotkeyIsPressed("session.queue") && 0 /*building placement*/)
     751            /* move building placement down*/;
     752        else
     753            moveForward -= m->ViewScrollSpeed * deltaRealTime;
     754    }
    739755
    740756    if (g_Joystick.IsEnabled())
    741757    {
  • source/gui/MiniMap.cpp

    diff --git a/source/gui/MiniMap.cpp b/source/gui/MiniMap.cpp
    index f05da71..237b502 100644
    a b  
    4444
    4545bool g_GameRestarted = false;
    4646
    47 static unsigned int ScaleColor(unsigned int color, float x)
     47static unsigned int ScaleColor(unsigned int color, unsigned int val, unsigned int div)
    4848{
    49     unsigned int r = unsigned(float(color & 0xff) * x);
    50     unsigned int g = unsigned(float((color>>8) & 0xff) * x);
    51     unsigned int b = unsigned(float((color>>16) & 0xff) * x);
     49    unsigned int r = (color & 0xff) * val / div;
     50    unsigned int g = ((color>>8) & 0xff) * val / div;
     51    unsigned int b = ((color>>16) & 0xff) * val / div;
    5252    return (0xff000000 | r | g<<8 | b<<16);
    5353}
    5454
    void CMiniMap::RebuildTerrainTexture()  
    483483    u32 y = 0;
    484484    u32 w = m_MapSize - 1;
    485485    u32 h = m_MapSize - 1;
    486     float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
     486    float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight * 4.0f;
    487487
    488488    m_TerrainDirty = false;
    489489
    void CMiniMap::RebuildTerrainTexture()  
    492492        u32 *dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x;
    493493        for(u32 i = 0; i < w; i++)
    494494        {
    495             float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j)
     495            float avgHeight = m_Terrain->GetVertexGroundLevel((int)i, (int)j)
    496496                    + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j)
    497497                    + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1)
    498                     + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
    499                 ) / 4.0f;
     498                    + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1);
    500499
    501500            if(avgHeight < waterHeight)
    502501            {
    void CMiniMap::RebuildTerrainTexture()  
    525524                    }
    526525                }
    527526
    528                 *dataPtr++ = ScaleColor(color, float(val) / 255.0f);
     527                *dataPtr++ = ScaleColor(color, val, 255);
    529528            }
    530529        }
    531530    }