Author Topic: Modding dyson spheres, with code samples.  (Read 11272 times)

Offline BadgerBadger

  • Arcen Volunteer
  • Hero Member Mark III
  • *****
  • Posts: 1,229
  • BadgerBadgerBadgerBadger
Modding dyson spheres, with code samples.
« on: June 20, 2017, 10:20:50 pm »
I am hoping I'll be able to mod a galaxy to be filled with Dyson Spheres, and then let them go to war with 4 Devourer Golems.

Offline TheVampire100

  • Master Member
  • *****
  • Posts: 1,382
  • Ordinary Vampire
Re: Modding dyson spheres, with code samples.
« Reply #1 on: June 20, 2017, 11:48:23 pm »
Dyson Spheres produce an endless amount of units. Devourer Golems eat an endless amount of units. Sounds to me like a stalemate for eternity.

https://youtu.be/C8M7HopGZX4?t=1m59s

Offline keith.lamothe

  • Arcen Games Staff
  • Arcen Staff
  • Zenith Council Member Mark III
  • *****
  • Posts: 19,505
Re: Modding dyson spheres, with code samples.
« Reply #2 on: June 21, 2017, 08:14:36 am »
I am hoping I'll be able to mod a galaxy to be filled with Dyson Spheres, and then let them go to war with 4 Devourer Golems.
Depending on what you mean by "go to war" that should be relatively easy for someone already used to the ExternalCode.

Here's the (in-progress) dyson sphere faction:

Code: [Select]
using Arcen.AIW2.Core;
using System;
using System.Collections.Generic;
using System.Text;
using Arcen.Universal;

namespace Arcen.AIW2.External
{
    public class SpecialFaction_DysonSphere : ISpecialFactionImplementation
    {
        public static SpecialFaction_DysonSphere Instance;
        public SpecialFaction_DysonSphere() { Instance = this; }

        public static readonly string DYSON_SPHERE_TAG = "Dyson";

        public void SetStartingSideRelationships( WorldSide side )
        {
            for ( int i = 0; i < World_AIW2.Instance.Sides.Count; i++ )
            {
                WorldSide otherSide = World_AIW2.Instance.Sides[i];
                if ( side == otherSide )
                    continue;
                switch ( otherSide.Type )
                {
                    case WorldSideType.Player:
                    case WorldSideType.AI:
                    case WorldSideType.SpecialFaction:
                        side.MakeHostileTo( otherSide );
                        otherSide.MakeHostileTo( side );
                        break;
                }
            }
        }

        public ArcenEnumIndexedArray_AIBudgetType<FInt> GetSpendingRatios( WorldSide side )
        {
            ArcenEnumIndexedArray_AIBudgetType<FInt> result = new ArcenEnumIndexedArray_AIBudgetType<FInt>();

            result[AIBudgetType.Reinforcement] = FInt.One;

            return result;
        }

        public bool GetShouldAttackNormallyExcludedTarget( WorldSide side, GameEntity Target )
        {
            return false;
        }

        public void SeedStartingEntities( WorldSide side, Galaxy galaxy, ArcenSimContext Context, MapTypeData mapType )
        {
            galaxy.Mapgen_SeedSpecialEntities( Context, side, DYSON_SPHERE_TAG, 1 );
        }

        public void DoLongRangePlanning( WorldSide side, ArcenLongTermPlanningContext Context )
        {
            side.Entities.DoForEntities( GameEntityCategory.Ship, delegate ( GameEntity entity )
            {
                if ( entity.LongRangePlanningData == null )
                    return DelReturn.Continue; // if created after the start of this planning cycle, skip

                Planet planet = World_AIW2.Instance.GetPlanetByIndex( entity.LongRangePlanningData.CurrentPlanetIndex );
                if ( entity.TypeData.GetHasTag( DYSON_SPHERE_TAG ) )
                {
                    // the sphere itself
                }
                else
                {
                    // something the sphere spawned
                }
                return DelReturn.Continue;
            } );
        }
    }
}

