Skip to content

Commit 87362f3

Browse files
committed
FInished the Step-by-step guide.
1 parent 55b7c66 commit 87362f3

File tree

1 file changed

+179
-2
lines changed

1 file changed

+179
-2
lines changed

BuffDebuff/stepbystep.MD

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,42 @@ This is a step-by-step guide for creating [Buff Debuff Demo](https://github.com/
1111

1212
![Random Buff](./img/random-buff.jpg)
1313

14+
## TOC
15+
16+
1. [Refer to the mod assembly (DLL)](#1-refer-to-the-mod-assembly-dll)
17+
2. [Create Positive Buff](#2-create-positive-buff)
18+
1. [Create `PositiveBuff` class](#21-create-positivebuff-class)
19+
2. [Create `PositiveBuffInstance` class](#22-create-positivebuffinstance-class)
20+
3. [Create `SpeedBuffEffect` class](#23-create-speedbuffeffect-class)
21+
4. [Register the Buff](#24-register-the-buff)
22+
5. [Test the Buff](#25-test-the-buff)
23+
3. [Create `BeaverBuffComponent`](#3-create-beaverbuffcomponent)
24+
1. [Create `BeaverBuffComponent` class](#31-create-beaverbuffcomponent-class)
25+
2. [Register the Component](#32-register-the-component)
26+
3. [Test the Buff](#33-test-the-buff)
27+
- [Practice 1](#practice-1)
28+
- [Practice 2](#practice-2)
29+
4. [Create Lucky Buff (with Timed Buff Instance)](#create-lucky-buff-with-timed-buff-instance)
30+
1431
## 1. Refer to the mod assembly (DLL)
1532

1633
- Add the mod reference to your project. The file should be at _SteamLibrary\steamapps\workshop\content\1062090\3433810580\version-0.7\BuffDebuff.dll_.
1734

35+
- Add `BuffDebuff` as a required mod for your mod:
36+
37+
**manifest.json**
38+
```jsonc
39+
{
40+
// Other entries
41+
"RequiredMods": [
42+
{
43+
"Id": "BuffDebuff",
44+
"MinimumVersion": "7.0.0"
45+
}
46+
]
47+
}
48+
```
49+
1850
- Optionally add `global using BuffDebuff` or `<Using Include="BuffDebuff" />` as this will be the namespace for all Buff & Debuff System classes.
1951

2052
**BuffDebuff.csproj**
@@ -519,6 +551,151 @@ You can just reuse <code>SpeedBuffEffect</code> for this buff as well, just with
519551
If you don't see the buff, make sure you have the <code>NegativeBuff</code> registered in the mod configuration.
520552
</details>
521553

522-
## Create Lucky Buff (with Timed Buff Instance)
554+
## 4. Create Lucky Buff (with Timed Buff Instance)
555+
556+
Each day, the beavers also get a random movement speed buff that lasts for a random amount of time between 0 to 30 hours. This is a bit more complex because we need a timed buff instance, and this time there is a chance two or more buff instances are present at the same time.
557+
558+
### 4.1 Create `LuckyBuff` class
559+
560+
[See full content with helpful comments here: **LuckyBuff.cs**](https://github.com/datvm/TimberbornMods/blob/master/BuffDebuffDemo/Buffs/LuckyBuff.cs)
561+
562+
```cs
563+
// This buff will create LuckyBuffInstance, a timed buff.
564+
// Since LuckyBuffInstance is not a BuffInstance<TValue, TBuff>, we cannot use SimpleValueBuff but only SimpleBuff
565+
// We then make our own `CreateInstance` that was provided by SimpleValueBuff.
566+
public class LuckyBuff(ISingletonLoader loader, IBuffService buffs, ILoc t, EventBus eb) : SimpleBuff(loader, buffs), IUnloadableSingleton
567+
{
568+
// ...
569+
}
570+
```
571+
572+
- Since `LuckyBuffInstance` is not a `BuffInstance<TValue, TBuff>`, we cannot use `SimpleValueBuff` but only `SimpleBuff`.
573+
- What we will be missing is only a `CreateInstance` method that we can easily implement.
574+
575+
```cs
576+
LuckyBuffInstance CreateInstance(float hours, float perc)
577+
{
578+
// Do not just create an instance directly, you need to inject the services
579+
// And values if it's of IValuedBuffInstance
580+
return buffs.CreateBuffInstance<LuckyBuff, LuckyBuffInstance, LuckyBuffInstanceValue>(this, new(hours, perc));
581+
}
582+
```
583+
584+
- Note: `LuckyBuffInstance` is not available yet, but we will create it in the next step.
585+
586+
- To create an instance of `LuckyBuffInstance`, we need to use `IBuffService.CreateBuffInstance()` method. This method will inject the necessary services _and value_ into the instance.
587+
588+
```cs
589+
[OnEvent]
590+
public void OnDaytimeStart(DaytimeStartEvent _)
591+
{
592+
const float MaxBuffPerc = .2f; // Only 20% compared to other buffs' 100%
593+
const float MaxBuffHours = 30f; // There is a chance two or more buffs exist at once
594+
595+
// We do not remove existing instance like the other buffs
596+
597+
// Now we create a random boost for a random amount of hours
598+
var hours = UnityEngine.Random.Range(0, MaxBuffHours);
599+
var perc = UnityEngine.Random.Range(0, MaxBuffPerc);
600+
601+
// Lucky buff is applied with positive percentage
602+
var instance = CreateInstance(hours, perc);
603+
buffs.Apply(instance);
604+
605+
Debug.Log($"Lucky buff applied with {perc:P} speed increased for {hours:F1} hours");
606+
}
607+
```
608+
609+
- The logic is similar to the `PositiveBuff` but we do not remove the existing instance. This is because we do not remove the buff when the day starts, but when the buff duration ends.
610+
611+
_Other parts of the code are similar to `PositiveBuff` so I will not repeat them here._
612+
613+
### 4.2 Create `LuckyBuffInstance` class
614+
615+
[See full content with helpful comments here: **LuckyBuffInstance.cs**](https://github.com/datvm/TimberbornMods/blob/master/BuffDebuffDemo/Buffs/LuckyBuffInstance.cs)
616+
617+
```cs
618+
// Since this BuffInstance needs a few values, we create a record to hold them.
619+
public readonly record struct LuckyBuffInstanceValue(float Hours, float Speed);
620+
```
621+
622+
- This time instead of using a `float` as the sole value, we use a `struct` (or anything you like) to hold both the hours and the speed boost value.
623+
624+
```cs
625+
// This BuffInstance is timed so we use TimedBuffInstance which already has the logic for processing it
626+
// We also implement IValuedBuffInstance<LuckyBuffInstanceValue> because TimedBuffInstance doesn't support it out of the box.
627+
public class LuckyBuffInstance : TimedBuffInstance<LuckyBuff>, IValuedBuffInstance<LuckyBuffInstanceValue>
628+
{
629+
// Property for IValuedBuffInstance<LuckyBuffInstanceValue> so the IBuffService can set it automatically
630+
public LuckyBuffInstanceValue Value { get; set; }
631+
632+
// ...
633+
}
634+
```
635+
636+
- We use `TimedBuffInstance` as the base class because this buff is timed. The base class already has the logic for: saving and loading the remaining time, removing the buff when the time is up.
637+
638+
- We implement `IValuedBuffInstance<LuckyBuffInstanceValue>` because we can then use `IBuffService.CreateBuffInstance()` to inject the `Value` property into the instance.
639+
640+
```cs
641+
// This is the total time when the buff starts. Make sure it's available before Init() call of the base class.
642+
public override float StartingTime => Value.Hours;
643+
644+
// If you override AdditionalDescription, note that the base class already has a logic to show the time left.
645+
// public override string? AdditionalDescription { get => base.AdditionalDescription; protected set => base.AdditionalDescription = value; }
646+
```
647+
648+
- `AdditionalDescription` is already implemented in the base class to show the remaining time. You can override it to show more information.
649+
650+
- `StartingTime` is the total time when the buff starts. Make sure it's available before `Init()` call of the base class.
651+
652+
> ![TIP]
653+
> Unless you have special logic, saving `StartingTime` is not necessary because the base class already saves and loads the remaining time for you.
654+
655+
```cs
656+
// These are the dependencies that the TimedBuffInstance needs to process it,
657+
// so we need to inject them as well beside what extra you may need
658+
protected override IBuffService Buffs { get; set; } = null!;
659+
protected override IDayNightCycle DayNight { get; set; } = null!;
660+
protected override ILoc T { get; set; } = null!;
661+
662+
IBuffableService buffables = null!;
663+
EventBus eb = null!;
664+
665+
[Inject]
666+
public void InjectDeps(IBuffService buffs, IDayNightCycle dayNight, ILoc t, IBuffableService buffables, EventBus eb)
667+
{
668+
// Beside injecting your own dependencies, you need to inject the base class dependencies as well
669+
base.Inject(buffs, dayNight, t);
670+
671+
this.buffables = buffables;
672+
this.eb = eb;
673+
}
674+
```
675+
676+
- Beside the dependencies you need, you also need to inject the base class dependencies. You can do this by calling `base.Inject()`. Those are the services the base class needs to process the timed buff.
677+
678+
_Other parts of the code are similar to `PositiveBuffInstance` so I will not repeat them here._
679+
680+
### 4.3 Register the Buff
681+
682+
In your mod configuration, register the buff like the other buffs:
683+
684+
```cs
685+
public override void Configure()
686+
{
687+
// ...
688+
689+
Bind<LuckyBuff>().AsSingleton();
690+
}
691+
```
692+
693+
### 4.4 Test the Buff
694+
695+
Now load the game again, you should see the lucky buff applied to the beavers every day. The buff will last for a random amount of time between 0 to 30 hours.
696+
697+
![Lucky Buff](./img/random-buff.jpg)
698+
699+
This buff's bonus should stack with the positive buff and the negative buff you created.
523700

524-
Docs for this part is coming soon.
701+
You can also try saving the game, then reloading it to make sure the value and remaining time is saved and loaded correctly.

0 commit comments

Comments
 (0)