Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to fix Skirmish #224

Open
TobiasKarnat opened this issue Dec 16, 2020 · 14 comments
Open

Attempt to fix Skirmish #224

TobiasKarnat opened this issue Dec 16, 2020 · 14 comments
Labels
standalone Only affects Standalone builds tiberian dawn Only affects the Tiberian Dawn builds

Comments

@TobiasKarnat
Copy link

Please consider this to fix skirmish in TD

diff -urN a/tiberiandawn/house.cpp b/tiberiandawn/house.cpp
--- a/tiberiandawn/house.cpp	2020-10-25 16:13:39.468969088 +0100
+++ b/tiberiandawn/house.cpp	2020-10-25 23:57:27.243747016 +0100
@@ -5541,11 +5543,11 @@
             maxaircraft /= enemycount;
         }
 
-        if (Control.MaxBuilding < (unsigned)maxbuilding + 10) {
-            Control.MaxBuilding = maxbuilding + 10;
+        if (Control.MaxBuilding < (unsigned)maxbuilding + 30) {
+            Control.MaxBuilding = maxbuilding + 30;
         }
-        if (Control.MaxUnit < (unsigned)maxunit + 10) {
-            Control.MaxUnit = maxunit + 10;
+        if (Control.MaxUnit < (unsigned)maxunit + 50) {
+            Control.MaxUnit = maxunit + 50;
         }
         if (Control.MaxInfantry < (unsigned)maxinfantry + 10) {
             Control.MaxInfantry = maxinfantry + 10;
@@ -5984,9 +5985,7 @@
                   {STRUCT_RADAR, URGENCY_MEDIUM},
                   {STRUCT_REPAIR, URGENCY_MEDIUM},
                   {STRUCT_OBELISK, URGENCY_HIGH},
-                  {STRUCT_TURRET, URGENCY_HIGH},
-                  {STRUCT_ATOWER, URGENCY_HIGH},
-                  {STRUCT_GTOWER, URGENCY_HIGH}};
+                  {STRUCT_ATOWER, URGENCY_HIGH}};
 
     /*
     **	Find a structure to sell and then sell it. Bail from further scanning until
@@ -6060,12 +6059,9 @@
                   {STRUCT_STORAGE, URGENCY_LOW},
                   {STRUCT_REPAIR, URGENCY_MEDIUM},
                   {STRUCT_OBELISK, URGENCY_HIGH},
-                  {STRUCT_TURRET, URGENCY_HIGH},
                   {STRUCT_ATOWER, URGENCY_HIGH},
-                  {STRUCT_GTOWER, URGENCY_HIGH},
                   {STRUCT_HELIPAD, URGENCY_MEDIUM},
                   {STRUCT_POWER, URGENCY_HIGH},
-                  {STRUCT_AIRSTRIP, URGENCY_HIGH},
                   {STRUCT_CONST, URGENCY_CRITICAL}};
 
     BuildingClass* b = 0;
@@ -6340,14 +6336,28 @@
         }
 
         /*
+        **	An airstrip would be good.
+        */
+        current = BQuantity[STRUCT_AIRSTRIP];
+        if (current < Round_Up(Rule.WarRatio * fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit) {
+            b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP);
+            if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
+                choiceptr = BuildChoice.Alloc();
+                if (choiceptr != NULL) {
+                    *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
+                }
+            }
+        }
+
+        /*
         **	Always build up some base defense.
         */
         // current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] +
         // BQuantity[STRUCT_FLAME_TURRET];
