/** * Make the current unit harvest spice. * * Stack: *none*. * * @param script The script engine to operate on. * @return ??. */ uint16 Script_Unit_Harvest(ScriptEngine *script) { Unit *u; uint16 packed; uint16 type; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; if (u->o.type != UNIT_HARVESTER) return 0; if (u->amount >= 100) return 0; packed = Tile_PackTile(u->o.position); type = Map_GetLandscapeType(packed); if (type != LST_SPICE && type != LST_THICK_SPICE) return 0; u->amount += Tools_Random_256() & 1; u->o.flags.s.inTransport = true; Unit_UpdateMap(2, u); if (u->amount > 100) u->amount = 100; if ((Tools_Random_256() & 0x1F) != 0) return 1; Map_ChangeSpiceAmount(packed, -1); return 0; }
static bool Skirmish_IsIslandEnclosed(int start, int end, const SkirmishData* sd) { const int dx[4] = {0, 1, 0, -1}; const int dy[4] = {-1, 0, 1, 0}; const uint16 islandID = sd->islandID[sd->buildable[start].packed]; for (int i = start; i < end; i++) { for (int j = 0; j < 4; j++) { const int x = sd->buildable[i].x + dx[j]; const int y = sd->buildable[i].y + dy[j]; if (!(Map_InRangeX(x) && Map_InRangeY(y))) continue; const uint16 packed = Tile_PackXY(x, y); const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); if (sd->islandID[packed] == islandID) continue; if (!(lst == LST_ENTIRELY_MOUNTAIN || lst == LST_PARTIAL_MOUNTAIN || lst == LST_WALL || lst == LST_STRUCTURE)) return false; } } return true; }
/** * Start the animation on the current tile. * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 1. Always. */ uint16 Script_Unit_StartAnimation(ScriptEngine *script) { Unit *u; uint16 animationUnitID; uint16 position; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; position = Tile_PackTile(Tile_Center(u->o.position)); Animation_Stop_ByTile(position); animationUnitID = g_table_landscapeInfo[Map_GetLandscapeType(Tile_PackTile(u->o.position))].isSand ? 0 : 1; if (u->o.script.variables[1] == 1) animationUnitID += 2; g_map[position].houseID = Unit_GetHouseID(u); assert(animationUnitID < 4); if (g_table_unitInfo[u->o.type].displayMode == 3) { Animation_Start(g_table_animation_unitScript1[animationUnitID], u->o.position, 0, Unit_GetHouseID(u), 4); } else { Animation_Start(g_table_animation_unitScript2[animationUnitID], u->o.position, 0, Unit_GetHouseID(u), 4); } return 1; }
/** * Handle damage to a tile, removing spice, removing concrete, stuff like that. * @param e The Explosion to handle damage on. * @param parameter Unused parameter. */ static void Explosion_Func_TileDamage(Explosion *e, uint16 parameter) { static const int16 craterIconMapIndex[] = { -1, 2, 1 }; uint16 packed; uint16 type; Tile *t; int16 iconMapIndex; uint16 overlaySpriteID; uint16 *iconMap; VARIABLE_NOT_USED(parameter); packed = Tile_PackTile(e->position); if (!Map_IsPositionUnveiled(packed)) return; type = Map_GetLandscapeType(packed); if (type == LST_STRUCTURE || type == LST_DESTROYED_WALL) return; t = &g_map[packed]; if (type == LST_CONCRETE_SLAB) { t->groundSpriteID = g_mapSpriteID[packed]; Map_Update(packed, 0, false); } if (g_table_landscapeInfo[type].craterType == 0) return; /* You cannot damage veiled tiles */ overlaySpriteID = t->overlaySpriteID; if (!Sprite_IsUnveiled(overlaySpriteID)) return; iconMapIndex = craterIconMapIndex[g_table_landscapeInfo[type].craterType]; iconMap = &g_iconMap[g_iconMap[iconMapIndex]]; if (iconMap[0] <= overlaySpriteID && overlaySpriteID <= iconMap[10]) { /* There already is a crater; make it bigger */ overlaySpriteID -= iconMap[0]; if (overlaySpriteID < 4) overlaySpriteID += 2; } else { /* Randomly pick 1 of the 2 possible craters */ overlaySpriteID = Tools_Random_256() & 1; } /* Reduce spice if there is any */ Map_ChangeSpiceAmount(packed, -1); /* Boom a bloom if there is one */ if (t->groundSpriteID == g_bloomSpriteID) { Map_Bloom_ExplodeSpice(packed, g_playerHouseID); return; } /* Update the tile with the crater */ t->overlaySpriteID = overlaySpriteID + iconMap[0]; Map_Update(packed, 0, false); }
/** * Set the animation of a Explosion. * @param e The Explosion to change. * @param animationMapID The animation map to use. */ static void Explosion_Func_SetAnimation(Explosion *e, uint16 animationMapID) { uint16 packed; packed = Tile_PackTile(e->position); if (Structure_Get_ByPackedTile(packed) != NULL) return; animationMapID += Tools_Random_256() & 0x1; animationMapID += g_table_landscapeInfo[Map_GetLandscapeType(packed)].isSand ? 0 : 2; assert(animationMapID < 16); Animation_Start(g_table_animation_map[animationMapID], e->position, 0, e->houseID, 3); }
static uint16 Skirmish_PickRandomLocation(uint16 acceptableLstFlags, uint16 unacceptableLstFlags) { const MapInfo* mi = &g_mapInfos[0]; const int x = mi->minX + Tools_RandomLCG_Range(0, mi->sizeX - 1); const int y = mi->minY + Tools_RandomLCG_Range(0, mi->sizeY - 1); const uint16 packed = Tile_PackXY(x, y); if (g_map[packed].hasUnit) return 0; const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); const uint16 lstFlag = (1 << lst); if ((acceptableLstFlags & lstFlag) && !(unacceptableLstFlags & lstFlag)) return packed; return 0; }
static void Skirmish_GenUnitsAI(HouseType houseID) { const uint16 unacceptableLst = (1 << LST_WALL) | (1 << LST_STRUCTURE) | (1 << LST_BLOOM_FIELD); Structure* factory[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; unsigned int nfactories = 0; PoolFindStruct find; find.houseID = houseID; find.type = 0xFFFF; find.index = STRUCTURE_INDEX_INVALID; Structure* s; while ((nfactories < lengthof(factory)) && ((s = Structure_Find(&find)) != NULL)) { /* Do not produce from hi-tech. */ if (s->o.type == STRUCTURE_LIGHT_VEHICLE || s->o.type == STRUCTURE_HEAVY_VEHICLE || s->o.type == STRUCTURE_WOR_TROOPER || s->o.type == STRUCTURE_BARRACKS) { factory[nfactories] = s; nfactories++; } } if (nfactories == 0) return; for (int count = 8; count > 0;) { const uint16 packed = Skirmish_PickRandomLocation(0xFF, unacceptableLst); if (packed == 0) continue; uint16 dist_ally; uint16 dist_enemy; Skirmish_FindClosestStructures(houseID, packed, &dist_ally, &dist_enemy); if (dist_ally > 8) continue; /* If there's a mountain here, build infantry. */ const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); UnitType type; if (lst == LST_ENTIRELY_MOUNTAIN || lst == LST_PARTIAL_MOUNTAIN) { type = House_GetInfantrySquad(houseID); } /* Otherwise, build a random vehicle. */ else { const int r = Tools_RandomLCG_Range(0, nfactories - 1); type = (UnitType)StructureAI_PickNextToBuild(factory[r]); if (type == UNIT_INVALID) continue; } const UnitActionType actionType = ((Tools_Random_256() & 0x3) == 0) ? ACTION_AMBUSH : ACTION_AREA_GUARD; const tile32 position = Tile_UnpackTile(packed); Scenario_Create_Unit(houseID, type, 256, position, 127, actionType); count--; } }
static bool Skirmish_GenUnitsHuman(HouseType houseID, SkirmishData* sd) { const int delta[7] = { 0, -4, 4, -MAP_SIZE_MAX * 3 - 2, -MAP_SIZE_MAX * 3 + 2, MAP_SIZE_MAX * 3 - 2, MAP_SIZE_MAX * 3 + 2, }; const MapInfo* mi = &g_mapInfos[0]; /* Pick a tile that is not too close to the edge, and not too * close to the enemy. */ int r; for (int attempts = 0; attempts < 100; attempts++) { const int island = Skirmish_PickRandomIsland(sd); if (island < 0) return false; r = Tools_RandomLCG_Range(sd->island[island].start, sd->island[island].end - 1); if (!(mi->minX + 4 <= sd->buildable[r].x && sd->buildable[r].x < mi->minX + mi->sizeX - 4)) continue; if (!(mi->minY + 3 <= sd->buildable[r].y && sd->buildable[r].y < mi->minY + mi->sizeY - 3)) continue; PoolFindStruct find; find.houseID = HOUSE_INVALID; find.type = 0xFFFF; find.index = STRUCTURE_INDEX_INVALID; Structure* s = Structure_Find(&find); for (; s != NULL; s = Structure_Find(&find)) { if (s->o.type == STRUCTURE_SLAB_1x1 || s->o.type == STRUCTURE_SLAB_2x2 || s->o.type == STRUCTURE_WALL) continue; if (House_AreAllied(g_playerHouseID, s->o.houseID)) continue; const uint16 dist = Tile_GetDistancePacked(Tile_PackTile(s->o.position), sd->buildable[r].packed); if (dist < 24) break; } if (s == NULL) { break; } else { r = -1; } } if (r < 0) return false; bool mcvPlaced = false; for (int i = 0; i < 7; i++) { const uint16 packed = sd->buildable[r].packed + delta[i]; const tile32 position = Tile_UnpackTile(packed); UnitType type; if (!mcvPlaced) type = UNIT_MCV; else if (i >= 0 && i < 3) type = UNIT_SIEGE_TANK; else if (i >= 3 && i < 4) type = House_GetIXVehicle(houseID); else if (i >= 4 && i < 5) type = House_GetMediumVehicle(houseID); else if (i >= 5 && i < 6) type = UNIT_QUAD; else if (i >= 6) type = House_GetInfantrySquad(houseID); const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); /* If there's a structure or a bloom here, tough luck. */ if (lst == LST_STRUCTURE || lst == LST_BLOOM_FIELD) continue; /* If there's a mountain here, build infantry instead. */ if (lst == LST_ENTIRELY_MOUNTAIN || lst == LST_PARTIAL_MOUNTAIN) type = House_GetInfantrySquad(houseID); Scenario_Create_Unit(houseID, type, 256, position, 127, (UnitActionType)g_table_unitInfo[type].o.actionsPlayer[3]); if (type == UNIT_MCV) mcvPlaced = true; } return true; }
static bool Skirmish_GenStructuresAI(HouseType houseID, SkirmishData* sd) { uint16 tech_level = 0; uint16 structure = 0; int structure_count = 0; int structure_threshold = 100; int cpu_count = 0; for (HouseType h = HOUSE_HARKONNEN; h < HOUSE_MAX; h++) { if (g_skirmish.brain[h] == BRAIN_CPU_ENEMY || g_skirmish.brain[h] == BRAIN_CPU_ALLY) cpu_count++; } assert(cpu_count != 0); const int max_structure_count = 60 / cpu_count; /* First pass finds out what to build. */ tech_level = 0; structure = 0; structure_count = 0; for (structure = 0; (structure < lengthof(buildorder)) && (tech_level <= g_techLevel); structure++) { if (buildorder[structure].type == STRUCTURE_INVALID) tech_level++; else if ((buildorder[structure].availableHouse & (1 << houseID)) != 0) structure_count++; } if (structure_count > max_structure_count) { structure_count = max_structure_count; SkirmishBuildOrder* bo = (SkirmishBuildOrder*)malloc(structure * sizeof(SkirmishBuildOrder)); assert(bo != NULL); memcpy(bo, buildorder, structure * sizeof(SkirmishBuildOrder)); qsort(bo, structure, sizeof(SkirmishBuildOrder), Skirmish_BuildOrder_Sorter); structure_threshold = bo[structure_count].priority; free(bo); } /* Second pass builds structures below the threshold priority. */ tech_level = 0; structure = 0; for (int attempts = 0; attempts < 100; attempts++) { int island = Skirmish_PickRandomIsland(sd); int range = 8; if (island < 0) return false; /* Re-flood-fill the island, using a new starting point. */ { const int r = Tools_RandomLCG_Range(sd->island[island].start, sd->island[island].end - 1); uint16 dist_ally, dist_enemy; Skirmish_FindClosestStructures(houseID, sd->buildable[r].packed, &dist_ally, &dist_enemy); if ((dist_ally > 16 && dist_ally != 0xFFFF) || (dist_enemy < 24)) continue; const int area = Skirmish_FindBuildableArea(island, sd->buildable[r].x, sd->buildable[r].y, sd, sd->buildable + sd->island[island].start); assert(area == sd->island[island].end - sd->island[island].start); UNUSED(area); for (int i = sd->island[island].start; i < sd->island[island].end; i++) sd->islandID[sd->buildable[i].packed] = island; } /* Use no-cheat-mode to verify land is buildable. */ g_validateStrictIfZero--; assert(g_validateStrictIfZero == 0); /* Place structures. */ while (structure_count > 0) { assert(structure < lengthof(buildorder)); if (buildorder[structure].type == STRUCTURE_INVALID) { tech_level++; structure++; continue; } if (buildorder[structure].priority >= structure_threshold) { structure++; continue; } const StructureType type = buildorder[structure].type; const StructureInfo* si = &g_table_structureInfo[type]; const int r = Tools_RandomLCG_Range(0, range - 1); const uint16 packed = sd->buildable[sd->island[island].start + r].packed; if (Structure_IsValidBuildLandscape(packed, type) != 0) { Structure* s = Structure_Create(STRUCTURE_INDEX_INVALID, type, houseID, packed); assert(s != NULL); s->o.hitpoints = si->o.hitpoints; s->o.flags.s.degrades = false; s->state = STRUCTURE_STATE_IDLE; if (House_AreAllied(g_playerHouseID, houseID)) s->o.seenByHouses = 0xFF; if (s->o.type == STRUCTURE_PALACE) s->countDown = g_table_houseInfo[houseID].specialCountDown; range = min(range + 4, sd->island[island].end - sd->island[island].start); structure++; structure_count--; } else { range++; if (range > sd->island[island].end - sd->island[island].start) break; } } /* Connect structures on this island with concrete slabs. */ for (range--; range > 0; range--) { const int self = sd->island[island].start + range; uint16 packed = sd->buildable[self].packed; const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); if (lst != LST_STRUCTURE && lst != LST_CONCRETE_SLAB) continue; const int parent = sd->island[island].start + sd->buildable[self].parent; packed = sd->buildable[parent].packed; if (Structure_IsValidBuildLandscape(packed, STRUCTURE_SLAB_1x1) == 0) continue; g_validateStrictIfZero++; Structure_Create(STRUCTURE_INDEX_INVALID, STRUCTURE_SLAB_1x1, houseID, packed); g_validateStrictIfZero--; } /* Finished building on this island. Create sub-islands from * remaining buildable tiles. */ g_validateStrictIfZero++; Skirmish_DivideIsland(houseID, island, sd); if (structure_count <= 0) return true; } return false; }
/* Use breadth first flood fill to create a list of buildable tiles * around x0, y0. Only fill tiles that are part of the same source * island (similar to bucket fill same colour). */ static int Skirmish_FindBuildableArea(int island, int x0, int y0, SkirmishData* sd, BuildableTile* buildable) { const int dx[4] = {0, 1, 0, -1}; const int dy[4] = {-1, 0, 1, 0}; int* islandID = sd->islandID; int n = 0; /* Starting case. */ do { if (!(Map_InRangeX(x0) && Map_InRangeY(y0))) break; const uint16 packed = Tile_PackXY(x0, y0); if (islandID[packed] != island) break; const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); const LandscapeInfo* li = &g_table_landscapeInfo[lst]; if (!(li->isValidForStructure || li->isValidForStructure2)) break; buildable[n].x = x0; buildable[n].y = y0; buildable[n].packed = packed; buildable[n].parent = 0; islandID[packed] = sd->nislands; n++; } while (false); for (int i = 0; i < n; i++) { const int r = Tools_Random_256() & 0x3; for (int j = 0; j < 4; j++) { const int x = buildable[i].x + dx[(r + j) & 0x3]; const int y = buildable[i].y + dy[(r + j) & 0x3]; if (!(Map_InRangeX(x) && Map_InRangeY(y))) continue; const uint16 packed = Tile_PackXY(x, y); if (islandID[packed] != island) continue; const LandscapeType lst = (const LandscapeType)Map_GetLandscapeType(packed); const LandscapeInfo* li = &g_table_landscapeInfo[lst]; if (!(li->isValidForStructure || li->isValidForStructure2)) continue; buildable[n].x = x; buildable[n].y = y; buildable[n].packed = packed; buildable[n].parent = i; islandID[packed] = sd->nislands; n++; } } return n; }
/** * 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); } }
/** * 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); }