Skip to content

Commit 19a34e9

Browse files
committed
v1.2.1 - Crystal scaling + refined logging
- Add crystal_multiplier: scales MonsterDropDefine.Money at runtime (bypasses Il2Cpp native calls by modifying game data directly) - Restructured logging: unified [AI Limit: Difficulty Scaling] prefix for startup, compact [AIL:DS] for runtime debug, patch status list with checkmark/cross per method
1 parent c5085f2 commit 19a34e9

9 files changed

Lines changed: 101 additions & 71 deletions

File tree

DifficultyScaling/src/EasyMod.cs

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Reflection;
45
using MelonLoader;
56
using HarmonyLib;
67
using Il2Cpp;
78
using Il2CppInterop.Runtime;
89

9-
[assembly: MelonInfo(typeof(DifficultyScaling.ModMain), "AI Limit Difficulty Scaling", "1.0.0", "CyrixJD115")]
10+
[assembly: MelonInfo(typeof(DifficultyScaling.ModMain), "AI Limit Difficulty Scaling", "1.2.1", "CyrixJD115")]
1011
[assembly: MelonGame("SenseGames", "AILIMIT")]
1112

1213
namespace DifficultyScaling;
@@ -18,7 +19,12 @@ public class ModMain : MelonMod
1819
private static float _playerHpMult = 2.0f;
1920
private static float _playerDefMult = 2.0f;
2021
private static float _monsterDefMult = 0.4f;
22+
private static float _crystalMult = 1.0f;
2123
private static bool _debug = false;
24+
private static bool _crystalScaled = false;
25+
26+
private const string LOG = "[AI Limit: Difficulty Scaling]";
27+
private const string DBG = "[AIL:DS]";
2228

2329
public override void OnInitializeMelon()
2430
{
@@ -27,7 +33,7 @@ public override void OnInitializeMelon()
2733
var flagFiles = Directory.GetFiles(cfgDir, "difficulty.*");
2834
if (flagFiles.Length == 0)
2935
{
30-
MelonLogger.Error("No difficulty.* flag file found in DifficultyScaling_cfg/. Mod will use defaults.");
36+
MelonLogger.Error($"{LOG} No difficulty.* flag file found in DifficultyScaling_cfg/. Mod will use defaults.");
3137
return;
3238
}
3339

@@ -50,7 +56,7 @@ public override void OnInitializeMelon()
5056

5157
if (matchedDir == null)
5258
{
53-
MelonLogger.Error($"No preset matching '{flagValue}' found. Mod will use defaults.");
59+
MelonLogger.Error($"{LOG} No preset matching '{flagValue}' found. Mod will use defaults.");
5460
return;
5561
}
5662

@@ -62,10 +68,11 @@ public override void OnInitializeMelon()
6268
_playerHpMult = TomlConfig.GetFloat(cfg, "player_hp_multiplier", 2.0f);
6369
_playerDefMult = TomlConfig.GetFloat(cfg, "player_defense_multiplier", 2.0f);
6470
_monsterDefMult = TomlConfig.GetFloat(cfg, "monster_defense_multiplier", 0.4f);
71+
_crystalMult = TomlConfig.GetFloat(cfg, "crystal_multiplier", 1.0f);
6572
_debug = TomlConfig.GetBool(cfg, "debug", false);
6673

67-
MelonLogger.Msg($"Difficulty Scaling loaded (preset: {Path.GetFileName(matchedDir)})");
68-
MelonLogger.Msg($"PlayerAtk x{_playerAttackMult}, MonsterAtk x{_monsterAttackMult}, PlayerHP x{_playerHpMult}, PlayerDef x{_playerDefMult}, MonsterDef x{_monsterDefMult}, Debug={_debug}");
74+
MelonLogger.Msg($"{LOG} Loaded preset: {Path.GetFileName(matchedDir)}");
75+
MelonLogger.Msg($"{LOG} PlayerAtk x{_playerAttackMult} | MonsterAtk x{_monsterAttackMult} | PlayerDef x{_playerDefMult} | MonsterDef x{_monsterDefMult} | PlayerHP x{_playerHpMult} | Crystal x{_crystalMult}");
6976
}
7077

7178
public override void OnLateInitializeMelon()
@@ -75,81 +82,84 @@ public override void OnLateInitializeMelon()
7582
var playerType = typeof(Player);
7683
var monsterType = typeof(Monster);
7784

