Duskers

Duskers

View Stats:
vindicar Nov 19, 2017 @ 5:23am
Modding - How to do it yourself
So mod request is a popular one, and the developer has stated that "it was too much work" to add modding support. However, all is not lost - the game seems to be written in C# with .NET support, which means game logic is stored in .NET assemblies. And now we have some wonderful tools - ILSPy[ilspy.net] and dnSpy[github.com] - that allow us to decompile and edit assemblies. ILSpy is better for analyzing assemblies, and dnSpy can edit them or export them into Visual Studio projects.

Before we start: always make a backup of any file you're editing! You can also rely on Steam validation to reacquire unmodded version.

Assemblies we're interested in are stored in steamapps\common\Duskers\Duskers_Data\Managed. Specifically we need to take a look at Assembly-CSharp.dll, as it seems to contain most of the game logic. For example, let's rework the way drone upgrades work - have them lose durability per activation, not per mission, but at reduced cost per activation (10% of normal cost).

In the main namespace "{}" of Assembly-CSharp.dll we can find a lot of classes. How do we find which one we need? First, we can start by looking at class names. If you click on "{}" in the tree view on the left, you will see a list of classes on the right. Searching (Ctrl-F) for substring "Upgrade" leads us to class BaseDroneUpgrade.

This class has some potential, since (as its name implies) it's a base class that defines behaviour common for all upgrades. Let's search for substring "Break" - this shows us several properties, like BreakTime or BreakProbability, and methods, like UpgradeBreakFactor() and Break(). The latter is interesting, since it handles upgrade entering broken state. Let's see what can provoke it by looking for "Break()" (method has no parameterers). Or in ILSpy we can right-click the method name and choose "Analyze" - in the bottom window you will see "Used By" list, showing where this method is called. We can see 4 methods: BaseDroneUpgrade.ReduceQuality(), Drone.AddDroneUpgrade(), DungeonManager.BeginExit() and ShieldUpgrade.OnUpdate(). We can view those methods and find where Break() is mentioned.
The first method is fairly trivial, putting upgrade into "Errors Detected" state and then into "Broken" state.
The second method shows a (seemingly impossible) chance that upgrade will break when being installed.
The fourth method ensures that shield upgrade always breaks on a dead drone.
And the third, DungeonManager.BeginExit(), is the most interesting - it seems to handle the end of the mission and calculation of mission stats.

