/** * Handles the Click events for the Viewport widget. * * @param w The widget. */ bool GUI_Widget_Viewport_Click(Widget *w) { uint16 direction; uint16 x, y; uint16 spriteID; uint16 packed; bool click, drag; spriteID = g_cursorSpriteID; switch (w->index) { default: break; case 39: spriteID = 1; break; case 40: spriteID = 2; break; case 41: spriteID = 4; break; case 42: spriteID = 3; break; case 43: spriteID = g_cursorDefaultSpriteID; break; case 44: spriteID = g_cursorDefaultSpriteID; break; case 45: spriteID = 0; break; } if (spriteID != g_cursorSpriteID) { /* HotSpots for different cursor types. */ static const XYPosition cursorHotSpots[6] = {{0, 0}, {5, 0}, {8, 5}, {5, 8}, {0, 5}, {8, 8}}; s_tickCursor = g_timerGame; Sprites_SetMouseSprite(cursorHotSpots[spriteID].x, cursorHotSpots[spriteID].y, g_sprites[spriteID]); g_cursorSpriteID = spriteID; } if (w->index == 45) return true; click = false; drag = false; if ((w->state.buttonState & 0x11) != 0) { click = true; g_var_37B8 = false; } else if ((w->state.buttonState & 0x22) != 0 && !g_var_37B8) { drag = true; } /* ENHANCEMENT -- Dune2 depends on slow CPUs to limit the rate mouse clicks are handled. */ if (g_dune2_enhanced && (click || drag)) { if (s_tickClick + 2 >= g_timerGame) return true; s_tickClick = g_timerGame; } direction = 0xFFFF; switch (w->index) { default: break; case 39: direction = 0; break; case 40: direction = 2; break; case 41: direction = 6; break; case 42: direction = 4; break; } if (direction != 0xFFFF) { /* Always scroll if we have a click or a drag */ if (!click && !drag) { /* Wait for either one of the timers */ if (s_tickMapScroll + 10 >= g_timerGame || s_tickCursor + 20 >= g_timerGame) return true; /* Don't scroll if we have a structure/unit selected and don't want to autoscroll */ if (g_gameConfig.autoScroll == 0 && (g_selectionType == SELECTIONTYPE_STRUCTURE || g_selectionType == SELECTIONTYPE_UNIT)) return true; } s_tickMapScroll = g_timerGame; Map_MoveDirection(direction); return true; } if (click) { x = g_mouseClickX; y = g_mouseClickY; } else { x = g_mouseX; y = g_mouseY; } if (w->index == 43) { x = x / 16 + Tile_GetPackedX(g_minimapPosition); y = (y - 40) / 16 + Tile_GetPackedY(g_minimapPosition); } else if (w->index == 44) { uint16 mapScale; const MapInfo *mapInfo; mapScale = g_scenario.mapScale; mapInfo = &g_mapInfos[mapScale]; x = min((max(x, 256) - 256) / (mapScale + 1), mapInfo->sizeX - 1) + mapInfo->minX; y = min((max(y, 136) - 136) / (mapScale + 1), mapInfo->sizeY - 1) + mapInfo->minY; } packed = Tile_PackXY(x, y); if (click && g_selectionType == SELECTIONTYPE_TARGET) { Unit *u; ActionType action; uint16 encoded; GUI_DisplayText(NULL, -1); if (g_unitHouseMissile != NULL) { Unit_LaunchHouseMissile(packed); return true; } u = g_unitActive; action = g_activeAction; Object_Script_Variable4_Clear(&u->o); u->targetAttack = 0; u->targetMove = 0; u->route[0] = 0xFF; if (action != ACTION_MOVE && action != ACTION_HARVEST) { encoded = Tools_Index_Encode(Unit_FindTargetAround(packed), IT_TILE); } else { encoded = Tools_Index_Encode(packed, IT_TILE); } Unit_SetAction(u, action); if (action == ACTION_MOVE) { Unit_SetDestination(u, encoded); } else if (action == ACTION_HARVEST) { u->targetMove = encoded; } else { Unit *target; Unit_SetTarget(u, encoded); target = Tools_Index_GetUnit(u->targetAttack); if (target != NULL) target->blinkCounter = 8; } if (g_enableVoices == 0) { Driver_Sound_Play(36, 0xFF); } else if (g_table_unitInfo[u->o.type].movementType == MOVEMENT_FOOT) { Sound_StartSound(g_table_actionInfo[action].soundID); } else { Sound_StartSound(((Tools_Random_256() & 0x1) == 0) ? 20 : 17); } g_unitActive = NULL; g_activeAction = 0xFFFF; GUI_ChangeSelectionType(SELECTIONTYPE_UNIT); return true; } if (click && g_selectionType == SELECTIONTYPE_PLACE) { const StructureInfo *si; Structure *s; House *h; s = g_structureActive; si = &g_table_structureInfo[g_structureActiveType]; h = g_playerHouse; if (Structure_Place(s, g_selectionPosition)) { Voice_Play(20); if (s->o.type == STRUCTURE_PALACE) House_Get_ByIndex(s->o.houseID)->palacePosition = s->o.position; if (g_structureActiveType == STRUCTURE_REFINERY && g_validateStrictIfZero == 0) { Unit *u; g_validateStrictIfZero++; u = Unit_CreateWrapper(g_playerHouseID, UNIT_HARVESTER, Tools_Index_Encode(s->o.index, IT_STRUCTURE)); g_validateStrictIfZero--; if (u == NULL) { h->harvestersIncoming++; } else { u->originEncoded = Tools_Index_Encode(s->o.index, IT_STRUCTURE); } } GUI_ChangeSelectionType(SELECTIONTYPE_STRUCTURE); s = Structure_Get_ByPackedTile(g_structureActivePosition); if (s != NULL) { if ((Structure_GetBuildable(s) & (1 << s->objectType)) == 0) Structure_BuildObject(s, 0xFFFE); } g_structureActiveType = 0xFFFF; g_structureActive = NULL; g_selectionState = 0; /* Invalid. */ GUI_DisplayHint(si->o.hintStringID, si->o.spriteID); House_UpdateRadarState(h); if (h->powerProduction < h->powerUsage) { if ((h->structuresBuilt & (1 << STRUCTURE_OUTPOST)) != 0) { GUI_DisplayText(String_Get_ByIndex(STR_NOT_ENOUGH_POWER_FOR_RADAR_BUILD_WINDTRAPS), 3); } } return true; } Voice_Play(47); if (g_structureActiveType == STRUCTURE_SLAB_1x1 || g_structureActiveType == STRUCTURE_SLAB_2x2) { GUI_DisplayText(String_Get_ByIndex(STR_CAN_NOT_PLACE_FOUNDATION_HERE), 2); } else { GUI_DisplayHint(STR_STRUCTURES_MUST_BE_PLACED_ON_CLEAR_ROCK_OR_CONCRETE_AND_ADJACENT_TO_ANOTHER_FRIENDLY_STRUCTURE, 0xFFFF); GUI_DisplayText(String_Get_ByIndex(STR_CAN_NOT_PLACE_S_HERE), 2, String_Get_ByIndex(si->o.stringID_abbrev)); } return true; } if (click && w->index == 43) { uint16 position; if (g_debugScenario) { position = packed; } else { position = Unit_FindTargetAround(packed); } if (g_map[position].overlaySpriteID != g_veiledSpriteID || g_debugScenario) { if (Object_GetByPackedTile(position) != NULL || g_debugScenario) { Map_SetSelection(position); Unit_DisplayStatusText(g_unitSelected); } } if ((w->state.buttonState & 0x10) != 0) Map_SetViewportPosition(packed); return true; } if ((click || drag) && w->index == 44) { Map_SetViewportPosition(packed); return true; } if (g_selectionType == SELECTIONTYPE_TARGET) { Map_SetSelection(Unit_FindTargetAround(packed)); } else if (g_selectionType == SELECTIONTYPE_PLACE) { Map_SetSelection(packed); } return true; }
static void InGame_Numpad_Move(uint16 key) { if (key == 0) return; switch (key) { case 0x0010: /* TAB */ Map_SelectNext(true); return; case 0x0110: /* SHIFT TAB */ Map_SelectNext(false); return; case 0x005C: /* NUMPAD 4 / ARROW LEFT */ case 0x045C: case 0x055C: Map_MoveDirection(6); return; case 0x0066: /* NUMPAD 6 / ARROW RIGHT */ case 0x0466: case 0x0566: Map_MoveDirection(2); return; case 0x0060: /* NUMPAD 8 / ARROW UP */ case 0x0460: case 0x0560: Map_MoveDirection(0); return; case 0x0062: /* NUMPAD 2 / ARROW DOWN */ case 0x0462: case 0x0562: Map_MoveDirection(4); return; case 0x005B: /* NUMPAD 7 / HOME */ case 0x045B: case 0x055B: Map_MoveDirection(7); return; case 0x005D: /* NUMPAD 1 / END */ case 0x045D: case 0x055D: Map_MoveDirection(5); return; case 0x0065: /* NUMPAD 9 / PAGE UP */ case 0x0465: case 0x0565: Map_MoveDirection(1); return; case 0x0067: /* NUMPAD 3 / PAGE DOWN */ case 0x0467: case 0x0567: Map_MoveDirection(3); return; default: return; } }
/* Process input not caught by widgets, including keypad scrolling, * squad selection, and changing zoom levels. Also handles screen * shake logic. */ static void GameLoop_ProcessUnhandledInput(uint16 key) { const struct { Scancode code; int dx, dy; } keypad[8] = { {SCANCODE_KEYPAD_1, -1, 1}, {SCANCODE_KEYPAD_2, 0, 1}, {SCANCODE_KEYPAD_3, 1, 1}, {SCANCODE_KEYPAD_4, -1, 0}, {SCANCODE_KEYPAD_6, 1, 0}, {SCANCODE_KEYPAD_7, -1, -1}, {SCANCODE_KEYPAD_8, 0, -1}, {SCANCODE_KEYPAD_9, 1, -1} }; int dx = 0, dy = 0; for (unsigned int i = 0; i < lengthof(keypad); i++) { if ((key == keypad[i].code) || (key == 0 && Input_Test(keypad[i].code))) { dx += keypad[i].dx; dy += keypad[i].dy; } } if (dx != 0 || dy != 0) { dx = g_gameConfig.scrollSpeed * clamp(-1, dx, 1); dy = g_gameConfig.scrollSpeed * clamp(-1, dy, 1); } if ((fabsf(g_viewport_desiredDX) >= 4.0f) || (fabsf(g_viewport_desiredDY) >= 4.0f)) { dx += 0.25 * g_viewport_desiredDX; dy += 0.25 * g_viewport_desiredDY; if (fabsf(g_viewport_desiredDX) >= 4.0f) g_viewport_desiredDX *= 0.75; if (fabsf(g_viewport_desiredDY) >= 4.0f) g_viewport_desiredDY *= 0.75; } if (dx != 0 || dy != 0) { Map_MoveDirection(dx, dy); } switch (key) { case SCANCODE_1: case SCANCODE_2: case SCANCODE_3: case SCANCODE_4: case SCANCODE_5: case SCANCODE_6: case SCANCODE_7: case SCANCODE_8: case SCANCODE_9: case SCANCODE_0: Viewport_Hotkey((SquadID)(key - SCANCODE_1 + SQUADID_1)); break; case SCANCODE_H: Viewport_Homekey(); break; case SCANCODE_F5: Audio_DisplayMusicName(); break; case SCANCODE_F6: case SCANCODE_F7: { const bool increase = (key == SCANCODE_F7); const bool adjust_current_track_only = Input_Test(SCANCODE_LSHIFT); Audio_AdjustMusicVolume(increase ? 0.05f : -0.05f, adjust_current_track_only); Audio_DisplayMusicName(); } break; case SCANCODE_OPENBRACE: case SCANCODE_CLOSEBRACE: { ScreenDivID divID = (key == SCANCODE_OPENBRACE) ? SCREENDIV_MENUBAR : SCREENDIV_SIDEBAR; ScreenDiv* viewport = &g_screenDiv[SCREENDIV_VIEWPORT]; ScreenDiv* div = &g_screenDiv[divID]; const int oldh = viewport->height; div->scalex = (div->scalex >= 1.5f) ? 1.0f : 2.0f; div->scaley = div->scalex; A5_InitTransform(false); GameLoop_TweakWidgetDimensions(); g_factoryWindowTotal = -1; Map_MoveDirection(0, oldh - viewport->height); } return; case 0x80 | MOUSE_ZAXIS: if (g_mouseDZ == 0) break; if (g_gameConfig.holdControlToZoom) { if (!Input_Test(SCANCODE_LCTRL)) { Widget* w = GUI_Widget_Get_ByIndex(g_widgetLinkedListHead, 5); if ((w != NULL) && !w->flags.invisible) GUI_Widget_SpriteTextButton_Click(w); break; } } else { const WidgetProperties* w = &g_widgetProperties[WINDOWID_ACTIONPANEL_FRAME]; if (Mouse_InRegion_Div(SCREENDIV_SIDEBAR, w->xBase, w->yBase, w->xBase + w->width - 1, w->yBase + w->height - 1)) break; } /* Fall though. */ case SCANCODE_MINUS: case SCANCODE_EQUALS: case SCANCODE_KEYPAD_MINUS: case SCANCODE_KEYPAD_PLUS: { const float scaling_factor[] = {1.0f, 1.5f, 2.0f, 3.0f}; ScreenDiv* viewport = &g_screenDiv[SCREENDIV_VIEWPORT]; int curr; for (curr = 0; curr < (int)lengthof(scaling_factor); curr++) { if (viewport->scalex <= scaling_factor[curr]) break; } const int tilex = Tile_GetPackedX(g_viewportPosition); const int tiley = Tile_GetPackedY(g_viewportPosition); int viewport_cx = g_viewport_scrollOffsetX + viewport->width / 2; int viewport_cy = g_viewport_scrollOffsetY + viewport->height / 2; int new_scale; /* For mouse wheel zooming in, zoom towards the cursor. */ if ((key == (0x80 | MOUSE_ZAXIS)) && (g_mouseDZ > 0)) { const ScreenDiv* div = &g_screenDiv[SCREENDIV_VIEWPORT]; if (Mouse_InRegion_Div(SCREENDIV_VIEWPORT, 0, 0, div->width, div->height)) { int mousex, mousey; new_scale = curr + 1; Mouse_TransformToDiv(SCREENDIV_VIEWPORT, &mousex, &mousey); viewport_cx = (0.50 * viewport_cx) + (0.50 * mousex); viewport_cy = (0.50 * viewport_cy) + (0.50 * mousey); } else { new_scale = curr + 1; } } else { if (key == SCANCODE_EQUALS || key == SCANCODE_KEYPAD_PLUS) { new_scale = curr + 1; } else { new_scale = curr - 1; } } new_scale = clamp(0, new_scale, (int)lengthof(scaling_factor) - 1); viewport_cx += TILE_SIZE * tilex; viewport_cy += TILE_SIZE * tiley; if (new_scale != curr) { viewport->scalex = scaling_factor[new_scale]; viewport->scaley = scaling_factor[new_scale]; A5_InitTransform(false); GameLoop_TweakWidgetDimensions(); Map_CentreViewport(viewport_cx, viewport_cy); return; } } break; default: break; } /* If we did not init the transform, we may still need to do some * translation transforms for screen shakes. */ if (GFX_ScreenShake_Tick()) A5_InitTransform(false); }