78-
var playerDecreaseHp = playerType.GetMethod("DecreaseHp", new Type[] { typeof(float), typeof(ActorDefine.DeadType), typeof(bool), typeof(bool), typeof(bool) });
79-
var monsterDecreaseHp = monsterType.GetMethod("DecreaseHp", new Type[] { typeof(float), typeof(ActorDefine.DeadType), typeof(bool), typeof(bool), typeof(bool) });
80-
var playerGetHpMax = playerType.GetMethod("GetHpMaxAttribute", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null);
81-
82-
if (_debug)
83-
{
84-
MelonLogger.Msg($"Player.DecreaseHp: {(playerDecreaseHp != null ? "FOUND" : "NOT FOUND")}");
85-
MelonLogger.Msg($"Monster.DecreaseHp: {(monsterDecreaseHp != null ? "FOUND" : "NOT FOUND")}");
86-
MelonLogger.Msg($"Player.GetHpMaxAttribute: {(playerGetHpMax != null ? "FOUND" : "NOT FOUND")}");
87-
}
88-
89-
var prefixDecreaseHpOnPlayer = new HarmonyMethod(typeof(ModMain).GetMethod(nameof(PlayerDecreaseHpPrefix), BindingFlags.Static | BindingFlags.NonPublic));
90-
var prefixDecreaseHpOnMonster = new HarmonyMethod(typeof(ModMain).GetMethod(nameof(MonsterDecreaseHpPrefix), BindingFlags.Static | BindingFlags.NonPublic));
91-
var postfixGetHpMax = new HarmonyMethod(typeof(ModMain).GetMethod(nameof(GetHpMaxPostfix), BindingFlags.Static | BindingFlags.NonPublic));
92-
93-
if (playerDecreaseHp != null)
85+
var patches = new (string Name, MethodInfo Method, MethodInfo Prefix, MethodInfo Postfix)[]
9486
{
95-
harmony.Patch(playerDecreaseHp, prefix: prefixDecreaseHpOnPlayer);
96-
if (_debug) MelonLogger.Msg("Patched Player.DecreaseHp");
97-
}
98-
99-
if (monsterDecreaseHp != null)
87+
("Player.DecreaseHp", playerType.GetMethod("DecreaseHp", new Type[] { typeof(float), typeof(ActorDefine.DeadType), typeof(bool), typeof(bool), typeof(bool) }),
88+
typeof(ModMain).GetMethod(nameof(PlayerDecreaseHpPrefix), BindingFlags.Static | BindingFlags.NonPublic), null),
89+
("Monster.DecreaseHp", monsterType.GetMethod("DecreaseHp", new Type[] { typeof(float), typeof(ActorDefine.DeadType), typeof(bool), typeof(bool), typeof(bool) }),
90+
typeof(ModMain).GetMethod(nameof(MonsterDecreaseHpPrefix), BindingFlags.Static | BindingFlags.NonPublic), null),
91+
("Player.GetHpMaxAttribute", playerType.GetMethod("GetHpMaxAttribute", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null),
92+
null, typeof(ModMain).GetMethod(nameof(GetHpMaxPostfix), BindingFlags.Static | BindingFlags.NonPublic)),
93+
("Player.GetDefenseInfo", playerType.GetMethod("GetDefenseInfo", new Type[] { typeof(bool) }),
94+
null, typeof(ModMain).GetMethod(nameof(PlayerGetDefenseInfoPostfix), BindingFlags.Static | BindingFlags.NonPublic)),
95+
("Monster.GetDefenseInfo", monsterType.GetMethod("GetDefenseInfo", new Type[] { typeof(bool) }),
96+
null, typeof(ModMain).GetMethod(nameof(MonsterGetDefenseInfoPostfix), BindingFlags.Static | BindingFlags.NonPublic)),
97+
};
98+
99+
int patched = 0;
100+
foreach (var (name, method, prefix, postfix) in patches)
100101
{
101-
harmony.Patch(monsterDecreaseHp, prefix: prefixDecreaseHpOnMonster);
102-
if (_debug) MelonLogger.Msg("Patched Monster.DecreaseHp");
102+
if (method != null)
103+
{
104+
harmony.Patch(method,
105+
prefix: prefix != null ? new HarmonyMethod(prefix) : null,
106+
postfix: postfix != null ? new HarmonyMethod(postfix) : null);
107+
MelonLogger.Msg($"{LOG} \u2713 {name}");
108+
patched++;
109+
}
110+
else
111+
{
112+
MelonLogger.Msg($"{LOG} \u2717 {name} \u2014 NOT FOUND");
113+
}
103114
}
104115