(the dyson's unit production is defined in xml, like other ships that produce internal "drones" and release them when threatened, though the release conditions may be changed)


And here's the Devourer (done for now, but a bit simplistic) :

Code: [Select]
using Arcen.AIW2.Core;
using System;
using System.Collections.Generic;
using System.Text;
using Arcen.Universal;

namespace Arcen.AIW2.External
{
    public class SpecialFaction_Devourer : ISpecialFactionImplementation
    {
        public static SpecialFaction_Devourer Instance;
        public SpecialFaction_Devourer() { Instance = this; }

        public static readonly string DEVOURER_TAG = "Devourer";

        public void SetStartingSideRelationships( WorldSide side )
        {
            for ( int i = 0; i < World_AIW2.Instance.Sides.Count; i++ )
            {
                WorldSide otherSide = World_AIW2.Instance.Sides[i];
                if ( side == otherSide )
                    continue;
                switch ( otherSide.Type )
                {
                    case WorldSideType.Player:
                    case WorldSideType.AI:
                    case WorldSideType.SpecialFaction:
                        side.MakeHostileTo( otherSide );
                        otherSide.MakeHostileTo( side );
                        break;
                }
            }
        }

        public ArcenEnumIndexedArray_AIBudgetType<FInt> GetSpendingRatios( WorldSide side )
        {
            ArcenEnumIndexedArray_AIBudgetType<FInt> result = new ArcenEnumIndexedArray_AIBudgetType<FInt>();

            result[AIBudgetType.Reinforcement] = FInt.One;

            return result;
        }

        public bool GetShouldAttackNormallyExcludedTarget( WorldSide side, GameEntity Target )
        {
            return false;
        }

        public void SeedStartingEntities( WorldSide side, Galaxy galaxy, ArcenSimContext Context, MapTypeData mapType )
        {
            galaxy.Mapgen_SeedSpecialEntities( Context, side, DEVOURER_TAG, 1 );
        }

        public void DoLongRangePlanning( WorldSide side, ArcenLongTermPlanningContext Context )
        {
            Galaxy galaxy = World_AIW2.Instance.SetOfGalaxies.Galaxies[0];

            side.Entities.DoForEntities( GameEntityCategory.Ship, delegate ( GameEntity entity )
            {
                if ( entity.LongRangePlanningData == null )
                    return DelReturn.Continue; // if created after the start of this planning cycle, skip

                if ( !entity.TypeData.GetHasTag( DEVOURER_TAG ) )
                    return DelReturn.Continue; // if not the Big D, skip (shouldn't happen, but if somebody mods in a reclamator gun for the thing, etc)

                if ( entity.LongRangePlanningData.FinalDestinationPlanetIndex != -1 &&
                     entity.LongRangePlanningData.FinalDestinationPlanetIndex != entity.LongRangePlanningData.CurrentPlanetIndex )
                    return DelReturn.Continue; // if heading somewhere else, skip

                Planet planet = World_AIW2.Instance.GetPlanetByIndex( entity.LongRangePlanningData.CurrentPlanetIndex );

                List<GameEntity> threatShipsNotAssignedElsewhere = new List<GameEntity>();
                threatShipsNotAssignedElsewhere.Add( entity );
                Helper_SendThreatOnRaid( threatShipsNotAssignedElsewhere, side, galaxy, planet, Context );

                return DelReturn.Continue;
            } );
        }

        private void Helper_SendThreatOnRaid( List<GameEntity> threatShipsNotAssignedElsewhere, WorldSide worldSide, Galaxy galaxy, Planet planet, ArcenLongTermPlanningContext Context )
        {
            List<Planet> potentialAttackTargets = new List<Planet>();
            List<Planet> planetsToCheckInFlood = new List<Planet>();
            planetsToCheckInFlood.Add( planet );
            planet.AIPlanning_CheapestRaidPathToHereComesFrom = planet;
            for ( int k = 0; k < planetsToCheckInFlood.Count; k++ )
            {
                Planet floodPlanet = planetsToCheckInFlood[k];
                floodPlanet.DoForLinkedNeighbors( delegate ( Planet neighbor )
                {
                    FInt totalCostFromOriginToNeighbor = floodPlanet.AIPlanning_CheapestRaidPathToHereCost + 1;
                    if ( !potentialAttackTargets.Contains( neighbor ) )
                        potentialAttackTargets.Add( neighbor );
                    if ( neighbor.AIPlanning_CheapestRaidPathToHereComesFrom != null &&
                         neighbor.AIPlanning_CheapestRaidPathToHereCost <= totalCostFromOriginToNeighbor )
                        return DelReturn.Continue;
                    neighbor.AIPlanning_CheapestRaidPathToHereComesFrom = floodPlanet;
                    neighbor.AIPlanning_CheapestRaidPathToHereCost = totalCostFromOriginToNeighbor;
                    planetsToCheckInFlood.Add( neighbor );
                    return DelReturn.Continue;
                } );
            }
            if ( potentialAttackTargets.Count <= 0 )
                return;

            Planet threatTarget = potentialAttackTargets[Context.QualityRandom.Next( 0, potentialAttackTargets.Count )];

            List<Planet> path = new List<Planet>();
            Planet workingPlanet = threatTarget;
            while ( workingPlanet != planet )
            {
                path.Insert( 0, workingPlanet );
                workingPlanet = workingPlanet.AIPlanning_CheapestRaidPathToHereComesFrom;
            }
            if ( path.Count > 0 )
            {
                GameCommand command = GameCommand.Create( GameCommandType.SetWormholePath );
                for ( int k = 0; k < threatShipsNotAssignedElsewhere.Count; k++ )
                    command.RelatedEntityIDs.Add( threatShipsNotAssignedElsewhere[k].PrimaryKeyID );
                for ( int k = 0; k < path.Count; k++ )
                    command.RelatedPlanetIndices.Add( path[k].PlanetIndex );
                Context.QueueCommandForSendingAtEndOfContext( command );
            }
        }
    }
}

The threat code for the Devourer is more complex than necessary because it's actually a stripped down version of the AI's general-purpose threat code.

So you can change how many are seeded, by just changing the last parameter of the Mapgen_SeedSpecialEntities calls. That will tend to distribute them evenly across the galaxy and at some distance away from the AI and Human homeworlds. If you just want to put one on each planet (for the dysons) you could loop over galaxy.Planets; I forget the call to seed directly from there but I think you have an example somewhere in the mapgen code. I recommend against putting a dyson on the human starting planet :) But no reason it wouldn't function, that I know of.