-        current = BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_OBELISK];
+        current = BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_GTOWER];
         if (current < Round_Up(Rule.DefenseRatio * fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) {
             // b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET);
-            b = &BuildingTypeClass::As_Reference(STRUCT_OBELISK);
+            b = &BuildingTypeClass::As_Reference(STRUCT_GTOWER);
             if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
                 choiceptr = BuildChoice.Alloc();
                 if (choiceptr != NULL) {
@@ -6479,31 +6489,44 @@
 #endif
 
     /*
-    **	A helipad would be good.
+    **	Advanced base defense would be good.
     */
-    current = BQuantity[STRUCT_HELIPAD];
-    if (current < Round_Up(Rule.HelipadRatio * fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) {
-        b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD);
-        if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
+    current = BQuantity[STRUCT_ATOWER] + BQuantity[STRUCT_OBELISK];
+    if (current < Round_Up(Rule.TeslaRatio * fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) {
+        b = &BuildingTypeClass::As_Reference(STRUCT_ATOWER);
+        if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 0x0100) {
             choiceptr = BuildChoice.Alloc();
             if (choiceptr != NULL) {
-                int threat_quantity = 0;
-                if (enemy != NULL) {
-                    threat_quantity = enemy->CurAircraft;
+                *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
+            }
+        } else {
+            b = &BuildingTypeClass::As_Reference(STRUCT_OBELISK);
+            if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 0x0100) {
+                choiceptr = BuildChoice.Alloc();
+                if (choiceptr != NULL) {
+                    *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
                 }
+            }
+        }
+    }
 
-                *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM,
-                                              b->Type);
+    // Build repair if GDI for Mammoth tanks
+    if (BQuantity[STRUCT_REPAIR] == 0) {
+        b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR);
+        if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && ActLike == HOUSE_GOOD) {
+            choiceptr = BuildChoice.Alloc();
+            if (choiceptr != NULL) {
+                *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type);
             }
         }
     }
 
     /*
-    **	An airstrip would be good.
+    **	A helipad would be good.
     */
-    current = BQuantity[STRUCT_AIRSTRIP];
-    if (current < Round_Up(Rule.AirstripRatio * fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) {
-        b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP);
+    current = BQuantity[STRUCT_HELIPAD];
+    if (current < Round_Up(Rule.HelipadRatio * fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) {
+        b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD);
         if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) {
             choiceptr = BuildChoice.Alloc();
             if (choiceptr != NULL) {
@@ -7062,7 +7085,7 @@
         if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HELICOPTER), ActLike) &&
             // AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel &&
             AircraftTypeClass::As_Reference(AIRCRAFT_HELICOPTER).Level <= (unsigned)BuildLevel
-            && BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_HELICOPTER] + AQuantity[AIRCRAFT_ORCA]) {
+            && BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_HELICOPTER] + AQuantity[AIRCRAFT_ORCA] + 1) {
             BuildAircraft = AIRCRAFT_HELICOPTER;
             return (TICKS_PER_SECOND);
         }
@@ -7070,7 +7093,7 @@
         if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_ORCA), ActLike) &&
             // AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel &&
             AircraftTypeClass::As_Reference(AIRCRAFT_ORCA).Level <= (unsigned)BuildLevel
-            && BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_ORCA] + AQuantity[AIRCRAFT_HELICOPTER]) {
+            && BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_ORCA] + AQuantity[AIRCRAFT_HELICOPTER] + 1) {
             BuildAircraft = AIRCRAFT_ORCA;
             return (TICKS_PER_SECOND);
         }
+#endif
diff -urN a/tiberiandawn/rules.cpp b/tiberiandawn/rules.cpp
--- a/tiberiandawn/rules.cpp	2020-10-25 16:13:39.478969300 +0100
+++ b/tiberiandawn/rules.cpp	2020-10-25 17:12:46.975529119 +0100
@@ -92,6 +92,8 @@
     , PowerEmergencyFraction(0x00C0)
     , HelipadRatio(0x1E)
     , HelipadLimit(5)
+    , TeslaRatio(0x0028)
+    , TeslaLimit(10)
     , AARatio(0x0024)
     , AALimit(10)
     , DefenseRatio(0x0066)

This on top would improve skirmish by randomizing building choice, prioritizing power and sell off later [TD].

