/* * Recount all Units, ignoring the cache array. Also set the unitCount * of all houses to zero. */ internal static void Unit_Recount() { ushort index; var find = new PoolFindStruct(); unchecked { find.houseID = (byte)-1; find.type = (ushort)-1; find.index = (ushort)-1; } var h = House_Find(find); while (h != null) { h.unitCount = 0; h = House_Find(find); } g_unitFindCount = 0; for (index = 0; index < (ushort)UnitIndex.UNIT_INDEX_MAX; index++) { var u = Unit_Get_ByIndex(index); if (!u.o.flags.used) { continue; } h = House_Get_ByIndex(u.o.houseID); h.unitCount++; g_unitFindArray[g_unitFindCount++] = u; } }
static uint s_tickTeamGameLoop; /*!< Indicates next time the GameLoop function is executed. */ /* * Loop over all teams, performing various of tasks. */ internal static void GameLoop_Team() { var find = new PoolFindStruct(); if (s_tickTeamGameLoop > g_timerGame) { return; } s_tickTeamGameLoop = (uint)(g_timerGame + (Tools_Random_256() & 7) + 5); find.houseID = (byte)HouseType.HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; g_scriptCurrentObject = null; g_scriptCurrentUnit = null; g_scriptCurrentStructure = null; while (true) { CTeam t; CHouse h; t = Team_Find(find); if (t == null) { break; } h = House_Get_ByIndex(t.houseID); g_scriptCurrentTeam = t; if (!h.flags.isAIActive) { continue; } if (t.script.delay != 0) { t.script.delay--; continue; } if (!Script_IsLoaded(t.script)) { continue; } if (!Script_Run(t.script)) { /* ENHANCEMENT -- Dune2 aborts all other teams if one gives a script error. This doesn't seem correct */ if (g_dune2_enhanced) { continue; } break; } } }
/* * Save all new Units information to a file. It converts pointers to indices * where needed. * @param fp The file to save to. * @return True if and only if all bytes were written successful. */ internal static bool UnitNew_Save(BinaryWriter fp) { var find = new PoolFindStruct { houseID = (byte)HouseType.HOUSE_INVALID, type = 0xFFFF, index = 0xFFFF }; while (true) { CUnit u; u = Unit_Find(find); if (u == null) { break; } if (!SaveLoad_Save(s_saveUnitNewIndex, fp, u.o)) { return(false); } if (!SaveLoad_Save(s_saveUnitNew, fp, u)) { return(false); } } return(true); }
/* * Save all Teams to a file. It converts pointers to indices where needed. * @param fp The file to save to. * @return True if and only if all bytes were written successful. */ internal static bool Team_Save(BinaryWriter fp) { var find = new PoolFindStruct { houseID = (byte)HouseType.HOUSE_INVALID, type = 0xFFFF, index = 0xFFFF }; while (true) { CTeam t; t = Team_Find(find); if (t == null) { break; } if (!SaveLoad_Save(s_saveTeam, fp, t)) { return(false); } } return(true); }
/* * Recount all Structures, ignoring the cache array. Also set the structureCount * of all houses to zero. */ internal static void Structure_Recount() { ushort index; var find = new PoolFindStruct(); unchecked { find.houseID = (byte)-1; find.type = (ushort)-1; find.index = (ushort)-1; } var h = House_Find(find); while (h != null) { h.unitCount = 0; h = House_Find(find); } g_structureFindCount = 0; for (index = 0; index < (ushort)StructureIndex.STRUCTURE_INDEX_MAX_SOFT; index++) { var s = Structure_Get_ByIndex(index); if (s.o.flags.used) { g_structureFindArray[g_structureFindCount++] = s; } } }
/* * Save all Structures to a file. It converts pointers to indices where needed. * @param fp The file to save to. * @return True if and only if all bytes were written successful. */ internal static bool Structure_Save(BinaryWriter fp) { var find = new PoolFindStruct { houseID = (byte)HouseType.HOUSE_INVALID, type = 0xFFFF, index = 0xFFFF }; while (true) { CStructure s; s = Structure_Find(find); if (s == null) { break; } if (!SaveLoad_Save(s_saveStructure, fp, s)) { return(false); } } return(true); }
//internal static void Unit_Set_ByIndex(Unit u) //{ // Debug.Assert(u.o.index < (ushort)UnitIndex.UNIT_INDEX_MAX); // g_unitArray[u.o.index] = u; //} /* * Find the first matching Unit based on the PoolFindStruct filter data. * * @param find A pointer to a PoolFindStruct which contains filter data and * last known tried index. Calling this functions multiple times with the * same 'find' parameter walks over all possible values matching the filter. * @return The Unit, or NULL if nothing matches (anymore). */ internal static CUnit Unit_Find(PoolFindStruct find) { if (find.index >= g_unitFindCount && find.index != 0xFFFF) { return(null); } find.index++; /* First, we always go to the next index */ for (; find.index < g_unitFindCount; find.index++) { var u = g_unitFindArray[find.index]; if (u == null) { continue; } if (u.o.flags.isNotOnMap && g_validateStrictIfZero == 0) { continue; } if (find.houseID != (byte)HouseType.HOUSE_INVALID && find.houseID != Unit_GetHouseID(u)) { continue; } if (find.type != (ushort)UnitIndex.UNIT_INDEX_INVALID && find.type != u.o.type) { continue; } return(u); } return(null); }
//internal static void House_Set_ByIndex(House h) //{ // Debug.Assert(h.index < (byte)HouseIndex.HOUSE_INDEX_MAX); // g_houseArray[h.index] = h; //} /* * Find the first matching House based on the PoolFindStruct filter data. * * @param find A pointer to a PoolFindStruct which contains filter data and * last known tried index. Calling this functions multiple times with the * same 'find' parameter walks over all possible values matching the filter. * @return The House, or NULL if nothing matches (anymore). */ internal static CHouse House_Find(PoolFindStruct find) { if (find.index >= g_houseFindCount && find.index != 0xFFFF) { return(null); } find.index++; /* First, we always go to the next index */ for (; find.index < g_houseFindCount; find.index++) { var h = g_houseFindArray[find.index]; if (h != null) { return(h); } } return(null); }
/* * Gets the best target for the current team. * * Stack: *none*. * * @param script The script engine to operate on. * @return The encoded index of the best target or 0 if none found. */ internal static ushort Script_Team_FindBestTarget(ScriptEngine _) { CTeam t; var find = new PoolFindStruct(); t = g_scriptCurrentTeam; find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; ushort target; u = Unit_Find(find); if (u == null) { break; } if (u.team - 1 != t.index) { continue; } target = Unit_FindBestTargetEncoded(u, (ushort)(t.action == (ushort)TeamActionType.TEAM_ACTION_KAMIKAZE ? 4 : 0)); if (target == 0) { continue; } if (t.target == target) { return(target); } t.target = target; t.targetTile = Tile_GetTileInDirectionOf(Tile_PackTile(u.o.position), Tools_Index_GetPackedTile(target)); return(target); } return(0); }
/* * Update the CreditsStorage by walking over all structures and checking what * they can hold. * @param houseID The house to check the storage for. */ internal static void House_UpdateCreditsStorage(byte houseID) { var find = new PoolFindStruct(); uint creditsStorage; var oldValidateStrictIfZero = g_validateStrictIfZero; g_validateStrictIfZero = 0; find.houseID = houseID; find.index = 0xFFFF; find.type = 0xFFFF; creditsStorage = 0; while (true) { StructureInfo si; CStructure s; s = Structure_Find(find); if (s == null) { break; } si = g_table_structureInfo[s.o.type]; creditsStorage += si.creditsStorage; } if (creditsStorage > 32000) { creditsStorage = 32000; } House_Get_ByIndex(houseID).creditsStorage = (ushort)creditsStorage; g_validateStrictIfZero = oldValidateStrictIfZero; }
//internal static void Team_Set_ByIndex(Team t) //{ // Debug.Assert(t.index < (ushort)TeamIndex.TEAM_INDEX_MAX); // g_teamArray[t.index] = t; //} /* * Find the first matching Team based on the PoolFindStruct filter data. * * @param find A pointer to a PoolFindStruct which contains filter data and * last known tried index. Calling this functions multiple times with the * same 'find' parameter walks over all possible values matching the filter. * @return The Team, or NULL if nothing matches (anymore). */ internal static CTeam Team_Find(PoolFindStruct find) { if (find.index >= g_teamFindCount && find.index != 0xFFFF) { return(null); } find.index++; /* First, we always go to the next index */ for (; find.index < g_teamFindCount; find.index++) { var t = g_teamFindArray[find.index]; if (t == null) { continue; } if (find.houseID == (byte)HouseType.HOUSE_INVALID || find.houseID == t.houseID) { return(t); } } return(null); }
//internal static void Structure_Set_ByIndex(Structure s) //{ // Debug.Assert(s.o.index < (ushort)StructureIndex.STRUCTURE_INDEX_MAX_HARD); // g_structureArray[s.o.index] = s; //} /* * Find the first matching Structure based on the PoolFindStruct filter data. * * @param find A pointer to a PoolFindStruct which contains filter data and * last known tried index. Calling this functions multiple times with the * same 'find' parameter walks over all possible values matching the filter. * @return The Structure, or NULL if nothing matches (anymore). */ internal static CStructure Structure_Find(PoolFindStruct find) { if (find.index >= g_structureFindCount + 3 && find.index != 0xFFFF) { return(null); } find.index++; /* First, we always go to the next index */ Debug.Assert(g_structureFindCount <= (ushort)StructureIndex.STRUCTURE_INDEX_MAX_SOFT); for (; find.index < g_structureFindCount + 3; find.index++) { CStructure s = null; if (find.index < g_structureFindCount) { s = g_structureFindArray[find.index]; } else { /* There are 3 special structures that are never in the Find array */ Debug.Assert(find.index - g_structureFindCount < 3); switch (find.index - g_structureFindCount) { case 0: s = Structure_Get_ByIndex((ushort)StructureIndex.STRUCTURE_INDEX_WALL); if (s.o.index != (ushort)StructureIndex.STRUCTURE_INDEX_WALL) { continue; } break; case 1: s = Structure_Get_ByIndex((ushort)StructureIndex.STRUCTURE_INDEX_SLAB_2x2); if (s.o.index != (ushort)StructureIndex.STRUCTURE_INDEX_SLAB_2x2) { continue; } break; case 2: s = Structure_Get_ByIndex((ushort)StructureIndex.STRUCTURE_INDEX_SLAB_1x1); if (s.o.index != (ushort)StructureIndex.STRUCTURE_INDEX_SLAB_1x1) { continue; } break; } } if (s == null) { continue; } if (s.o.flags.isNotOnMap && g_validateStrictIfZero == 0) { continue; } if (find.houseID != (byte)HouseType.HOUSE_INVALID && find.houseID != s.o.houseID) { continue; } if (find.type != (ushort)StructureIndex.STRUCTURE_INDEX_INVALID && find.type != s.o.type) { continue; } return(s); } return(null); }
/* * Find a Unit which is within range and not an ally. * * Stack: 1 - Range to find a target in (amount of tiles multiplied with 256). * * @param script The script engine to operate on. * @return The Unit Index of the closest unit within range and not friendly, * or 0 if none exists. */ internal static ushort Script_Structure_FindTargetUnit(ScriptEngine script) { var find = new PoolFindStruct(); CStructure s; CUnit u; uint distanceCurrent; uint targetRange; Tile32 position; s = g_scriptCurrentStructure; targetRange = STACK_PEEK(script, 1); distanceCurrent = 32000; u = null; find.houseID = (byte)HouseType.HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; /* ENHANCEMENT -- The original code calculated distances from the top-left corner of the structure. */ position = g_dune2_enhanced ? Tile_Center(s.o.position) : s.o.position; while (true) { ushort distance; CUnit uf; uf = Unit_Find(find); if (uf == null) { break; } if (House_AreAllied(s.o.houseID, Unit_GetHouseID(uf))) { continue; } if (uf.o.type != (byte)UnitType.UNIT_ORNITHOPTER) { if ((uf.o.seenByHouses & (1 << s.o.houseID)) == 0) { continue; } } distance = Tile_GetDistance(uf.o.position, position); if (distance >= distanceCurrent) { continue; } if (g_dune2_enhanced) { if (uf.o.type == (byte)UnitType.UNIT_ORNITHOPTER) { if (distance > targetRange * 3) { continue; } } else { if (distance > targetRange) { continue; } } } else { if (uf.o.type == (byte)UnitType.UNIT_ORNITHOPTER) { if (distance >= targetRange * 3) { continue; } } else { if (distance >= targetRange) { continue; } } } /* ENHANCEMENT -- The original code swapped the assignment, making it do nothing, Now it finds the closest unit to shoot at, what seems to be the intention */ if (g_dune2_enhanced) { distanceCurrent = distance; } u = uf; } if (u == null) { return((ushort)IndexType.IT_NONE); } return(Tools_Index_Encode(u.o.index, IndexType.IT_UNIT)); }
/* * Redraw parts of the viewport that require redrawing. * * @param forceRedraw If true, dirty flags are ignored, and everything is drawn. * @param hasScrolled Viewport position has changed * @param drawToMainScreen True if and only if we are drawing to the main screen and not some buffer screen. */ internal static void GUI_Widget_Viewport_Draw(bool forceRedraw, bool hasScrolled, bool drawToMainScreen) { var paletteHouse = new byte[16]; /*!< Used for palette manipulation to get housed coloured units etc. */ paletteHouse[0] = 0; ushort x; ushort y; ushort i; ushort curPos; bool updateDisplay; Screen oldScreenID; ushort oldWidgetID; var minX = new short[10]; var maxX = new short[10]; var find = new PoolFindStruct(); updateDisplay = forceRedraw; Array.Fill <short>(minX, 0xF, 0, minX.Length); //memset(minX, 0xF, sizeof(minX)); //Array.Fill<short>(maxX, 0, 0, minX.Length); //memset(maxX, 0, sizeof(minX)); oldScreenID = GFX_Screen_SetActive(Screen.NO1); oldWidgetID = Widget_SetCurrentWidget(2); if (g_dirtyViewportCount != 0 || forceRedraw) { for (y = 0; y < 10; y++) { var top = (ushort)((y << 4) + 0x28); /* 40 */ for (x = 0; x < (drawToMainScreen ? 15 : 16); x++) { CTile t; ushort left; curPos = (ushort)(g_viewportPosition + Tile_PackXY(x, y)); if (x < 15 && !forceRedraw && BitArray_Test(g_dirtyViewport, curPos)) { if (maxX[y] < x) { maxX[y] = (short)x; } if (minX[y] > x) { minX[y] = (short)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] = (short)x; } if (minX[y] > x) { minX[y] = (short)x; } } t = g_map[curPos]; left = (ushort)(x << 4); if (!g_debugScenario && g_veiledTileID == t.overlayTileID) { /* draw a black rectangle */ GUI_DrawFilledRectangle((short)left, (short)top, (short)(left + 15), (short)(top + 15), 12); continue; } GFX_DrawTile(t.groundTileID, left, top, t.houseID); if (t.overlayTileID != 0 && !g_debugScenario) { GFX_DrawTile(t.overlayTileID, left, top, t.houseID); } } } g_dirtyViewportCount = 0; } /* Draw Sandworm */ find.type = (ushort)UnitType.UNIT_SANDWORM; find.index = 0xFFFF; find.houseID = (byte)HouseType.HOUSE_INVALID; while (true) { CUnit u; byte[] sprite; u = Unit_Find(find); if (u == null) { break; } if (!u.o.flags.isDirty && !forceRedraw) { continue; } u.o.flags.isDirty = false; if (!g_map[Tile_PackTile(u.o.position)].isUnveiled && !g_debugScenario) { continue; } sprite = g_sprites[g_table_unitInfo[u.o.type].groundSpriteID]; GUI_Widget_Viewport_GetSprite_HousePalette(sprite, Unit_GetHouseID(u), paletteHouse); if (Map_IsPositionInViewport(u.o.position, out x, out y)) { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)x, (short)y, 2, DRAWSPRITE_FLAG_BLUR | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } if (Map_IsPositionInViewport(u.targetLast, out x, out y)) { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)x, (short)y, 2, DRAWSPRITE_FLAG_BLUR | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } if (Map_IsPositionInViewport(u.targetPreLast, out x, out y)) { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)x, (short)y, 2, DRAWSPRITE_FLAG_BLUR | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } if (u == g_unitSelected && Map_IsPositionInViewport(u.o.position, out x, out y)) { GUI_DrawSprite(Screen.ACTIVE, g_sprites[6], (short)x, (short)y, 2, DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } } if (g_unitSelected == null && (g_selectionRectangleNeedRepaint || hasScrolled) && (Structure_Get_ByPackedTile(g_selectionRectanglePosition) != null || g_selectionType == (ushort)SelectionType.PLACE || g_debugScenario)) { var x1 = (ushort)((Tile_GetPackedX(g_selectionRectanglePosition) - Tile_GetPackedX(g_minimapPosition)) << 4); var y1 = (ushort)(((Tile_GetPackedY(g_selectionRectanglePosition) - Tile_GetPackedY(g_minimapPosition)) << 4) + 0x28); var x2 = (ushort)(x1 + (g_selectionWidth << 4) - 1); var y2 = (ushort)(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 == (ushort)SelectionType.PLACE) { GUI_DrawLine((short)x1, (short)y1, (short)x2, (short)y2, 0xFF); GUI_DrawLine((short)x2, (short)y1, (short)x1, (short)y2, 0xFF); } GUI_SetClippingArea(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); g_selectionRectangleNeedRepaint = false; } /* Draw ground units */ if (g_dirtyUnitCount != 0 || forceRedraw || updateDisplay) { find.type = 0xFFFF; find.index = 0xFFFF; find.houseID = (byte)HouseType.HOUSE_INVALID; while (true) { CUnit u; UnitInfo ui; ushort packed; byte orientation; ushort index; ushort spriteFlags = 0; u = Unit_Find(find); if (u == null) { break; } if (u.o.index is < 20 or > 101) { continue; } packed = Tile_PackTile(u.o.position); if ((!u.o.flags.isDirty || u.o.flags.isNotOnMap) && !forceRedraw && !BitArray_Test(g_dirtyViewport, packed)) { continue; } u.o.flags.isDirty = false; if (!g_map[packed].isUnveiled && !g_debugScenario) { continue; } ui = g_table_unitInfo[u.o.type]; if (!Map_IsPositionInViewport(u.o.position, out x, out y)) { continue; } x += g_table_tilediff[0][u.wobbleIndex].x; y += g_table_tilediff[0][u.wobbleIndex].y; orientation = Orientation_Orientation256ToOrientation8((byte)u.orientation[0].current); if (u.spriteOffset >= 0 || ui.destroyedSpriteID == 0) { index = ui.groundSpriteID; switch ((DisplayMode)ui.displayMode) { case DisplayMode.UNIT: case DisplayMode.ROCKET: if (ui.movementType == (ushort)MovementType.MOVEMENT_SLITHER) { break; } index += values_32A4[orientation][0]; spriteFlags = values_32A4[orientation][1]; break; case DisplayMode.INFANTRY_3_FRAMES: { index += (ushort)(values_32C4[orientation][0] * 3); //[orientation][0] index += values_334A[u.spriteOffset & 3]; spriteFlags = values_32C4[orientation][1]; //[orientation][1] } break; case DisplayMode.INFANTRY_4_FRAMES: index += (ushort)(values_32C4[orientation][0] * 4); //[orientation][0] index += (ushort)(u.spriteOffset & 3); spriteFlags = values_32C4[orientation][1]; //[orientation][1] break; default: spriteFlags = 0; break; } } else { index = (ushort)(ui.destroyedSpriteID - u.spriteOffset - 1); spriteFlags = 0; } if (u.o.type != (byte)UnitType.UNIT_SANDWORM && u.o.flags.isHighlighted) { spriteFlags |= DRAWSPRITE_FLAG_REMAP; } if (ui.o.flags.blurTile) { spriteFlags |= DRAWSPRITE_FLAG_BLUR; } spriteFlags |= DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER; if (GUI_Widget_Viewport_GetSprite_HousePalette(g_sprites[index], (u.deviated != 0) ? u.deviatedHouse : Unit_GetHouseID(u), paletteHouse)) { spriteFlags |= DRAWSPRITE_FLAG_PAL; GUI_DrawSprite(Screen.ACTIVE, g_sprites[index], (short)x, (short)y, 2, spriteFlags, paletteHouse, g_paletteMapping2, (short)1); } else { GUI_DrawSprite(Screen.ACTIVE, g_sprites[index], (short)x, (short)y, 2, spriteFlags, g_paletteMapping2, 1); } if (u.o.type == (byte)UnitType.UNIT_HARVESTER && u.actionID == (byte)ActionType.ACTION_HARVEST && u.spriteOffset >= 0 && (u.actionID == (byte)ActionType.ACTION_HARVEST || u.actionID == (byte)ActionType.ACTION_MOVE)) { var type = Map_GetLandscapeType(packed); if (type is ((ushort)LandscapeType.LST_SPICE)or((ushort)LandscapeType.LST_THICK_SPICE)) { /*GUI_Widget_Viewport_GetSprite_HousePalette(. . ., Unit_GetHouseID(u), paletteHouse),*/ GUI_DrawSprite(Screen.ACTIVE, g_sprites[(u.spriteOffset % 3) + 0xDF + (values_32A4[orientation][0] * 3)], (short)(x + values_334E[orientation][0]), (short)(y + values_334E[orientation][1]), 2, values_32A4[orientation][1] | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } } if (u.spriteOffset >= 0 && ui.turretSpriteID != 0xFFFF) { short offsetX = 0; short offsetY = 0; var spriteID = ui.turretSpriteID; orientation = Orientation_Orientation256ToOrientation8((byte)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 */ offsetX = values_336E[orientation][0]; offsetY = values_336E[orientation][1]; } break; case 0x88: { /* devastator */ offsetX = values_338E[orientation][0]; offsetY = values_338E[orientation][1]; } break; default: break; } spriteID += values_32A4[orientation][0]; if (GUI_Widget_Viewport_GetSprite_HousePalette(g_sprites[spriteID], Unit_GetHouseID(u), paletteHouse)) { GUI_DrawSprite(Screen.ACTIVE, g_sprites[spriteID], (short)(x + offsetX), (short)(y + offsetY), 2, values_32A4[orientation][1] | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER | DRAWSPRITE_FLAG_PAL, paletteHouse); } else { GUI_DrawSprite(Screen.ACTIVE, g_sprites[spriteID], (short)(x + offsetX), (short)(y + offsetY), 2, values_32A4[orientation][1] | DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } } if (u.o.flags.isSmoking) { var spriteID = (ushort)(180 + (u.spriteOffset & 3)); if (spriteID == 183) { spriteID = 181; } GUI_DrawSprite(Screen.ACTIVE, g_sprites[spriteID], (short)x, (short)(y - 14), 2, DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } if (u != g_unitSelected) { continue; } GUI_DrawSprite(Screen.ACTIVE, g_sprites[6], (short)x, (short)y, 2, DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER); } g_dirtyUnitCount = 0; } /* draw explosions */ for (i = 0; i < EXPLOSION_MAX; i++) { var 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, out x, out y)) { continue; } /*GUI_Widget_Viewport_GetSprite_HousePalette(g_sprites[e->spriteID], e->houseID, paletteHouse);*/ GUI_DrawSprite(Screen.ACTIVE, g_sprites[e.spriteID], (short)x, (short)y, 2, DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER /*, paletteHouse*/); } /* draw air units */ if (g_dirtyAirUnitCount != 0 || forceRedraw || updateDisplay) { find.type = 0xFFFF; find.index = 0xFFFF; find.houseID = (byte)HouseType.HOUSE_INVALID; while (true) { CUnit u; UnitInfo ui; byte orientation; byte[] sprite; ushort index; ushort spriteFlags; u = Unit_Find(find); if (u == null) { break; } if (u.o.index > 15) { continue; } curPos = Tile_PackTile(u.o.position); if ((!u.o.flags.isDirty || u.o.flags.isNotOnMap) && !forceRedraw && !BitArray_Test(g_dirtyViewport, curPos)) { continue; } u.o.flags.isDirty = false; if (!g_map[curPos].isUnveiled && !g_debugScenario) { continue; } ui = g_table_unitInfo[u.o.type]; if (!Map_IsPositionInViewport(u.o.position, out x, out y)) { continue; } index = ui.groundSpriteID; orientation = (byte)u.orientation[0].current; spriteFlags = DRAWSPRITE_FLAG_WIDGETPOS | DRAWSPRITE_FLAG_CENTER; switch ((DisplayMode)ui.displayMode) { case DisplayMode.SINGLE_FRAME: if (u.o.flags.bulletIsBig) { index++; } break; case DisplayMode.UNIT: orientation = Orientation_Orientation256ToOrientation8(orientation); index += values_32E4[orientation][0]; spriteFlags |= values_32E4[orientation][1]; break; case DisplayMode.ROCKET: { orientation = Orientation_Orientation256ToOrientation16(orientation); index += values_3304[orientation][0]; spriteFlags |= values_3304[orientation][1]; } break; case DisplayMode.ORNITHOPTER: { orientation = Orientation_Orientation256ToOrientation8(orientation); index += (ushort)((values_32E4[orientation][0] * 3) + values_33AE[u.spriteOffset & 3]); spriteFlags |= values_32E4[orientation][1]; } break; default: spriteFlags = 0x0; break; } if (ui.flags.hasAnimationSet && u.o.flags.animationFlip) { index += 5; } if (u.o.type == (byte)UnitType.UNIT_CARRYALL && u.o.flags.inTransport) { index += 3; } sprite = g_sprites[index]; if (ui.o.flags.hasShadow) { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)(x + 1), (short)(y + 3), 2, (spriteFlags & ~DRAWSPRITE_FLAG_PAL) | DRAWSPRITE_FLAG_REMAP | DRAWSPRITE_FLAG_BLUR, g_paletteMapping1, (short)1); } if (ui.o.flags.blurTile) { spriteFlags |= DRAWSPRITE_FLAG_BLUR; } if (GUI_Widget_Viewport_GetSprite_HousePalette(sprite, Unit_GetHouseID(u), paletteHouse)) { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)x, (short)y, 2, spriteFlags | DRAWSPRITE_FLAG_PAL, paletteHouse); } else { GUI_DrawSprite(Screen.ACTIVE, sprite, (short)x, (short)y, 2, spriteFlags); } } g_dirtyAirUnitCount = 0; } if (updateDisplay) { Array.Fill <byte>(g_dirtyMinimap, 0, 0, g_dirtyMinimap.Length); //memset(g_dirtyMinimap, 0, sizeof(g_dirtyMinimap)); Array.Fill <byte>(g_dirtyViewport, 0, 0, g_dirtyViewport.Length); //memset(g_dirtyViewport, 0, sizeof(g_dirtyViewport)); } if (g_changedTilesCount != 0) { var init = false; var update = false; ushort minY = 0xffff; ushort maxY = 0; var oldScreenID2 = Screen.NO1; 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.NO1); GUI_Mouse_Hide_InWidget(3); } if (GUI_Widget_Viewport_DrawTile(curPos)) { y = (ushort)(Tile_GetPackedY(curPos) - g_mapInfos[g_scenario.mapScale].minY); /* +136 */ y *= (ushort)(g_scenario.mapScale + 1); if (y > maxY) { maxY = y; } if (y < minY) { minY = y; } } if (!update && BitArray_Test(g_displayedMinimap, curPos)) { update = true; } } if (update) { Map_UpdateMinimapPosition(g_minimapPosition, true); } if (init) { if (hasScrolled) { /* force copy of the whole map (could be of the white rectangle) */ minY = 0; maxY = (ushort)(63 - g_scenario.mapScale); } /* MiniMap : redraw only line that changed */ if (minY < maxY) { GUI_Screen_Copy(32, (short)(136 + minY), 32, (short)(136 + minY), 8, (short)(maxY + 1 + g_scenario.mapScale - minY), Screen.ACTIVE, Screen.NO0); } GFX_Screen_SetActive(oldScreenID2); GUI_Mouse_Show_InWidget(); } if (g_changedTilesCount == g_changedTiles.Length) { g_changedTilesCount = 0; for (i = 0; i < 4096; i++) { if (!BitArray_Test(g_changedTilesMap, i)) { continue; } g_changedTiles[g_changedTilesCount++] = i; if (g_changedTilesCount == g_changedTiles.Length) { break; } } } else { g_changedTilesCount = 0; } } if ((g_viewportMessageCounter & 1) != 0 && g_viewportMessageText != null && (minX[6] <= 14 || maxX[6] >= 0 || hasScrolled || 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) { var oldScreenID2 = GFX_Screen_SetActive(Screen.NO0); GUI_DrawFilledRectangle((short)(g_curWidgetXBase << 3), (short)g_curWidgetYBase, (short)((g_curWidgetXBase + g_curWidgetWidth) << 3), (short)(g_curWidgetYBase + g_curWidgetHeight), 0); GFX_Screen_SetActive(oldScreenID2); } GUI_Screen_FadeIn(g_curWidgetXBase, g_curWidgetYBase, g_curWidgetXBase, g_curWidgetYBase, g_curWidgetWidth, g_curWidgetHeight, Screen.ACTIVE, Screen.NO0); GUI_Mouse_Show_InWidget(); g_viewport_fadein = false; } else { var init = false; for (i = 0; i < 10; i++) { ushort width; ushort height; if (hasScrolled) { minX[i] = 0; maxX[i] = 14; } if (maxX[i] < minX[i]) { continue; } x = (ushort)(minX[i] * 2); y = (ushort)((i << 4) + 0x28); width = (ushort)((maxX[i] - minX[i] + 1) * 2); height = 16; if (!init) { GUI_Mouse_Hide_InWidget(g_curWidgetIndex); init = true; } GUI_Screen_Copy((short)x, (short)y, (short)x, (short)y, (short)width, (short)height, Screen.ACTIVE, Screen.NO0); } if (init) { GUI_Mouse_Show_InWidget(); } } } GFX_Screen_SetActive(oldScreenID); Widget_SetCurrentWidget(oldWidgetID); }
/* * Calculate the power usage and production, and the credits storage. * * @param h The house to calculate the numbers for. */ internal static void House_CalculatePowerAndCredit(CHouse h) { var find = new PoolFindStruct(); if (h == null) { return; } h.powerUsage = 0; h.powerProduction = 0; h.creditsStorage = 0; find.houseID = h.index; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { StructureInfo si; CStructure s; s = Structure_Find(find); if (s == null) { break; } /* ENHANCEMENT -- Only count structures that are placed on the map, not ones we are building. */ if (g_dune2_enhanced && s.o.flags.isNotOnMap) { continue; } si = g_table_structureInfo[s.o.type]; h.creditsStorage += si.creditsStorage; /* Positive values means usage */ if (si.powerUsage >= 0) { h.powerUsage += (ushort)si.powerUsage; continue; } /* Negative value and full health means everything goes to production */ if (s.o.hitpoints >= si.o.hitpoints) { h.powerProduction += (ushort)-si.powerUsage; continue; } /* Negative value and partial health, calculate how much should go to production (capped at 50%) */ /* ENHANCEMENT -- The 50% cap of Dune2 is silly and disagress with the GUI. If your hp is 10%, so should the production. */ if (!g_dune2_enhanced && s.o.hitpoints <= si.o.hitpoints / 2) { h.powerProduction += (ushort)((-si.powerUsage) / 2); continue; } h.powerProduction += (ushort)((-si.powerUsage) * s.o.hitpoints / si.o.hitpoints); } /* Check if we are low on power */ if (h.index == (byte)g_playerHouseID && h.powerUsage > h.powerProduction) { GUI_DisplayText(String_Get_ByIndex(Text.STR_INSUFFICIENT_POWER_WINDTRAP_IS_NEEDED), 1); } /* If there are no buildings left, you lose your right on 'credits without storage' */ if (h.index == (byte)g_playerHouseID && h.structuresBuilt == 0 && g_validateStrictIfZero == 0) { g_playerCreditsNoSilo = 0; } }
/* * Tries to add the closest unit to the current team. * * Stack: *none*. * * @param script The script engine to operate on. * @return The amount of space left in current team. */ internal static ushort Script_Team_AddClosestUnit(ScriptEngine _) { CTeam t; CUnit closest = null; CUnit closest2 = null; ushort minDistance = 0; ushort minDistance2 = 0; var find = new PoolFindStruct(); t = g_scriptCurrentTeam; if (t.members >= t.maxMembers) { return(0); } find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; CTeam t2; ushort distance; u = Unit_Find(find); if (u == null) { break; } if (!u.o.flags.byScenario) { continue; } if (u.o.type == (byte)UnitType.UNIT_SABOTEUR) { continue; } if (g_table_unitInfo[u.o.type].movementType != t.movementType) { continue; } if (u.team == 0) { distance = Tile_GetDistance(t.position, u.o.position); if (distance >= minDistance && minDistance != 0) { continue; } minDistance = distance; closest = u; continue; } t2 = Team_Get_ByIndex((ushort)(u.team - 1)); if (t2.members > t2.minMembers) { continue; } distance = Tile_GetDistance(t.position, u.o.position); if (distance >= minDistance2 && minDistance2 != 0) { continue; } minDistance2 = distance; closest2 = u; } if (closest == null) { closest = closest2; } if (closest == null) { return(0); } Unit_RemoveFromTeam(closest); return(Unit_AddToTeam(closest, t)); }
/* * Unknown function 0788. * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 0. Always. */ internal static ushort Script_Team_Unknown0788(ScriptEngine _) { CTeam t; Tile32 tile; var find = new PoolFindStruct(); t = g_scriptCurrentTeam; if (t.target == 0) { return(0); } tile = Tools_Index_GetTile(t.target); find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; ushort distance; ushort packed; short orientation; u = Unit_Find(find); if (u == null) { break; } if (u.team - 1 != t.index) { continue; } if (t.target == 0) { Unit_SetAction(u, ActionType.ACTION_GUARD); continue; } distance = (ushort)(g_table_unitInfo[u.o.type].fireDistance << 8); if (u.actionID == (byte)ActionType.ACTION_ATTACK && u.targetAttack == t.target) { if (u.targetMove != 0) { continue; } if (Tile_GetDistance(u.o.position, tile) >= distance) { continue; } } if (u.actionID != (byte)ActionType.ACTION_ATTACK) { Unit_SetAction(u, ActionType.ACTION_ATTACK); } orientation = (short)((Tile_GetDirection(tile, u.o.position) & 0xC0) + Tools_RandomLCG_Range(0, 127)); if (orientation < 0) { orientation += 256; } packed = Tile_PackTile(Tile_MoveByDirection(tile, orientation, distance)); if (Object_GetByPackedTile(packed) == null) { Unit_SetDestination(u, Tools_Index_Encode(packed, IndexType.IT_TILE)); } else { Unit_SetDestination(u, Tools_Index_Encode(Tile_PackTile(tile), IndexType.IT_TILE)); } Unit_SetTarget(u, t.target); } return(0); }
/* * Gets the average distance between current team members, and set the * position of the team to the average position. * * Stack: *none*. * * @param script The script engine to operate on. * @return The average distance. */ internal static ushort Script_Team_GetAverageDistance(ScriptEngine _) { ushort averageX = 0; ushort averageY = 0; ushort count = 0; ushort distance = 0; CTeam t; var find = new PoolFindStruct(); t = g_scriptCurrentTeam; find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; u = Unit_Find(find); if (u == null) { break; } if (t.index != u.team - 1) { continue; } count++; averageX += (ushort)((u.o.position.x >> 8) & 0x3f); averageY += (ushort)((u.o.position.y >> 8) & 0x3f); } if (count == 0) { return(0); } averageX /= count; averageY /= count; Tile_MakeXY(ref t.position, averageX, averageY); find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; u = Unit_Find(find); if (u == null) { break; } if (t.index != u.team - 1) { continue; } distance += Tile_GetDistanceRoundedUp(u.o.position, t.position); } distance /= count; if (t.target == 0 || t.targetTile == 0) { return(distance); } if (Tile_GetDistancePacked(Tile_PackXY(averageX, averageY), Tools_Index_GetPackedTile(t.target)) <= 10) { t.targetTile = 2; } return(distance); }
/* * Loop over all houses, preforming various of tasks. */ internal static void GameLoop_House() { var find = new PoolFindStruct(); CHouse h; // = NULL; var tickHouse = false; var tickPowerMaintenance = false; var tickStarport = false; var tickReinforcement = false; var tickMissileCountdown = false; var tickStarportAvailability = false; if (g_debugScenario) { return; } if (s_tickHouseHouse <= g_timerGame) { tickHouse = true; s_tickHouseHouse = g_timerGame + 900; } if (g_tickHousePowerMaintenance <= g_timerGame) { tickPowerMaintenance = true; g_tickHousePowerMaintenance = g_timerGame + 10800; } if (s_tickHouseStarport <= g_timerGame) { tickStarport = true; s_tickHouseStarport = g_timerGame + 180; } if (s_tickHouseReinforcement <= g_timerGame) { tickReinforcement = true; s_tickHouseReinforcement = (uint)(g_timerGame + (g_debugGame ? 60 : 600)); } if (s_tickHouseMissileCountdown <= g_timerGame) { tickMissileCountdown = true; s_tickHouseMissileCountdown = g_timerGame + 60; } if (s_tickHouseStarportAvailability <= g_timerGame) { tickStarportAvailability = true; s_tickHouseStarportAvailability = g_timerGame + 1800; } if (tickMissileCountdown && g_houseMissileCountdown != 0) { g_houseMissileCountdown--; Sound_Output_Feedback((ushort)(g_houseMissileCountdown + 41)); if (g_houseMissileCountdown == 0) { Unit_LaunchHouseMissile(Map_FindLocationTile(4, (byte)g_playerHouseID)); } } if (tickStarportAvailability) { ushort type; /* Pick a random unit to increase starport availability */ type = Tools_RandomLCG_Range(0, (ushort)(UnitType.UNIT_MAX - 1)); /* Increase how many of this unit is available via starport by one */ if (g_starportAvailable[type] is not 0 and < 10) { if (g_starportAvailable[type] == -1) { g_starportAvailable[type] = 1; } else { g_starportAvailable[type]++; } } } if (tickReinforcement) { CUnit nu = null; int i; for (i = 0; i < 16; i++) { ushort locationID; bool deployed; CUnit u; if (g_scenario.reinforcement[i].unitID == (ushort)UnitIndex.UNIT_INDEX_INVALID) { continue; } if (g_scenario.reinforcement[i].timeLeft == 0) { continue; } if (--g_scenario.reinforcement[i].timeLeft != 0) { continue; } u = Unit_Get_ByIndex(g_scenario.reinforcement[i].unitID); locationID = g_scenario.reinforcement[i].locationID; deployed = false; if (locationID >= 4) { if (nu == null) { nu = Unit_Create((ushort)UnitIndex.UNIT_INDEX_INVALID, (byte)UnitType.UNIT_CARRYALL, u.o.houseID, Tile_UnpackTile(Map_FindLocationTile((ushort)(Tools_Random_256() & 3), u.o.houseID)), 100); if (nu != null) { nu.o.flags.byScenario = true; Unit_SetDestination(nu, Tools_Index_Encode(Map_FindLocationTile(locationID, u.o.houseID), IndexType.IT_TILE)); } } if (nu != null) { u.o.linkedID = nu.o.linkedID; nu.o.linkedID = (byte)u.o.index; nu.o.flags.inTransport = true; g_scenario.reinforcement[i].unitID = (ushort)UnitIndex.UNIT_INDEX_INVALID; deployed = true; } else { /* Failed to create carry-all, try again in a short moment */ g_scenario.reinforcement[i].timeLeft = 1; } } else { deployed = Unit_SetPosition(u, Tile_UnpackTile(Map_FindLocationTile(locationID, u.o.houseID))); } if (deployed && g_scenario.reinforcement[i].repeat != 0) { var tile = new Tile32 { x = 0xFFFF, y = 0xFFFF }; g_validateStrictIfZero++; u = Unit_Create((ushort)UnitIndex.UNIT_INDEX_INVALID, u.o.type, u.o.houseID, tile, 0); g_validateStrictIfZero--; if (u != null) { g_scenario.reinforcement[i].unitID = u.o.index; g_scenario.reinforcement[i].timeLeft = g_scenario.reinforcement[i].timeBetween; } } } } find.houseID = (byte)HouseType.HOUSE_INVALID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { h = House_Find(find); if (h == null) { break; } if (tickHouse) { /* ENHANCEMENT -- Originally this code was outside the house loop, which seems very odd. * This problem is considered to be so bad, that the original code has been removed. */ if (h.index != (byte)g_playerHouseID) { if (h.creditsStorage < h.credits) { h.credits = h.creditsStorage; } } else { var maxCredits = Math.Max(h.creditsStorage, g_playerCreditsNoSilo); if (h.credits > maxCredits) { h.credits = maxCredits; GUI_DisplayText(String_Get_ByIndex(Text.STR_INSUFFICIENT_SPICE_STORAGE_AVAILABLE_SPICE_IS_LOST), 1); } } if (h.index == (byte)g_playerHouseID) { if (h.creditsStorage > g_playerCreditsNoSilo) { g_playerCreditsNoSilo = 0; } if (g_playerCreditsNoSilo == 0 && g_campaignID > 1 && h.credits != 0) { if (h.creditsStorage != 0 && ((h.credits * 256 / h.creditsStorage) > 200)) { GUI_DisplayText(String_Get_ByIndex(Text.STR_SPICE_STORAGE_CAPACITY_LOW_BUILD_SILOS), 0); } } if (h.credits < 100 && g_playerCreditsNoSilo != 0) { GUI_DisplayText(String_Get_ByIndex(Text.STR_CREDITS_ARE_LOW_HARVEST_SPICE_FOR_MORE_CREDITS), 0); } } } if (tickHouse) { House_EnsureHarvesterAvailable(h.index); } if (tickStarport && h.starportLinkedID != (ushort)UnitIndex.UNIT_INDEX_INVALID) { CUnit u = null; h.starportTimeLeft--; if ((short)h.starportTimeLeft < 0) { h.starportTimeLeft = 0; } if (h.starportTimeLeft == 0) { CStructure s; s = Structure_Get_ByIndex(g_structureIndex); if (s.o.type == (byte)StructureType.STRUCTURE_STARPORT && s.o.houseID == h.index) { u = Unit_CreateWrapper(h.index, UnitType.UNIT_FRIGATE, Tools_Index_Encode(s.o.index, IndexType.IT_STRUCTURE)); } else { var find2 = new PoolFindStruct { houseID = h.index, index = 0xFFFF, type = (ushort)StructureType.STRUCTURE_STARPORT }; while (true) { s = Structure_Find(find2); if (s == null) { break; } if (s.o.linkedID != 0xFF) { continue; } u = Unit_CreateWrapper(h.index, UnitType.UNIT_FRIGATE, Tools_Index_Encode(s.o.index, IndexType.IT_STRUCTURE)); break; } } if (u != null) { u.o.linkedID = (byte)h.starportLinkedID; h.starportLinkedID = (ushort)UnitIndex.UNIT_INDEX_INVALID; u.o.flags.inTransport = true; Sound_Output_Feedback(38); } h.starportTimeLeft = (ushort)((u != null) ? g_table_houseInfo[h.index].starportDeliveryTime : 1); } } if (tickHouse) { House_CalculatePowerAndCredit(h); Structure_CalculateHitpointsMax(h); if (h.timerUnitAttack != 0) { h.timerUnitAttack--; } if (h.timerSandwormAttack != 0) { h.timerSandwormAttack--; } if (h.timerStructureAttack != 0) { h.timerStructureAttack--; } if (h.harvestersIncoming > 0 && Unit_CreateWrapper(h.index, UnitType.UNIT_HARVESTER, 0) != null) { h.harvestersIncoming--; } } if (tickPowerMaintenance) { var powerMaintenanceCost = (ushort)((h.powerUsage / 32) + 1); h.credits -= Math.Min(h.credits, powerMaintenanceCost); } } }
/* * Unknown function 0543. * * Stack: 1 - A distance. * * @param script The script engine to operate on. * @return The number of moving units. */ internal static ushort Script_Team_Unknown0543(ScriptEngine script) { CTeam t; ushort count = 0; ushort distance; var find = new PoolFindStruct(); t = g_scriptCurrentTeam; distance = STACK_PEEK(script, 1); find.houseID = t.houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { CUnit u; Tile32 tile; ushort distanceUnitDest; ushort distanceUnitTeam; ushort distanceTeamDest; u = Unit_Find(find); if (u == null) { break; } if (t.index != u.team - 1) { continue; } tile = Tools_Index_GetTile(u.targetMove); distanceUnitTeam = Tile_GetDistanceRoundedUp(u.o.position, t.position); if (u.targetMove != 0) { distanceUnitDest = Tile_GetDistanceRoundedUp(u.o.position, tile); distanceTeamDest = Tile_GetDistanceRoundedUp(t.position, tile); } else { distanceUnitDest = 64; distanceTeamDest = 64; } if ((distanceUnitDest < distanceTeamDest && (distance + 2) < distanceUnitTeam) || (distanceUnitDest >= distanceTeamDest && distanceUnitTeam > distance)) { Unit_SetAction(u, ActionType.ACTION_MOVE); tile = Tile_MoveByRandom(t.position, (ushort)(distance << 4), true); Unit_SetDestination(u, Tools_Index_Encode(Tile_PackTile(tile), IndexType.IT_TILE)); count++; continue; } Unit_SetAction(u, ActionType.ACTION_GUARD); } return(count); }
/* * Gives a harvester to the given house if it has a refinery and no harvesters. * * @param houseID The index of the house to give a harvester to. */ static void House_EnsureHarvesterAvailable(byte houseID) { var find = new PoolFindStruct(); CStructure s; find.houseID = houseID; find.type = 0xFFFF; find.index = 0xFFFF; while (true) { s = Structure_Find(find); if (s == null) { break; } /* ENHANCEMENT -- Dune2 checked the wrong type to skip. LinkedID is a structure for a Construction Yard */ if (!g_dune2_enhanced && s.o.type == (byte)StructureType.STRUCTURE_HEAVY_VEHICLE) { continue; } if (g_dune2_enhanced && s.o.type == (byte)StructureType.STRUCTURE_CONSTRUCTION_YARD) { continue; } if (s.o.linkedID == (byte)UnitType.UNIT_INVALID) { continue; } if (Unit_Get_ByIndex(s.o.linkedID).o.type == (byte)UnitType.UNIT_HARVESTER) { return; } } find.houseID = houseID; find.type = (ushort)UnitType.UNIT_CARRYALL; find.index = 0xFFFF; while (true) { CUnit u; u = Unit_Find(find); if (u == null) { break; } if (u.o.linkedID == (byte)UnitType.UNIT_INVALID) { continue; } if (Unit_Get_ByIndex(u.o.linkedID).o.type == (byte)UnitType.UNIT_HARVESTER) { return; } } if (Unit_IsTypeOnMap(houseID, (byte)UnitType.UNIT_HARVESTER)) { return; } find.houseID = houseID; find.type = (ushort)StructureType.STRUCTURE_REFINERY; find.index = 0xFFFF; s = Structure_Find(find); if (s == null) { return; } if (Unit_CreateWrapper(houseID, UnitType.UNIT_HARVESTER, Tools_Index_Encode(s.o.index, IndexType.IT_STRUCTURE)) == null) { return; } if (houseID != (byte)g_playerHouseID) { return; } GUI_DisplayText(String_Get_ByIndex(Text.STR_HARVESTER_IS_HEADING_TO_REFINERY), 0); }
/* * Save the game to a filename * * @param fp The filename of the savegame. * @param description The description of the savegame. * @return True if and only if all bytes were written successful. */ internal static bool SaveGame_SaveFile(string filename, string description) { FileStream fp; bool res; /* In debug-scenario mode, the whole map is uncovered. Cover it now in * the savegame based on the current position of the units and * structures. */ if (g_debugScenario) { var find = new PoolFindStruct(); ushort i; /* Add fog of war for all tiles on the map */ for (i = 0; i < 0x1000; i++) { var tile = g_map[i]; tile.isUnveiled = false; tile.overlayTileID = g_veiledTileID; } find.houseID = (byte)HouseType.HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Remove the fog of war for all units */ while (true) { CUnit u; u = Unit_Find(find); if (u == null) { break; } Unit_RemoveFog(u); } find.houseID = (byte)HouseType.HOUSE_INVALID; find.type = 0xFFFF; find.index = 0xFFFF; /* Remove the fog of war for all structures */ while (true) { CStructure s; s = Structure_Find(find); if (s == null) { break; } if (s.o.type is ((byte)StructureType.STRUCTURE_SLAB_1x1)or((byte)StructureType.STRUCTURE_SLAB_2x2) or((byte)StructureType.STRUCTURE_WALL)) { continue; } Structure_RemoveFog(s); } } fp = FOpenDataDir(SearchDirectory.SEARCHDIR_PERSONAL_DATA_DIR, filename, "wb"); if (fp == null) { Trace.WriteLine($"ERROR: Failed to open file '{filename}' for writing."); return(false); } g_validateStrictIfZero++; res = Save_Main(fp, description); g_validateStrictIfZero--; fp.Close(); if (!res) { /* TODO -- Also remove the savegame now */ Trace.WriteLine($"ERROR: Error while writing savegame '{filename}'."); return(false); } return(true); }