In the middle of that method we can find this snippet:
foreach (Drone current in DroneManager.Instance.dronesList) { if (current != null) { foreach (BaseDroneUpgrade current2 in current.Upgrades) {
It seems to be iterating over every upgrade of every drone. Then the game checks 1) if the upgrade is installed, 2) if it's been used this mission and 3) if the upgrade is not broken already. If all condition are true, it increments mission counter, checks if current break probability is more than 15%, and if it is, rolls the dice to see if the upgrade broke. If it didn't break (flag4 is not set), it performs some kind of calculation, depending on whether the tutorial is being played, difficulty level, upgrade robustness (value returned by aforementioned UpgradeBreakFactor()) and random chance. The resulting value is used to increment the breaking chance.

So now we know how the game handles upgrades wear and tear, let's see about changing it!
Find DungeonManager.BeginExit() in dnSpy, right-click the method name, and choose "Edit method(C#)" or press Ctrl+Shift+E.
For our example, we should remove part of the body of that inner foreach loop that recalculates break probability (the entire conditional operator if (!flag4) { ... }). Let's cut it and paste it in a text file just in case. Press "Compile" on the bottom right and the Assembly will be updated. Press Ctrl-Shift-S to save your changes.

After that, run the game, check upgrade durability, then do a mission and check it again. Upgrade durability won't change, since we removed the code that changed it!

Now we need to add this or similar functionality to the BaseDroneUpgrade class. Let's take a closer look at ActivateAbility() method. It seems to hold some basic code to put drone upgrade into activated state, starting with sanity checks (is the upgrade activated? is it broken?), then marking it as active via this._IsActivated variable and then notifying UI and emitting an event about upgrade activation.

For the purposes of this example, we will simply increase BreakProbability by 0.3%-0.6% (ten times less than basic chance per mission in vanilla game) factored by upgrade robustness. Let's do it by creating a new method in the BaseDroneUpgrade class. Rightclick the class name on the left, choose "Add Method" and enter method name. We don't need parameters or return value, so nothing else is to do here. Then edit the method (C#) as usual.
public extern void ChangeDurabilityOnActivation() { float chance = UnityEngine.Random.Range(0.3f, 0.6f); //basic chance chance *= this.UpgradeBreakFactor; //factor in current upgrade robustness this.BreakProbability += chance; //increase breaking chance. breaking will only occur after the mission. }

You might encounter a problem with recompiling ActivateAbility() method the same way we did with the BeginExit(). It might be due to a bug in dnSpy. Still, we can edit the byte-code (machine-readable representation of the C# code) via rightclicking the method name and choosing "Edit method body". Go to the end of the window that will appear (byte-code instruction list), find and select "ret" instruction (which corresponds to "return" statement). We need 2 more instruction before it, so right-click or select the "ret" line and press "F" to add another instruction before it. It will be a "nop". Do this again. Then select "ldarg.0" instead of first "nop", "call" instead of "nop", click "null" to the right and select "Method", then choose BaseDroneUpgrade.ChangeDurabilityOnActivation(). Press OK.
If you did it right, you will see that it resulted in adding a "this.ChangeDurabilityOnActivation()" call at the last moment before return. Save all (Ctrl-Shift-S) and run the game to test it. You should see that now upgrades that have been used during the mission will start accumulating wear again, depending on how much you used them.

If you wish to implement more complex logic, you can edit the method we introduced (ChangeDurabilityOnActivation()). For example, you can try and increase wear and tear on damaged drones, thus prompting player to choose between repairing the drone or spending scrap on repairing upgrades.

This concludes this simple example. Sure, it requires some understanding of C# programming, but still, it's much easier than what you might think. If the aforementioned bug is fixed, you won't have to bother with editing byte-code at all.
I hope that proper modding support will be added eventually (or in the second version of the game), and I sure hope that this little tutorial won't draw ire of the developer on my head. ;)

Happy modding!
Last edited by vindicar; Nov 19, 2017 @ 5:35am
< >
Showing 1-13 of 13 comments
Biscuit Goddess Anu Feb 24, 2018 @ 10:25am 
What if I just wish to do something as simple as change the name of my starting ship?
vindicar Feb 24, 2018 @ 12:05pm 
Originally posted by Dat Guy Under the Stairs:
What if I just wish to do something as simple as change the name of my starting ship?
I found something relevant in GalaxyMapManager class, method Awake().
Specifically, player ship name is pulled from the save file (SHIP_ID value). If it's not there, a string 'The Justice Ryder' is substituted as the name instead. I'm not entirely sure if this branch of code is run when you start the game.
Perhaps it'd be easier to edit the save file after starting the game, once the name has been saved.
barquinha™ Aug 1, 2019 @ 12:37pm 
There's some kinda of documentary about the code? Holy smokes! I know that i don't know C# but i am a programmer of Python and understand how the code works. But i can't understand anything! What is the "brain"? I simply want to understand something as simple as the enemy AI!
vindicar Aug 1, 2019 @ 12:55pm 
Originally posted by CaioDosGames:
There's some kinda of documentary about the code? Holy smokes! I know that i don't know C# but i am a programmer of Python and understand how the code works. But i can't understand anything! What is the "brain"? I simply want to understand something as simple as the enemy AI!
Sorry, but you basically have to reverse engineer the game in order to mod something. That ain't gonna be easy.
As far as I remember (I poked aroudn a long while ago) XXXBrain classes indeed contain most of enemy AI logic. If you tell me what you are trying to do, perhaps I can point you towards specific classes and methods of interest...

For example, a quick way to add/remove existing abilities for enemies is to find XXXEnemy class and edit Behaviors property. This is one for Swarm:
protected override EnemyAiBehaviors Behaviors { get { return EnemyAiBehaviors.AttacksWhenHit | EnemyAiBehaviors.ChewsThroughDoors | EnemyAiBehaviors.Wanders | EnemyAiBehaviors.AttacksDroneOnSight | EnemyAiBehaviors.AttractedToLures | EnemyAiBehaviors.AttacksProbes | EnemyAiBehaviors.DetectsEnemyInAdjacentRoom | EnemyAiBehaviors.CanMove | EnemyAiBehaviors.DetectsStealth | EnemyAiBehaviors.CuriousSeeker; } }
You can then start looking for references to the particular behaviour enum to see where they are implemented.
Last edited by vindicar; Aug 1, 2019 @ 1:01pm
barquinha™ Aug 3, 2019 @ 7:17pm 
I was poking around and make somthing easy before i start making something big as unofficial patches. I can do the other drone upgrades craftable, since it's just a Ctrl+C and Ctrl+V question. And i found something interesting. In the middle of the upgrades list, i found the name "Repair". I think that it is an unused upgrade, and maybe there's still the code or forgotten segments about it. But it can be also be a macgyver way to repair drones in that interface, since we don't know how the programmers code this. Still, mod a game where there's no reference how to mod, only to decompile is pretty hard and a good challenge.
barquinha™ Aug 4, 2019 @ 7:17am 
Huh, strange. I can't recompile the code again.
barquinha™ Aug 4, 2019 @ 7:35am 
Ok, i tried to compile just to check what is going on. And there's 2 errors in each 4 broken segments. And theses 4 broken segments are the ship components you're visiting. (Defense, terminal, fuel access and power inlet. One error of each means a "virtual" modifier that isn't valid for this item. The 2nd error is "
Explicit method implementation "DungeonDefense.get_IsDead ()" cannot implement "IHasHitpoints.IsDead.get" because it is an accessor". I will try to fix the problem and check if the game accepts the code.
barquinha™ Aug 4, 2019 @ 7:53am 
Tried to compile the code after fixing those problems and the Visual Studio showed me errors that didn't show before. I am almost giving up on modding this game.
Gmanicus Aug 10, 2019 @ 5:01pm 
I applaude your motivation! I hope something worked out.
Dano416 Sep 16, 2019 @ 6:39am 
All of you in this thread: I applaud you for your impulses in the modding direction.

Here's the thing, though. I'm by no means any sort of coder, but I've spent a long time with this game, and I've spent a fair amount of time interacting with coders who actually play this game, some of whom have actually decompiled it at had a look at its innards, and from what they describe, it's a ♥♥♥♥♥♥♥ mess.

There was a person called Decavoid who actually released a bunch of unofficial patches last year (about 20 of them in all), but who has since retired from that gig. The patches made the game a lot more playable--especially the campaign mode and the weekly challenges, but they're still buggy as all get-out. The most common metaphor I think I've encountered for what the Duskers game code looks like is "spaghetti"m (or some other pasta-related term)....make of that what you will. If you want to talk with some of the players who know the game the best, the place to go is probably the unofficial Discord [okay, tried to share the link, and Steam removed it...do a google search for "Duskers unofficial Discord", follow one of the top links, and you will find your way] and then go about introducing yourself.

That said, if you want to mod, given the current (and probably permanent) state of the game, I think the thing to do is to try and learn the language of the save files. They're all .txt files, and aside from scripting new campaign modes (a lot of that, it turns out, is hard-coded into the game itself), you could do an awful lot just by manipulating those files through a text editor (or building and distributing a tool to facilitate that work). It's not for the faint of heart, because it's all kind of a mess, but if you wanted to delve into it, things of beauty could be constructed as a result, I suspect.


Last edited by Dano416; Sep 16, 2019 @ 6:44am
barquinha™ Sep 16, 2019 @ 3:17pm 
I've... Abandoned this project a long time ago. I like the game, but it is left in development, no news for anything, broken. I would still try to fix it, but i won't change a value for 100+ variables. If you have a fixed code, send me and i'll start modding. I won't continue for now. For all the persons, especially Dano416, i wish a good luck for you. Good luck trying to fix the spaghetti code.
barquinha™ Sep 29, 2019 @ 3:48pm 
I'm back! I've discovered that i was using an old version of Duskers. The current code isn't broken like it was. Oh, i made a basic mod recently. It just sets all upgrade crafting cost to zero (additionaly, it puts "It's free lol" as description). Download link: {LINK REMOVED} . Duskers has a great potential, but it is on development. The game's code isn't THAT messy, but i'm sure that a documentary would be good. The only thing that seems to block me is that i didn't even seen a C# lesson in anywhere. I think that i'm just making progress because i've learned Python some years ago, even if i didn't use it that much.
Last edited by barquinha™; Sep 29, 2019 @ 3:50pm
Flap Oct 6, 2019 @ 7:56am 
C# is the language used with unity (and seing the structure you describe), it looks like it has been made with unity.
You might want to have a look at the tutorials and docs.
< >
Showing 1-13 of 13 comments
Per page: 1530 50