diff -urN a/tiberiandawn/building.cpp b/tiberiandawn/building.cpp
--- a/tiberiandawn/building.cpp	2020-12-01 07:00:16.085406000 +0100
+++ b/tiberiandawn/building.cpp	2020-12-01 22:10:49.375598625 +0100
@@ -1192,7 +1192,7 @@
             if (House->Available_Money() >= REPAIR_THRESHHOLD) {
                 Repair(1);
             } else {
-                if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger) {
+                if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger && *this != STRUCT_CONST && Health_Ratio() < (unsigned)(0x0040)) {
                     if (GameToPlay != GAME_NORMAL || Scenario != 15 || PlayerPtr->ActLike != HOUSE_GOOD
                         || *this != STRUCT_TEMPLE) {
                         Sell_Back(1);

diff -urN a/redalert/house.cpp b/redalert/house.cpp
--- a/redalert/house.cpp	2020-10-27 11:45:30.892148194 +0100
+++ b/redalert/house.cpp	2020-10-27 14:29:29.591686676 +0100
@@ -5854,7 +5854,7 @@
         if (Can_Build(b, ActLike) && Power <= Drain + Rule.PowerSurplus && b->Cost_Of() < money) {
             choiceptr = BuildChoice.Alloc();
             if (choiceptr != NULL) {
-                *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
+                *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : Power < Drain ? URGENCY_CRITICAL : URGENCY_MEDIUM, b->Type);
             }
         } else {
             b = &BuildingTypeClass::As_Reference(STRUCT_POWER);
@@ -5862,7 +5862,7 @@
                 choiceptr = BuildChoice.Alloc();
                 if (choiceptr != NULL) {
                     *choiceptr =
-                        BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
+                        BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : Power < Drain ? URGENCY_CRITICAL : URGENCY_MEDIUM, b->Type);
                 }
             }
         }
@@ -6161,15 +6161,26 @@
         **	Pick the choice that is the most urgent.
         */
         UrgencyType best = URGENCY_NONE;
-        int bestindex;
+        DynamicVectorClass<int> bestindex;
         for (int index = 0; index < BuildChoice.Count(); index++) {
             if (BuildChoice.Ptr(index)->Urgency > best) {
-                bestindex = index;
+                bestindex.Clear();
+                bestindex.Add(index);
                 best = BuildChoice.Ptr(index)->Urgency;
             }
+            else if (BuildChoice.Ptr(index)->Urgency == best) {
+                bestindex.Add(index);
+            }
         }
+
+        int choice = 0;
+
+        if (bestindex.Count() > 1) {
+            choice = Random_Pick(0, bestindex.Count()-1);
+        }
+
         if (best != URGENCY_NONE) {
-            BuildStructure = BuildChoice.Ptr(bestindex)->Structure;
+            BuildStructure = BuildChoice.Ptr(bestindex[choice])->Structure;
         }
     }
 
diff -urN a/tiberiandawn/house.cpp b/tiberiandawn/house.cpp
--- a/tiberiandawn/house.cpp	2020-10-27 11:45:30.904148400 +0100
+++ b/tiberiandawn/house.cpp	2020-10-27 13:51:03.512931501 +0100
@@ -6321,7 +6321,7 @@
         if (Can_Build(b, ActLike) && Power <= Drain + Rule.PowerSurplus && b->Cost_Of() < money) {
             choiceptr = BuildChoice.Alloc();
             if (choiceptr != NULL) {
-                *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
+                *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : Power < Drain ? URGENCY_CRITICAL : URGENCY_MEDIUM, b->Type);
             }
         } else {
             b = &BuildingTypeClass::As_Reference(STRUCT_POWER);
@@ -6329,7 +6329,7 @@
                 choiceptr = BuildChoice.Alloc();
                 if (choiceptr != NULL) {
                     *choiceptr =
-                        BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type);
+                        BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : Power < Drain ? URGENCY_CRITICAL : URGENCY_MEDIUM, b->Type);
                 }
             }
         }
@@ -6630,15 +6630,26 @@
     **	Pick the choice that is the most urgent.
     */
     UrgencyType best = URGENCY_NONE;
-    int bestindex;
+    DynamicVectorClass<int> bestindex;
     for (int index = 0; index < BuildChoice.Count(); index++) {
         if (BuildChoice.Ptr(index)->Urgency > best) {
-            bestindex = index;
+            bestindex.Clear();
+            bestindex.Add(index);
             best = BuildChoice.Ptr(index)->Urgency;
         }
+        else if (BuildChoice.Ptr(index)->Urgency == best) {
+            bestindex.Add(index);
+        }
     }