As far as them going to war on each other, their stuff should automatically shoot at each other due to their SetStartingSideRelationships implementations (namely: hostile to every side except self; incidentally, hostile-to-self doesn't work consistently).

They would not seek each other out, but you could put something in the "// something the sphere spawned" branch of the Dyson faction's "DoLongRangePlanning" to make its ships into Devourer-hunters. Basically pull up the Devourer's WorldSide object, side.DoForEntities() and check for the Devourer tag to get your target(s), then decide if you want them to just bum-rush the nearest one, or pool near a sphere until they have a total strength over some threshold first, etc.

And the Devourers could have their DoLongRangePlanning similarly altered to go after planets with Spheres on them.

Those directives would basically only get the respective units to the target planets. From there the lower level ship-control code is supposed to tell it what point to move towards and what things to shoot at. That said, I don't think there's anything that would stop a GameCommandType.Attack from working.
Have ideas or bug reports for one of our games? Mantis for Suggestions and Bug Reports. Thanks for helping to make our games better!

Offline BadgerBadger

  • Arcen Volunteer
  • Hero Member Mark III
  • *****
  • Posts: 1,229
  • BadgerBadgerBadgerBadger
Re: Modding dyson spheres, with code samples.
« Reply #3 on: June 21, 2017, 09:19:18 am »
That's some excellent insight into how to do generic AI modding as well. Thanks!

Offline BadgerBadger

  • Arcen Volunteer
  • Hero Member Mark III
  • *****
  • Posts: 1,229
  • BadgerBadgerBadgerBadger
Re: Modding dyson spheres, with code samples.
« Reply #4 on: June 21, 2017, 10:53:47 am »
So if I wanted to force, say, every cluster in a Clusters-style map to have a dyson sphere, I'd need to have my mapgen override the SeedSpecialEntities function in Mapgen_Base (I'd cut and paste it entirely except for the changes I'm making). Then I'd use the Galaxy planet list and my knowledge of the ordering of the planet list (lets say that planets 0-10, 11-20, 21-30.... are in their own clusters) to make sure that at least one planet in each cluster has a Dyson Sphere by calling planet.Mapgen_SeedAIEntity()?

Offline keith.lamothe

  • Arcen Games Staff
  • Arcen Staff
  • Zenith Council Member Mark III
  • *****
  • Posts: 19,505
Re: Modding dyson spheres, with code samples.
« Reply #5 on: June 21, 2017, 03:35:44 pm »
So if I wanted to force, say, every cluster in a Clusters-style map to have a dyson sphere
Bear in mind that seeding the unit is different from having the Dyson Sphere "faction" enabled. So the unit could exist, on some other side, but not have the faction's behavior. You could mod the dyson faction to be "always_enabled" or whatever it is, which is how the zombie factions are done: they're always "on", but they only get units if the botnet or whatever causes them to come into being.

Quote
I'd need to have my mapgen override the SeedSpecialEntities function in Mapgen_Base (I'd cut and paste it entirely except for the changes I'm making).
As an aside, why copy and paste instead of calling base.SeedSpecialEntities followed by your custom logic?

But more directly: it's better to do this in the faction's SeedStartingEntities method, switching on World_AIW2.Instance.Setup.MapType.InternalName=="Clusters" (or whatever it is; you could also do a type-check since you have the types in the external dll).

Quote
Then I'd use the Galaxy planet list and my knowledge of the ordering of the planet list (lets say that planets 0-10, 11-20, 21-30.... are in their own clusters)
Feel free to put your own "static Dictionary<int,BadgersExtraClustersPlanetData> ExtraData" in the clusters mapgen class or whatever, and:
- clear it at the start of Generate()
- when you want to remember something mapgen-related about a planet, stick it in ExtraData[planet.PlanetIndex]
- in your faction SeedStartingEntities, when looking at a planet, you can do if(ExtraData.GetHasKey(planet.PlanetIndex) && ExtraData[planet.PlanetIndex].ClusterIndex == whatever) and use that data.

You could also use a more directly-to-the-point data structure of List<List<int>> PlanetIDsInClusters, etc. My point is that you aren't restricted by the object structure of the Core DLL in how your external classes communicate with one another.

Quote
to make sure that at least one planet in each cluster has a Dyson Sphere by calling planet.Mapgen_SeedAIEntity()?
You wouldn't want to seed it as an AI entity, but rather one of the dyson faction's WorldSide. But other than that, yes, you could do that.
Have ideas or bug reports for one of our games? Mantis for Suggestions and Bug Reports. Thanks for helping to make our games better!

Offline Draco18s

  • Resident Velociraptor
  • Core Member Mark V
  • *****
  • Posts: 4,251
Re: Modding dyson spheres, with code samples.
« Reply #6 on: June 21, 2017, 03:45:56 pm »
I am note having terrible thoughts about seeing dyson spheres on sides They Should Not Be On. Like player, AI, and zombie......

Offline Toranth

  • Hero Member Mark III
  • *****
  • Posts: 1,244
Re: Modding dyson spheres, with code samples.
« Reply #7 on: June 21, 2017, 03:56:13 pm »
I am note having terrible thoughts about seeing dyson spheres on sides They Should Not Be On. Like player, AI, and zombie......
Zombie Spheres, Mmmmmm..... sounds wonderful.  Made from two hemispheres!

Offline keith.lamothe

  • Arcen Games Staff
  • Arcen Staff
  • Zenith Council Member Mark III
  • *****
  • Posts: 19,505
Re: Modding dyson spheres, with code samples.
« Reply #8 on: June 22, 2017, 12:48:25 pm »
Well, for that matter there's no particular reason you can't mod it to spawn a Dyson Sphere as your Ark, you just wouldn't be able to move to different planets.

Unless you put engines on a Dyson Sphere. That doesn't sound like a good idea.
Have ideas or bug reports for one of our games? Mantis for Suggestions and Bug Reports. Thanks for helping to make our games better!

Offline Draco18s

  • Resident Velociraptor
  • Core Member Mark V
  • *****
  • Posts: 4,251
Re: Modding dyson spheres, with code samples.
« Reply #9 on: June 22, 2017, 03:32:00 pm »
Unless you put engines on a Dyson Sphere. That doesn't sound like a good idea.

No no, that sounds like the best idea.

Offline TheVampire100

  • Master Member
  • *****
  • Posts: 1,382
  • Ordinary Vampire
Re: Modding dyson spheres, with code samples.
« Reply #10 on: June 22, 2017, 05:46:52 pm »
Uhm, it's a freaking star, that would mean you would drag the entire solar system with it, that is bound to it.

Offline BadgerBadger

  • Arcen Volunteer
  • Hero Member Mark III
  • *****
  • Posts: 1,229
  • BadgerBadgerBadgerBadger
Re: Modding dyson spheres, with code samples.
« Reply #11 on: June 22, 2017, 05:52:12 pm »
Don't tell Physics, we might get in trouble!

Offline Pumpkin

  • Hero Member Mark III
  • *****
  • Posts: 1,201
  • Neinzul Gardener Enclave
Re: Modding dyson spheres, with code samples.
« Reply #12 on: July 06, 2017, 07:41:16 am »
Hello. I'm just passing by and would like to add an idea I discussed with a friend during a (rather) recent AIWC campaign.

My main issue with the Dyson Sphere is not a technical one (gameplay-wise, I love it), but an aesthetic one: it's too small. My take would be to change the background planet to BE the Dyson Sphere. (A full-scale star encased in a gigantic structure, remember? :P) The gatlings would be produced by an orbital factory, occupying the place of the Sphere in AIWC.

My question is: how (I'm)possible is it? Can a mod or minor faction access and modify the background planet? Would it cause performance issue? (The background planets are pretty nice in AIW2, with clouds and lightning storms.)
Please excuse my english: I'm not a native speaker. Don't hesitate to correct me.

Offline keith.lamothe

  • Arcen Games Staff
  • Arcen Staff
  • Zenith Council Member Mark III
  • *****
  • Posts: 19,505
Re: Modding dyson spheres, with code samples.
« Reply #13 on: July 06, 2017, 06:29:05 pm »
I have very little idea what's involved in modding in new planet models/shaders/etc. What I do know is you'd have to add a row to the PlanetDefinition xml data.

Then I guess your minor faction code could set thisPlanet.PlanetDefinition = PlanetDefinitionTable.Instance.GetRowByName("DysonBackgroundPlanet"); , or whatever you call it.

Whether that would work, changing it during the game, I dunno.
Have ideas or bug reports for one of our games? Mantis for Suggestions and Bug Reports. Thanks for helping to make our games better!

Offline Dominus Arbitrationis

  • Arcen Games Contractor
  • Arcen Staff
  • Sr. Member Mark III
  • *****
  • Posts: 479
Re: Modding dyson spheres, with code samples.
« Reply #14 on: July 07, 2017, 06:52:58 pm »
Hello. I'm just passing by and would like to add an idea I discussed with a friend during a (rather) recent AIWC campaign.

My main issue with the Dyson Sphere is not a technical one (gameplay-wise, I love it), but an aesthetic one: it's too small. My take would be to change the background planet to BE the Dyson Sphere. (A full-scale star encased in a gigantic structure, remember? :P) The gatlings would be produced by an orbital factory, occupying the place of the Sphere in AIWC.

My question is: how (I'm)possible is it? Can a mod or minor faction access and modify the background planet? Would it cause performance issue? (The background planets are pretty nice in AIW2, with clouds and lightning storms.)

What about a unit the size of a the entire playable area, that slowly moves into and out of the map (Pick a random edge instead of a wormhole possibly?)
Come help out at the Wiki!

Have ideas or bug reports for one of Arcen's games or any part of the site? Use  Mantis for Suggestions and Bug Reports. Thanks for helping to make our games and site better!