105-
if (playerGetHpMax != null)
106-
{
107-
harmony.Patch(playerGetHpMax, postfix: postfixGetHpMax);
108-
if (_debug) MelonLogger.Msg("Patched Player.GetHpMaxAttribute");
109-
}
116+
MelonLogger.Msg($"{LOG} Patched {patched}/{patches.Length} methods");
117+
}
110118

111-
var playerGetDefense = playerType.GetMethod("GetDefenseInfo", new Type[] { typeof(bool) });
112-
var monsterGetDefense = monsterType.GetMethod("GetDefenseInfo", new Type[] { typeof(bool) });
119+
public override void OnUpdate()
120+
{
121+
if (_crystalScaled || _crystalMult == 1.0f) return;
113122

114-
if (_debug)
123+
try
115124
{
116-
MelonLogger.Msg($"Player.GetDefenseInfo: {(playerGetDefense != null ? "FOUND" : "NOT FOUND")}");
117-
MelonLogger.Msg($"Monster.GetDefenseInfo: {(monsterGetDefense != null ? "FOUND" : "NOT FOUND")}");
118-
}
125+
var configData = GlobalConfig.ConfigData;
126+
if (configData == null) return;
119127

120-
var postfixPlayerDefense = new HarmonyMethod(typeof(ModMain).GetMethod(nameof(PlayerGetDefenseInfoPostfix), BindingFlags.Static | BindingFlags.NonPublic));
121-
var postfixMonsterDefense = new HarmonyMethod(typeof(ModMain).GetMethod(nameof(MonsterGetDefenseInfoPostfix), BindingFlags.Static | BindingFlags.NonPublic));
128+
var monsterDrops = configData.MonsterDrop;
129+
if (monsterDrops == null || monsterDrops.Count == 0) return;
122130

123-
if (playerGetDefense != null)
124-
{
125-
harmony.Patch(playerGetDefense, postfix: postfixPlayerDefense);
126-
if (_debug) MelonLogger.Msg("Patched Player.GetDefenseInfo");
127-
}
131+
int count = monsterDrops.Count;
132+
for (int i = 0; i < count; i++)
133+
{
134+
var drop = monsterDrops[i];
135+
if (drop == null) continue;
136+
drop.Money = (int)Math.Round(drop.Money * _crystalMult);
137+
}
128138

129-
if (monsterGetDefense != null)
139+
_crystalScaled = true;
140+
MelonLogger.Msg($"{LOG} Crystal x{_crystalMult} applied to {count} MonsterDrop entries");
141+
}
142+
catch (Exception ex)
130143
{
131-
harmony.Patch(monsterGetDefense, postfix: postfixMonsterDefense);
132-
if (_debug) MelonLogger.Msg("Patched Monster.GetDefenseInfo");
144+
if (_debug) MelonLogger.Msg($"{DBG} Crystal scaling waiting for data... ({ex.Message})");
133145
}
134-
135-
MelonLogger.Msg("Harmony patching complete.");
136146
}
137147

138148
private static void PlayerDecreaseHpPrefix(ref float value)
139149
{
140-
if (_debug) MelonLogger.Msg($"[Player.DecreaseHp] value={value} -> {value * _monsterAttackMult}");
150+
if (_debug) MelonLogger.Msg($"{DBG} Player DMG {value} \u2192 {value * _monsterAttackMult} (x{_monsterAttackMult})");
141151
value *= _monsterAttackMult;
142152
}
143153

144154
private static void MonsterDecreaseHpPrefix(ref float value)
145155
{
146-
if (_debug) MelonLogger.Msg($"[Monster.DecreaseHp] value={value} -> {value * _playerAttackMult}");
156+
if (_debug) MelonLogger.Msg($"{DBG} Monster HP {value} \u2192 {value * _playerAttackMult} (x{_playerAttackMult})");
147157
value *= _playerAttackMult;
148158
}
149159

150160
private static void GetHpMaxPostfix(ref float __result)
151161
{
152-
if (_debug) MelonLogger.Msg($"[Player.GetHpMaxAttribute] {__result} -> {__result * _playerHpMult}");
162+
if (_debug) MelonLogger.Msg($"{DBG} Player HP {__result} \u2192 {__result * _playerHpMult} (x{_playerHpMult})");
153163
__result *= _playerHpMult;
154164
}
155165