+
+    int choice = 0;
+
+    if (bestindex.Count() > 1) {
+        choice = Random_Pick(0, bestindex.Count()-1);
+    }
+
     if (best != URGENCY_NONE) {
-        BuildStructure = BuildChoice.Ptr(bestindex)->Structure;
+        BuildStructure = BuildChoice.Ptr(bestindex[choice])->Structure;
     }
 }
@TobiasKarnat
Copy link
Author

This shuffles build units around the base which gives AI more space for buildings and reduces risk that unit blocks refinery, by screaming_chicken (more simplified).

These two lines really change a lot for better gameplay :D

--- a/tiberiandawn/unit.cpp	2021-01-12 21:44:53.699981269 +0100
+++ b/tiberiandawn/unit.cpp	2021-01-13 00:30:04.497336061 +0100
@@ -1352,6 +1352,9 @@ void UnitClass::Enter_Idle_Mode(bool ini
                     }
 
 #endif
+                    if (initial && Frame > 1000) {
+                        this->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass*)this));
+                    }
                     //} else {
                     //	order = MISSION_HUNT;
                     //}

@TobiasKarnat
Copy link
Author

Zero initialize CurAircraft, CurInfantry, MaxAircraft, MaxInfantry and fill in values for MaxAircraft, MaxInfantry.

diff -urN a/tiberiandawn/defines.h b/tiberiandawn/defines.h
--- a/tiberiandawn/defines.h	2021-01-13 14:40:22.327308208 +0100
+++ b/tiberiandawn/defines.h	2021-01-13 15:26:14.004442791 +0100
@@ -1978,7 +1978,9 @@
 #define CONQUER_PATH_MAX 9 // Number of cells to look ahead for movement.
 
 #define EACH_UNIT_MAX     (UNIT_MAX / 4)     // Default maximum any one player can have.
+#define EACH_INFANTRY_MAX (INFANTRY_MAX / 4) // Default maximum any one player can have.
 #define EACH_BUILDING_MAX (BUILDING_MAX / 4) // Default maximum any one player can build.
