/** * @brief f__167E_00F3_001E_8CB3. * @details Simplified logic for IT_TILE. */ uint16 Tools_Index_Encode(uint16 index, IndexType type) { switch (type) { case IT_TILE: { const uint16 x = (Tile_GetPackedX(index) << 1) | 0x01; const uint16 y = (Tile_GetPackedY(index) << 8) | 0x80; return (0xC000 | y | x); } case IT_UNIT: { const Unit* u = Unit_Get_ByIndex(index); return (u->o.flags.s.allocated) ? (0x4000 | index) : 0; } case IT_STRUCTURE: return (0x8000 | index); case IT_NONE: default: break; } return 0; }
/** * Remove fog in the radius around the given tile. * * @param tile The tile to remove fog around. * @param radius The radius to remove fog around. */ void Tile_RemoveFogInRadius(tile32 tile, uint16 radius) { uint16 packed; uint16 x, y; int16 i, j; packed = Tile_PackTile(tile); if (!Map_IsValidPosition(packed)) return; x = Tile_GetPackedX(packed); y = Tile_GetPackedY(packed); tile = Tile_MakeXY(x, y); for (i = -radius; i <= radius; i++) { for (j = -radius; j <= radius; j++) { tile32 t; if ((x + i) < 0 || (x + i) >= 64) continue; if ((y + j) < 0 || (y + j) >= 64) continue; packed = Tile_PackXY(x + i, y + j); t = Tile_MakeXY(x + i, y + j); if (Tile_GetDistanceRoundedUp(tile, t) > radius) continue; Map_UnveilTile(packed, g_playerHouseID); } } }
/** * Get to direction to follow to go from packed_from to packed_to. * * @param packed_from The origin. * @param packed_to The destination. * @return The direction. */ uint8 Tile_GetDirectionPacked(uint16 packed_from, uint16 packed_to) { static uint8 returnValues[16] = {0x20, 0x40, 0x20, 0x00, 0xE0, 0xC0, 0xE0, 0x00, 0x60, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xA0, 0x80}; int16 x1, y1, x2, y2; int16 dx, dy; uint16 index; x1 = Tile_GetPackedX(packed_from); y1 = Tile_GetPackedY(packed_from); x2 = Tile_GetPackedX(packed_to); y2 = Tile_GetPackedY(packed_to); index = 0; dy = y1 - y2; if (dy < 0) { index |= 0x8; dy = -dy; } dx = x2 - x1; if (dx < 0) { index |= 0x4; dx = -dx; } if (dx >= dy) { if (((dx + 1) / 2) > dy) index |= 0x1; } else { index |= 0x2; if (((dy + 1) / 2) > dx) index |= 0x1; } return returnValues[index]; }
/** * Draw a single tile on the screen. * * @param packed The tile to draw. */ void GUI_Widget_Viewport_DrawTile(uint16 packed) { uint16 x; uint16 y; uint16 colour; uint16 spriteID; Tile *t; uint16 mapScale; colour = 12; spriteID = 0xFFFF; if (Tile_IsOutOfMap(packed) || !Map_IsValidPosition(packed)) return; x = Tile_GetPackedX(packed); y = Tile_GetPackedY(packed); mapScale = g_scenario.mapScale + 1; if (mapScale == 0 || BitArray_Test(g_displayedMinimap, packed)) return; t = &g_map[packed]; if ((t->isUnveiled && g_playerHouse->flags.radarActivated) || g_debugScenario) { uint16 type = Map_GetLandscapeType(packed); Unit *u; if (mapScale > 1) { spriteID = g_scenario.mapScale + g_table_landscapeInfo[type].spriteID - 1; } else { colour = g_table_landscapeInfo[type].radarColour; } if (g_table_landscapeInfo[type].radarColour == 0xFFFF) { if (mapScale > 1) { spriteID = mapScale + t->houseID * 2 + 29; } else { colour = g_table_houseInfo[t->houseID].minimapColor; } } u = Unit_Get_ByPackedTile(packed); if (u != NULL) { if (mapScale > 1) { if (u->o.type == UNIT_SANDWORM) { spriteID = mapScale + 53; } else { spriteID = mapScale + Unit_GetHouseID(u) * 2 + 29; } } else { if (u->o.type == UNIT_SANDWORM) { colour = 255; } else { colour = g_table_houseInfo[Unit_GetHouseID(u)].minimapColor; } } } } else { Structure *s; s = Structure_Get_ByPackedTile(packed); if (s != NULL && s->o.houseID == g_playerHouseID) { if (mapScale > 1) { spriteID = mapScale + s->o.houseID * 2 + 29; } else { colour = g_table_houseInfo[s->o.houseID].minimapColor; } } else { if (mapScale > 1) { spriteID = g_scenario.mapScale + g_table_landscapeInfo[LST_ENTIRELY_MOUNTAIN].spriteID - 1; } else { colour = 12; } } } x -= g_mapInfos[g_scenario.mapScale].minX; y -= g_mapInfos[g_scenario.mapScale].minY; if (spriteID != 0xFFFF) { x *= g_scenario.mapScale + 1; y *= g_scenario.mapScale + 1; GUI_DrawSprite(g_screenActiveID, g_sprites[spriteID], x, y, 3, 0x4000); } else { GFX_PutPixel(x + 256, y + 136, colour & 0xFF); } }
/** * 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; }
/** * Redraw parts of the viewport that require redrawing. * * @param forceRedraw If true, dirty flags are ignored, and everything is drawn. * @param arg08 ?? * @param drawToMainScreen True if and only if we are drawing to the main screen and not some buffer screen. */ void GUI_Widget_Viewport_Draw(bool forceRedraw, bool arg08, bool drawToMainScreen) { static const uint16 values_32A4[8][2] = { {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {3, 1}, {2, 1}, {1, 1} }; uint16 x; uint16 y; uint16 i; uint16 curPos; bool updateDisplay; Screen oldScreenID; uint16 oldValue_07AE_0000; int16 minX[10]; int16 maxX[10]; PoolFindStruct find; updateDisplay = forceRedraw; memset(minX, 0xF, sizeof(minX)); memset(maxX, 0, sizeof(minX)); oldScreenID = GFX_Screen_SetActive(SCREEN_1); oldValue_07AE_0000 = Widget_SetCurrentWidget(2); if (g_dirtyViewportCount != 0 || forceRedraw) { for (y = 0; y < 10; y++) { uint16 top = (y << 4) + 0x28; for (x = 0; x < (drawToMainScreen ? 15 : 16); x++) { Tile *t; uint16 left; curPos = g_viewportPosition + Tile_PackXY(x, y); if (x < 15 && !forceRedraw && BitArray_Test(g_dirtyViewport, curPos)) { if (maxX[y] < x) maxX[y] = x; if (minX[y] > x) minX[y] = x; updateDisplay = true; } if (!BitArray_Test(g_dirtyMinimap, curPos) && !forceRedraw) continue; BitArray_Set(g_dirtyViewport, curPos); if (x < 15) { updateDisplay = true; if (maxX[y] < x) maxX[y] = x; if (minX[y] > x) minX[y] = x; } t = &g_map[curPos]; left = x << 4; if (!g_debugScenario && g_veiledSpriteID == t->overlaySpriteID) { GUI_DrawFilledRectangle(left, top, left + 15, top + 15, 12); continue; } GFX_DrawSprite(t->groundSpriteID, left, top, t->houseID); if (t->overlaySpriteID == 0 || g_debugScenario) continue; GFX_DrawSprite(t->overlaySpriteID, left, top, t->houseID); } } g_dirtyViewportCount = 0; } find.type = UNIT_SANDWORM; find.index = 0xFFFF; find.houseID = HOUSE_INVALID; while (true) { Unit *u; uint8 *sprite; u = Unit_Find(&find); if (u == NULL) break; if (!u->o.flags.s.isDirty && !forceRedraw) continue; u->o.flags.s.isDirty = false; if (!g_map[Tile_PackTile(u->o.position)].isUnveiled && !g_debugScenario) continue; sprite = GUI_Widget_Viewport_Draw_GetSprite(g_table_unitInfo[u->o.type].groundSpriteID, Unit_GetHouseID(u)); s_spriteFlags = 0x200; if (Map_IsPositionInViewport(u->o.position, &x, &y)) GUI_DrawSprite(g_screenActiveID, sprite, x, y, 2, s_spriteFlags | 0xC000); if (Map_IsPositionInViewport(u->targetLast, &x, &y)) GUI_DrawSprite(g_screenActiveID, sprite, x, y, 2, s_spriteFlags | 0xC000); if (Map_IsPositionInViewport(u->targetPreLast, &x, &y)) GUI_DrawSprite(g_screenActiveID, sprite, x, y, 2, s_spriteFlags | 0xC000); if (u != g_unitSelected) continue; if (!Map_IsPositionInViewport(u->o.position, &x, &y)) continue; GUI_DrawSprite(g_screenActiveID, g_sprites[6], x, y, 2, 0xC000); } if (g_unitSelected == NULL && (g_var_3A08 != 0 || arg08) && (Structure_Get_ByPackedTile(g_selectionRectanglePosition) != NULL || g_selectionType == SELECTIONTYPE_PLACE || g_debugScenario)) { uint16 x1 = (Tile_GetPackedX(g_selectionRectanglePosition) - Tile_GetPackedX(g_minimapPosition)) << 4; uint16 y1 = ((Tile_GetPackedY(g_selectionRectanglePosition) - Tile_GetPackedY(g_minimapPosition)) << 4) + 0x28; uint16 x2 = x1 + (g_selectionWidth << 4) - 1; uint16 y2 = y1 + (g_selectionHeight << 4) - 1; GUI_SetClippingArea(0, 40, 239, SCREEN_HEIGHT - 1); GUI_DrawWiredRectangle(x1, y1, x2, y2, 0xFF); if (g_selectionState == 0 && g_selectionType == SELECTIONTYPE_PLACE) { GUI_DrawLine(x1, y1, x2, y2, 0xFF); GUI_DrawLine(x2, y1, x1, y2, 0xFF); } GUI_SetClippingArea(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); g_var_3A08 = 0; } if (g_dirtyUnitCount != 0 || forceRedraw || updateDisplay) { find.type = 0xFFFF; find.index = 0xFFFF; find.houseID = HOUSE_INVALID; while (true) { Unit *u; UnitInfo *ui; uint16 packed; uint8 orientation; uint16 index; u = Unit_Find(&find); if (u == NULL) break; if (u->o.index < 20 || u->o.index > 101) continue; packed = Tile_PackTile(u->o.position); if ((!u->o.flags.s.isDirty || u->o.flags.s.isNotOnMap) && !forceRedraw && !BitArray_Test(g_dirtyViewport, packed)) continue; u->o.flags.s.isDirty = false; if (!g_map[packed].isUnveiled && !g_debugScenario) continue; ui = &g_table_unitInfo[u->o.type]; if (!Map_IsPositionInViewport(u->o.position, &x, &y)) continue; x += g_table_tilediff[0][u->wobbleIndex].x; y += g_table_tilediff[0][u->wobbleIndex].y; orientation = Orientation_Orientation256ToOrientation8(u->orientation[0].current); if (u->spriteOffset >= 0 || ui->destroyedSpriteID == 0) { static const uint16 values_32C4[8][2] = { {0, 0}, {1, 0}, {1, 0}, {1, 0}, {2, 0}, {1, 1}, {1, 1}, {1, 1} }; index = ui->groundSpriteID; switch (ui->displayMode) { case DISPLAYMODE_UNIT: case DISPLAYMODE_ROCKET: if (ui->movementType == MOVEMENT_SLITHER) break; index += values_32A4[orientation][0]; s_spriteFlags = values_32A4[orientation][1]; break; case DISPLAYMODE_INFANTRY_3_FRAMES: { static const uint16 values_334A[4] = {0, 1, 0, 2}; index += values_32C4[orientation][0] * 3; index += values_334A[u->spriteOffset & 3]; s_spriteFlags = values_32C4[orientation][1]; } break; case DISPLAYMODE_INFANTRY_4_FRAMES: index += values_32C4[orientation][0] * 4; index += u->spriteOffset & 3; s_spriteFlags = values_32C4[orientation][1]; break; default: s_spriteFlags = 0; break; } } else { index = ui->destroyedSpriteID - u->spriteOffset - 1; s_spriteFlags = 0; } if (u->o.type != UNIT_SANDWORM && u->o.flags.s.isHighlighted) s_spriteFlags |= 0x100; if (ui->o.flags.blurTile) s_spriteFlags |= 0x200; GUI_DrawSprite(g_screenActiveID, GUI_Widget_Viewport_Draw_GetSprite(index, (u->deviated != 0) ? u->deviatedHouse : Unit_GetHouseID(u)), x, y, 2, s_spriteFlags | 0xE000, s_paletteHouse, g_paletteMapping2, 1); if (u->o.type == UNIT_HARVESTER && u->actionID == ACTION_HARVEST && u->spriteOffset >= 0 && (u->actionID == ACTION_HARVEST || u->actionID == ACTION_MOVE)) { uint16 type = Map_GetLandscapeType(packed); if (type == LST_SPICE || type == LST_THICK_SPICE) { static const int16 values_334E[8][2] = { {0, 7}, {-7, 6}, {-14, 1}, {-9, -6}, {0, -9}, { 9, -6}, { 14, 1}, { 7, 6} }; GUI_DrawSprite(g_screenActiveID, GUI_Widget_Viewport_Draw_GetSprite((u->spriteOffset % 3) + 0xDF + (values_32A4[orientation][0] * 3), Unit_GetHouseID(u)), x + values_334E[orientation][0], y + values_334E[orientation][1], 2, values_32A4[orientation][1] | 0xC000); } } if (u->spriteOffset >= 0 && ui->turretSpriteID != 0xFFFF) { int16 offsetX = 0; int16 offsetY = 0; uint16 spriteID = ui->turretSpriteID; orientation = Orientation_Orientation256ToOrientation8(u->orientation[ui->o.flags.hasTurret ? 1 : 0].current); switch (ui->turretSpriteID) { case 0x8D: /* sonic tank */ offsetY = -2; break; case 0x92: /* rocket launcher */ offsetY = -3; break; case 0x7E: { /* siege tank */ static const int16 values_336E[8][2] = { { 0, -5}, { 0, -5}, { 2, -3}, { 2, -1}, {-1, -3}, {-2, -1}, {-2, -3}, {-1, -5} }; offsetX = values_336E[orientation][0]; offsetY = values_336E[orientation][1]; } break; case 0x88: { /* devastator */ static const int16 values_338E[8][2] = { { 0, -4}, {-1, -3}, { 2, -4}, {0, -3}, {-1, -3}, { 0, -3}, {-2, -4}, {1, -3} }; offsetX = values_338E[orientation][0]; offsetY = values_338E[orientation][1]; } break; default: break; } s_spriteFlags = values_32A4[orientation][1]; spriteID += values_32A4[orientation][0]; GUI_DrawSprite(g_screenActiveID, GUI_Widget_Viewport_Draw_GetSprite(spriteID, Unit_GetHouseID(u)), x + offsetX, y + offsetY, 2, s_spriteFlags | 0xE000, s_paletteHouse); } if (u->o.flags.s.isSmoking) { uint16 spriteID = 180 + (u->spriteOffset & 3); if (spriteID == 183) spriteID = 181; GUI_DrawSprite(g_screenActiveID, g_sprites[spriteID], x, y - 14, 2, 0xC000); } if (u != g_unitSelected) continue; GUI_DrawSprite(g_screenActiveID, g_sprites[6], x, y, 2, 0xC000); } g_dirtyUnitCount = 0; } for (i = 0; i < EXPLOSION_MAX; i++) { Explosion *e = Explosion_Get_ByIndex(i); curPos = Tile_PackTile(e->position); if (BitArray_Test(g_dirtyViewport, curPos)) e->isDirty = true; if (e->commands == NULL) continue; if (!e->isDirty && !forceRedraw) continue; if (e->spriteID == 0) continue; e->isDirty = false; if (!g_map[curPos].isUnveiled && !g_debugScenario) continue; if (!Map_IsPositionInViewport(e->position, &x, &y)) continue; s_spriteFlags = 0xC000; GUI_DrawSprite(g_screenActiveID, GUI_Widget_Viewport_Draw_GetSprite(e->spriteID, e->houseID), x, y, 2, s_spriteFlags, s_paletteHouse); } if (g_dirtyAirUnitCount != 0 || forceRedraw || updateDisplay) { find.type = 0xFFFF; find.index = 0xFFFF; find.houseID = HOUSE_INVALID; while (true) { static const uint16 values_32E4[8][2] = { {0, 0}, {1, 0}, {2, 0}, {1, 2}, {0, 2}, {1, 3}, {2, 1}, {1, 1} }; Unit *u; UnitInfo *ui; uint8 orientation; uint8 *sprite; uint16 index; u = Unit_Find(&find); if (u == NULL) break; if (u->o.index > 15) continue; curPos = Tile_PackTile(u->o.position); if ((!u->o.flags.s.isDirty || u->o.flags.s.isNotOnMap) && !forceRedraw && !BitArray_Test(g_dirtyViewport, curPos)) continue; u->o.flags.s.isDirty = false; if (!g_map[curPos].isUnveiled && !g_debugScenario) continue; ui = &g_table_unitInfo[u->o.type]; if (!Map_IsPositionInViewport(u->o.position, &x, &y)) continue; index = ui->groundSpriteID; orientation = u->orientation[0].current; s_spriteFlags = 0xC000; switch (ui->displayMode) { case DISPLAYMODE_SINGLE_FRAME: if (u->o.flags.s.bulletIsBig) index++; break; case DISPLAYMODE_UNIT: orientation = Orientation_Orientation256ToOrientation8(orientation); index += values_32E4[orientation][0]; s_spriteFlags |= values_32E4[orientation][1]; break; case DISPLAYMODE_ROCKET: { static const uint16 values_3304[16][2] = { {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {3, 2}, {2, 2}, {1, 2}, {0, 2}, {3, 3}, {2, 3}, {3, 3}, {4, 1}, {3, 1}, {2, 1}, {1, 1} }; orientation = Orientation_Orientation256ToOrientation16(orientation); index += values_3304[orientation][0]; s_spriteFlags |= values_3304[orientation][1]; } break; case DISPLAYMODE_ORNITHOPTER: { static const uint16 values_33AE[4] = {2, 1, 0, 1}; orientation = Orientation_Orientation256ToOrientation8(orientation); index += (values_32E4[orientation][0] * 3) + values_33AE[u->spriteOffset & 3]; s_spriteFlags |= values_32E4[orientation][1]; } break; default: s_spriteFlags = 0x0; break; } if (ui->flags.hasAnimationSet && u->o.flags.s.animationFlip) index += 5; if (u->o.type == UNIT_CARRYALL && u->o.flags.s.inTransport) index += 3; sprite = GUI_Widget_Viewport_Draw_GetSprite(index, Unit_GetHouseID(u)); if (ui->o.flags.hasShadow) GUI_DrawSprite(g_screenActiveID, sprite, x + 1, y + 3, 2, (s_spriteFlags & 0xDFFF) | 0x300, g_paletteMapping1, 1); if (ui->o.flags.blurTile) s_spriteFlags |= 0x200; GUI_DrawSprite(g_screenActiveID, sprite, x, y, 2, s_spriteFlags | 0x2000, s_paletteHouse); } g_dirtyAirUnitCount = 0; } if (updateDisplay) { memset(g_dirtyMinimap, 0, sizeof(g_dirtyMinimap)); memset(g_dirtyViewport, 0, sizeof(g_dirtyViewport)); } if (g_changedTilesCount != 0) { bool init = false; bool update = false; Screen oldScreenID2 = SCREEN_1; for (i = 0; i < g_changedTilesCount; i++) { curPos = g_changedTiles[i]; BitArray_Clear(g_changedTilesMap, curPos); if (!init) { init = true; oldScreenID2 = GFX_Screen_SetActive(SCREEN_1); GUI_Mouse_Hide_InWidget(3); } GUI_Widget_Viewport_DrawTile(curPos); if (!update && BitArray_Test(g_displayedMinimap, curPos)) update = true; } if (update) Map_UpdateMinimapPosition(g_minimapPosition, true); if (init) { GUI_Screen_Copy(32, 136, 32, 136, 8, 64, g_screenActiveID, SCREEN_0); GFX_Screen_SetActive(oldScreenID2); GUI_Mouse_Show_InWidget(); } if (g_changedTilesCount == lengthof(g_changedTiles)) { g_changedTilesCount = 0; for (i = 0; i < 4096; i++) { if (!BitArray_Test(g_changedTilesMap, i)) continue; g_changedTiles[g_changedTilesCount++] = i; if (g_changedTilesCount == lengthof(g_changedTiles)) break; } } else { g_changedTilesCount = 0; } } if ((g_viewportMessageCounter & 1) != 0 && g_viewportMessageText != NULL && (minX[6] <= 14 || maxX[6] >= 0 || arg08 || forceRedraw)) { GUI_DrawText_Wrapper(g_viewportMessageText, 112, 139, 15, 0, 0x132); minX[6] = -1; maxX[6] = 14; } if (updateDisplay && !drawToMainScreen) { if (g_viewport_fadein) { GUI_Mouse_Hide_InWidget(g_curWidgetIndex); /* ENHANCEMENT -- When fading in the game on start, you don't see the fade as it is against the already drawn screen. */ if (g_dune2_enhanced) { Screen oldScreenID2 = g_screenActiveID; GFX_Screen_SetActive(SCREEN_0); GUI_DrawFilledRectangle(g_curWidgetXBase << 3, g_curWidgetYBase, (g_curWidgetXBase + g_curWidgetWidth) << 3, g_curWidgetYBase + g_curWidgetHeight, 0); GFX_Screen_SetActive(oldScreenID2); } GUI_Screen_FadeIn(g_curWidgetXBase, g_curWidgetYBase, g_curWidgetXBase, g_curWidgetYBase, g_curWidgetWidth, g_curWidgetHeight, g_screenActiveID, SCREEN_0); GUI_Mouse_Show_InWidget(); g_viewport_fadein = false; } else { bool init = false; for (i = 0; i < 10; i++) { uint16 width; uint16 height; if (arg08) { minX[i] = 0; maxX[i] = 14; } if (maxX[i] < minX[i]) continue; x = minX[i] * 2; y = (i << 4) + 0x28; width = (maxX[i] - minX[i] + 1) * 2; height = 16; if (!init) { GUI_Mouse_Hide_InWidget(g_curWidgetIndex); init = true; } GUI_Screen_Copy(x, y, x, y, width, height, g_screenActiveID, SCREEN_0); } if (init) GUI_Mouse_Show_InWidget(); } } GFX_Screen_SetActive(oldScreenID); Widget_SetCurrentWidget(oldValue_07AE_0000); }
/* 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); }