@@ -164,7 +174,7 @@ private static void PlayerGetDefenseInfoPostfix(ref DefenseInfo __result)
164174
__result.nInfectDamageReductionRate *= _playerDefMult;
165175
__result.nGetHitConfidenceReductionRate *= _playerDefMult;
166176
__result.MaxHp *= _playerDefMult;
167-
if (_debug) MelonLogger.Msg($"[Player.GetDefenseInfo] defense scaled x{_playerDefMult}");
177+
if (_debug) MelonLogger.Msg($"{DBG} Player Def scaled x{_playerDefMult}");
168178
}
169179

170180
private static void MonsterGetDefenseInfoPostfix(ref DefenseInfo __result)
@@ -178,6 +188,6 @@ private static void MonsterGetDefenseInfoPostfix(ref DefenseInfo __result)
178188
__result.nInfectDamageReductionRate *= _monsterDefMult;
179189
__result.nGetHitConfidenceReductionRate *= _monsterDefMult;
180190
__result.MaxHp *= _monsterDefMult;
181-
if (_debug) MelonLogger.Msg($"[Monster.GetDefenseInfo] defense scaled x{_monsterDefMult}");
191+
if (_debug) MelonLogger.Msg($"{DBG} Monster Def scaled x{_monsterDefMult}");
182192
}
183193
}

DifficultyScaling_cfg/Difficulty/00_custom/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ player_attack_multiplier = 1.0
1111
player_defense_multiplier = 1.0
1212
player_hp_multiplier = 1.0
1313

14+
# === Economy Scaling ===
15+
crystal_multiplier = 1.0
16+
1417
debug = false

DifficultyScaling_cfg/Difficulty/01_story/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ player_attack_multiplier = 1.5
99
player_defense_multiplier = 1.2
1010
player_hp_multiplier = 1.5
1111

12+
# === Economy Scaling ===
13+
crystal_multiplier = 1.5
14+
1215
debug = false

DifficultyScaling_cfg/Difficulty/02_easy/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ player_attack_multiplier = 1.2
99
player_defense_multiplier = 1.2
1010
player_hp_multiplier = 1.35
1111

12+
# === Economy Scaling ===
13+
crystal_multiplier = 1.3
14+
1215
debug = false

DifficultyScaling_cfg/Difficulty/03_normal/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ player_attack_multiplier = 1.1
99
player_defense_multiplier = 1.1
1010
player_hp_multiplier = 1.2
1111

12+
# === Economy Scaling ===
13+
crystal_multiplier = 1.15
14+
1215
debug = false

DifficultyScaling_cfg/Difficulty/04_adept/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ player_attack_multiplier = 1.0
1010
player_defense_multiplier = 1.0
1111
player_hp_multiplier = 1.0
1212

13+
# === Economy Scaling ===
14+
crystal_multiplier = 1.0
15+
1316
debug = false

DifficultyScaling_cfg/Difficulty/05_hard/scaling.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ player_attack_multiplier = 0.9
99
player_defense_multiplier = 0.9
1010
player_hp_multiplier = 0.9
1111

12+
# === Economy Scaling ===
13+
crystal_multiplier = 0.95
14+
1215
debug = false

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Difficulty scaling mod for AI Limit. Pick a preset, drop it in, play.
44

5-
A MelonLoader mod for **AI Limit** that lets you customize game difficulty through presets. Choose from 5 difficulty levels plus a custom preset.
5+
A MelonLoader mod for **AI Limit** that lets you customize game difficulty through presets. Choose from 5 difficulty levels plus a custom preset. Scales monster stats, player stats, and crystal drops.
66

77
## Requirements
88

@@ -45,13 +45,13 @@ Only one `difficulty.*` file should exist at a time.
4545

4646
## Preset Values
4747