+#define EACH_AIRCRAFT_MAX (AIRCRAFT_MAX / 4) // Default maximum any one player can have.
 
 /**********************************************************************
 **	Terrain can be of these different classes. At any point in the game
diff -urN a/tiberiandawn/house.cpp b/tiberiandawn/house.cpp
--- a/tiberiandawn/house.cpp	2021-01-13 14:40:22.327308208 +0100
+++ b/tiberiandawn/house.cpp	2021-01-13 16:17:51.838944313 +0100
@@ -397,7 +397,9 @@
     Capacity = 0;
     Credits = 0;
     CreditsSpent = 0;
+    CurAircraft = 0;
     CurBuildings = 0;
+    CurInfantry = 0;
     CurUnits = 0;
     DamageTime = DAMAGE_DELAY;
     Drain = 0;
@@ -424,7 +426,9 @@
     IsToLose = false;
     IsToWin = false;
     Make_Ally(house);
+    MaxAircraft = 0;
     MaxBuilding = 0;
+    MaxInfantry = 0;
     MaxUnit = 0;
     NewAScan = 0;
     NewBScan = 0;
@@ -1901,15 +1905,25 @@
 
         maxunit = MAX(maxunit, 150);
 
+        int maxinfantry = ini.Get_Int(hname, "MaxInfantry", EACH_INFANTRY_MAX);
+
+        maxinfantry = MAX(maxinfantry, 150);
+
         int maxbuilding = ini.Get_Int(hname, "MaxBuilding", EACH_BUILDING_MAX);
 
         maxbuilding = MAX(maxbuilding, 150);
 
+        int maxaircraft = ini.Get_Int(hname, "MaxAircraft", EACH_AIRCRAFT_MAX);
+
+        maxaircraft = MAX(maxaircraft, 50);
+
         int credits = ini.Get_Int(hname, "Credits", 0);
 
         p = new HouseClass(index);
 
+        p->MaxAircraft = maxaircraft;
         p->MaxBuilding = maxbuilding;
+        p->MaxInfantry = maxinfantry;
         p->MaxUnit = maxunit;
         p->Credits = (long)credits * 100;
         p->InitialCredits = p->Credits;
@@ -1964,7 +1978,9 @@
             ini.Put_Int(p->Class->IniName, "Credits", (int)(p->Credits / 100));
             ini.Put_SourceType(p->Class->IniName, "Edge", p->Edge);
             ini.Put_Int(p->Class->IniName, "MaxUnit", p->MaxUnit);
+            ini.Put_Int(p->Class->IniName, "MaxInfantry", p->MaxInfantry);
             ini.Put_Int(p->Class->IniName, "MaxBuilding", p->MaxBuilding);
+            ini.Put_Int(p->Class->IniName, "MaxAircraft", p->MaxAircraft);
 
             bool first = true;
             char sbuffer[100] = "";

@hifi
Copy link
Collaborator

hifi commented Jan 26, 2021

Is still still relevant after we merged skirmish support?

@hifi hifi added standalone Only affects Standalone builds tiberian dawn Only affects the Tiberian Dawn builds labels Jan 26, 2021
@Gerwin2k
Copy link

There are many different tweaks in this issue, so I can't speak for everything, but did find:

  • AI NOD bases currently already spawn an Airfield to buy vehicles. Regardless of the patches here.
  • AI GDI Bases currently don't spawn base defenses, like guard towers. With the patches here they do.
  • Both AI sides currently prioritize helicopters over vehicles. With the patches here they prioritize vehicles first.

@TobiasKarnat
Copy link
Author

TobiasKarnat commented Jan 26, 2021

Is still still relevant after we merged skirmish support?

Yes in any case! The skirmish support only enabled the badly ported RA skrimish, it doesn't take into account the TD characteristics.

The first patch is the most important, fixes bugs:

  • As the resources are even more limited in TD than in RA, building of buildings and units get prioritized over infantry and aircraft units.
  • Base defense is not sold if there is limited power as in RA. Advance base defense can be sold.
  • Base and the airstrip (here = the weapon factory) is not sold if there is limited money as in RA. Advance base defense can be sold.
  • The airstrip is build earlier (higher priority) as the weapon factory in RA and only 2 (Rule.WarLimit) are build.
    It is not handled like the airstrip in RA which allows building of 5 (Rule.AirstripLimit) and is almost last to consider.
  • Guard towers are enabled for GDI and build in the same quantity as turrets. Same as in RA where flame vs Pillbox/turrets.
  • Advanced guard towers are enabled for GDI and build in the quantity as obelisks, it gets limited by the ported Rule.TeslaLimit from RA.
  • Service depot are enabled for GDI to empower the AI to build mammoth tanks.
  • Helipads get build at last and in priority as in RA.

The second patch should be guarded by a "new features" option:

  • Sell off of buildings behave like in RA only if there red health and the construction yard is never sold if damaged.
  • Building order is completely randomized in RA and TD skirmish so the AI less predictive and the priority is more important-
  • As the building order is random now, if there is less power than drain the urgency of building a power plant is now URGENCY_CRITICAL instead of URGENCY_MEDIUM (which almost everything else is).

The third patch makes units get moved after build:

  • Units get shuffled around the base after being build as in RA, but also because of the limited space between the buildings in TD the danger is high to block refineries entries.

The currently last patch zero initializes forgotten variables (which are actually evaluated) and reads in minimum values as in RA.

@Gerwin2k
Copy link

It improved things for sure, but I suppose there is still much more tuning to do. In that regard, are there debug/cheat options to better observe what the Computer Opponents are doing, without the hassle of trying to keep your own forces alive? If not, I would suggest such to be added. Like for example in OpenRA debug mode: See whole map, add credits, unlimited electric power, instant build speed, etc.

@TobiasKarnat
Copy link
Author

I think the first step is to bring TD skirmish to the same level as RA skrimish.
But we must make sure that it stays classic and doesn't feel like OpenRA.
Some QoL improvements for RA would be nice to have as well.

Regarding debug: I just quickly uncover the enemy base from start, but someone has implemented NO_SHROUD (haven't tested).
https://github.com/screamingchicken/CnC_Remastered_Collection/commits/Debug

@Gerwin2k
Copy link

To put in perspective: I made I classic mod for OpenRA once, like 5 years ago, the debug options there saved me a lot of time, tuning and debugging it. That is all I am saying.

I would welcome any (optional) enhancement to the computer opponents, but I understand at some point it is better suited to do that in a possible non-vanilla fork.

I remember the Red Alert Skirmish AI to be typical in becoming passive halfway through the game. Failing to resume harvesting after an interruption. I would call that a bug though, and not classic desirable behavior. Whatever classic desirable behavior may be...

@Gerwin2k
Copy link

Gerwin2k commented Mar 13, 2021

Just noticed there are already debug functions in debug.cpp. I don't know how to access those, but it is easy to move this bit to the main keyboard loop in conquer.cpp, around line 500. Then the END key can toggle shroud on/off.

 case KN_END: 
   Debug_Unshroud = (Debug_Unshroud == false);
   Map.Flag_To_Redraw(true);
 break;

I am not saying it should be in the normal code repository here, but I can add that bit to my own builds for testing.

Still on the wishlist is a debug cheat to have at least one player unit invisible or invincible.

PS the above is in VanillaTD, have not tried it with VanillaRA yet.

@giulianobelinassi
Copy link
Collaborator

I am willing to merge this patch with some changes. Please acknowledge if you agree or not with me:

  1. GDI should avoid building Guard Towers at all if Adv. Guard Towers are available. Guard Towers are very weak when compared to turrets and Adv. Guard Tower do not consume a lot of power.
  2. Nod should build less obelisks and prioritize turrets. They consume a lot of power and a turret spam is quite effective.
  3. The AI should prioritize refineries on early game.
  4. Armor and damage bump should be reduced on HARD.
  5. Nod should be able to build helipads. Probably the Can_Build() returns false for helipads if house is acting like Nod.

@Gerwin2k
Copy link

Glad to see the intention to improve Skirmish AI.
My answers:

  1. For the single player skirmish games I do see a purpose for Guard Towers. Contrary to Red Alert, engineers can capture undamaged buildings in VanillaTD, and guard towers are a sure way to block engineer access. Also they keep working when power is cut.
  2. Less obelisks, sure. I have seen several skirmish AI cutting their own power by building obelisks. Which is like sabotaging themselves.
  3. I found that giving the modded skirmish AI a lot of starting cash (more then default) gets them going quite well. In that case they build strong bases with up to three refineries. With the default starting cash they are slow to build-up their base, or seem forever content with a small base...
  4. I only ever tested Normal difficulty. No opinion on HARD.
  5. NOD helipads yes. The Skirmish patch here adds that.

WIN32 example build with slightly rebased code:
http://gb-homepage.nl/download/temp/VanillaTD-skirmishmod+crashfix+F4shroud.zip

  • I think the VanillaTD codebase used is from 01-2022. (I am not home now..)
  • Does contain this TobiasKarnat diffs for skirmish, adapted to 01-2022.
  • Did fix the nasty crash issue, as recently pointed out in issue 837 by ChthonVII.
  • Has the above mentioned cheat (key F4) so one can actually remove shroud and inspect the skirmish AI.

@giulianobelinassi
Copy link
Collaborator

Actually, hard difficulty is broken. The player do almost no damage to enemy units.

Can you link the codebase with your changes?

@Gerwin2k
Copy link

I am at work, and only had some backups on my USB. The zip file I linked to has the six changed .cpp files included. I will upload my whole source when I am back home...

@Gerwin2k
Copy link

The file I linked to yesterday, is now updated to contain the full source code. Based on SVN edb8391 from 19-12-2022. Any file with a later modification date was modded by me, modifications tagged with comment "GB 2022" or "TobiasKarnat". Mostly for skirmish AI.

Another subject to take into account here: Skirmish gameflow may differ in relation to map size. Of course the skirmish AI should function on the original C&C multiplayer maps, But skirmish can be a lot more interesting and less cramped with the Vanilla conquer MegaMap support. AFAIK the usual trick is to adjust some Sole Survivor maps for this purpose. But this complicates the license, so maybe I should make a MegaMap from scratch...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
standalone Only affects Standalone builds tiberian dawn Only affects the Tiberian Dawn builds
Projects
None yet
Development

No branches or pull requests

4 participants