48-
| Preset | Monster Atk | Monster Def | Player Atk | Player Def | Player HP |
49-
|---|---|---|---|---|---|
50-
| Story | x0.4 | x0.4 | x1.5 | x1.2 | x1.5 |
51-
| Easy | x0.6 | x0.6 | x1.2 | x1.2 | x1.35 |
52-
| Normal | x0.8 | x0.85 | x1.1 | x1.1 | x1.2 |
53-
| Adept | x1.0 | x1.0 | x1.0 | x1.0 | x1.0 |
54-
| Hard | x1.2 | x1.25 | x0.9 | x0.9 | x0.9 |
48+
| Preset | Monster Atk | Monster Def | Player Atk | Player Def | Player HP | Crystal |
49+
|---|---|---|---|---|---|---|
50+
| Story | x0.4 | x0.4 | x1.5 | x1.2 | x1.5 | x1.5 |
51+
| Easy | x0.6 | x0.6 | x1.2 | x1.2 | x1.35 | x1.3 |
52+
| Normal | x0.8 | x0.85 | x1.1 | x1.1 | x1.2 | x1.15 |
53+
| Adept | x1.0 | x1.0 | x1.0 | x1.0 | x1.0 | x1.0 |
54+
| Hard | x1.2 | x1.25 | x0.9 | x0.9 | x0.9 | x0.95 |
5555

5656
All multipliers are relative to the base game. **Adept** is identical to vanilla.
5757

@@ -65,6 +65,7 @@ monster_defense_multiplier = 0.85
6565
player_attack_multiplier = 1.05
6666
player_defense_multiplier = 1.05
6767
player_hp_multiplier = 1.2
68+
crystal_multiplier = 1.0
6869
```
6970

7071
## Debug Logging

nexus_description.bbcode

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
Difficulty scaling mod for AI Limit. Pick a preset, drop it in, play.
66

7-
A MelonLoader mod for [b]AI Limit[/b] that lets you customize game difficulty through presets. Choose from 5 difficulty levels plus a custom preset.
7+
A MelonLoader mod for [b]AI Limit[/b] that lets you customize game difficulty through presets. Choose from 5 difficulty levels plus a custom preset. Scales monster stats, player stats, and crystal drops.
88

99
[b]Requirements[/b]
1010

@@ -54,11 +54,11 @@ Only one [b]difficulty.*[/b] file should exist at a time.
5454
[b]Preset Values[/b]
5555

5656
[list]
57-
[*][b]Story[/b] — Monster Atk x0.4 | Monster Def x0.4 | Player Atk x1.5 | Player Def x1.2 | Player HP x1.5
58-
[*][b]Easy[/b] — Monster Atk x0.6 | Monster Def x0.6 | Player Atk x1.2 | Player Def x1.2 | Player HP x1.35
59-
[*][b]Normal[/b] — Monster Atk x0.8 | Monster Def x0.85 | Player Atk x1.1 | Player Def x1.1 | Player HP x1.2
60-
[*][b]Adept[/b] — Monster Atk x1.0 | Monster Def x1.0 | Player Atk x1.0 | Player Def x1.0 | Player HP x1.0
61-
[*][b]Hard[/b] — Monster Atk x1.2 | Monster Def x1.25 | Player Atk x0.9 | Player Def x0.9 | Player HP x0.9
57+
[*][b]Story[/b] — Monster Atk x0.4 | Monster Def x0.4 | Player Atk x1.5 | Player Def x1.2 | Player HP x1.5 | Crystal x1.5
58+
[*][b]Easy[/b] — Monster Atk x0.6 | Monster Def x0.6 | Player Atk x1.2 | Player Def x1.2 | Player HP x1.35 | Crystal x1.3
59+
[*][b]Normal[/b] — Monster Atk x0.8 | Monster Def x0.85 | Player Atk x1.1 | Player Def x1.1 | Player HP x1.2 | Crystal x1.15
60+
[*][b]Adept[/b] — Monster Atk x1.0 | Monster Def x1.0 | Player Atk x1.0 | Player Def x1.0 | Player HP x1.0 | Crystal x1.0
61+
[*][b]Hard[/b] — Monster Atk x1.2 | Monster Def x1.25 | Player Atk x0.9 | Player Def x0.9 | Player HP x0.9 | Crystal x0.95
6262
[/list]
6363

6464
All multipliers are relative to the base game. [b]Adept[/b] is identical to vanilla.
@@ -71,7 +71,8 @@ Edit [b]DifficultyScaling_cfg/Difficulty/00_custom/scaling.toml[/b] with your ow
7171
monster_defense_multiplier = 0.85
7272
player_attack_multiplier = 1.05
7373
player_defense_multiplier = 1.05
74-
player_hp_multiplier = 1.2[/code]
74+
player_hp_multiplier = 1.2
75+
crystal_multiplier = 1.0[/code]
7576

7677
[b]Debug Logging[/b]
7778

0 commit comments

Comments
 (0)