177 lines
5.9 KiB
Plaintext
177 lines
5.9 KiB
Plaintext
|
[SML] Developer Guide
|
||
|
|
||
|
[b]SML compatible mutator development guide[/b]
|
||
|
|
||
|
Mutator template
|
||
|
|
||
|
You can use this template to make the mutator compatible with [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2863226847]SML[/url].
|
||
|
Here I will use "[b]Example[/b]" as mutator name. Replace it with yours.
|
||
|
|
||
|
[b]ExampleMut.uc[/b]
|
||
|
[code]
|
||
|
class ExampleMut extends KFMutator;
|
||
|
|
||
|
var private Example Example;
|
||
|
|
||
|
public simulated function bool SafeDestroy()
|
||
|
{
|
||
|
return (bPendingDelete || bDeleteMe || Destroy());
|
||
|
}
|
||
|
|
||
|
public event PreBeginPlay()
|
||
|
{
|
||
|
Super.PreBeginPlay();
|
||
|
|
||
|
if (WorldInfo.NetMode == NM_Client) return;
|
||
|
|
||
|
foreach WorldInfo.DynamicActors(class'Example', Example)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (Example == None)
|
||
|
{
|
||
|
Example = WorldInfo.Spawn(class'Example');
|
||
|
}
|
||
|
|
||
|
if (Example == None)
|
||
|
{
|
||
|
`Log("Example: FATAL: Can't Spawn 'Example'");
|
||
|
SafeDestroy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function AddMutator(Mutator Mut)
|
||
|
{
|
||
|
if (Mut == Self) return;
|
||
|
|
||
|
if (Mut.Class == Class)
|
||
|
Mut.Destroy();
|
||
|
else
|
||
|
Super.AddMutator(Mut);
|
||
|
}
|
||
|
|
||
|
public function NotifyLogin(Controller C)
|
||
|
{
|
||
|
Example.NotifyLogin(C);
|
||
|
|
||
|
Super.NotifyLogin(C);
|
||
|
}
|
||
|
|
||
|
public function NotifyLogout(Controller C)
|
||
|
{
|
||
|
Example.NotifyLogout(C);
|
||
|
|
||
|
Super.NotifyLogout(C);
|
||
|
}
|
||
|
|
||
|
static function String GetLocalString(optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2)
|
||
|
{
|
||
|
return String(class'Example');
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
|
||
|
}
|
||
|
[/code]
|
||
|
|
||
|
[b]Example.uc[/b]
|
||
|
[code]
|
||
|
class Example extends Info;
|
||
|
|
||
|
public event PreBeginPlay()
|
||
|
{
|
||
|
Super.PreBeginPlay();
|
||
|
|
||
|
// do some initialization here
|
||
|
}
|
||
|
|
||
|
public event PostBeginPlay()
|
||
|
{
|
||
|
Super.PostBeginPlay();
|
||
|
|
||
|
// do some initialization here
|
||
|
}
|
||
|
|
||
|
public function NotifyLogin(Controller C)
|
||
|
{
|
||
|
// Do what you need here when the player log in
|
||
|
}
|
||
|
|
||
|
public function NotifyLogout(Controller C)
|
||
|
{
|
||
|
// Do what you need here when the player log out
|
||
|
}
|
||
|
|
||
|
public simulated function vector GetTargetLocation(optional actor RequestedBy, optional bool bRequestAlternateLoc)
|
||
|
{
|
||
|
local Controller C;
|
||
|
C = Controller(RequestedBy);
|
||
|
if (C != None) { bRequestAlternateLoc ? NotifyLogout(C) : NotifyLogin(C); }
|
||
|
return Super.GetTargetLocation(RequestedBy, bRequestAlternateLoc);
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
|
||
|
}
|
||
|
[/code]
|
||
|
|
||
|
That's all. You can create new classes and add any code to [b]Example.uc[/b], but refrain from implementing anything else in [b]ExampleMut.uc[/b]
|
||
|
|
||
|
Limitations
|
||
|
|
||
|
❌ Can't make ranked game mode this way;
|
||
|
❌ [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2863226847]SML[/url] can only emulate [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/Mutator.uc#L147]NotifyLogin(...)[/url] and [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/Mutator.uc#L141]NotifyLogout(...)[/url], other functions of the [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/Mutator.uc]Mutator[/url] and [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/KFMutator.uc]KFMutator[/url] classes are not supported - look for workarounds in this case.
|
||
|
|
||
|
[Tips] Alternative to the InitMutator(...) function
|
||
|
|
||
|
Even though the [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/KFGame/Classes/KFMutator.uc#L22]InitMutator(...)[/url] function is not supported, you can still parse the startup string if you need to:
|
||
|
Refer to [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/GameInfo.uc#L209]WorldInfo.Game.ServerOptions[/url] or [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/9553ac0b31c729c27e9cd0a460ab5a7bae2fd799/Engine/Classes/WorldInfo.uc#L1315]WorldInfo.GetLocalURL()[/url] and get the option from there. It's best to do this in [b]PreBeginPlay()[/b] or [b]PostBeginPlay()[/b] of your [b]Example.uc[/b] (as well as other initializations).
|
||
|
|
||
|
[Tips] XP for custom Zeds / Weapons
|
||
|
|
||
|
While custom weapons and zeds won't make your server unranked, the [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/513c60070e2b700b5c311843754ed0e39b55a14e/KFGame/Classes/KFGameInfo.uc#L2594]ValidateForXP(...)[/url] function will not allow you to gain experience if it detects a custom zed or custom damage type.
|
||
|
|
||
|
To work around this, don't use custom damage types on custom weapons, or replace information in [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/513c60070e2b700b5c311843754ed0e39b55a14e/KFGame/Classes/KFPawn.uc#L182]DamageHistory[/url] before it gets into the [url=https://github.com/GenZmeY/KF2-Dev-Scripts/blob/513c60070e2b700b5c311843754ed0e39b55a14e/KFGame/Classes/KFGameInfo.uc#L2489]DistributeMoneyAndXP(...)[/url] function.
|
||
|
|
||
|
[Tips] Replacing base classes to bypass restrictions
|
||
|
|
||
|
In some cases, changing the base classes of the game can help. For example, we cannot make [b][url=https://steamcommunity.com/sharedfiles/filedetails/?id=2379769040]TAWOD[/url][/b] and [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2863226847]SML[/url] compatible because the [url=https://github.com/GenZmeY/KF2-TAWOD/blob/master/TAWOD/Classes/TAWODMut.uc#L19]PreventDeath(...)[/url] function is not supported. But this can be bypassed by replacing the player's Pawn base class with custom Pawn class:
|
||
|
[code]
|
||
|
WorldInfo.Game.DefaultPawnClass = class'ExamplePawn_Human';
|
||
|
[/code]
|
||
|
(This can be done in [b]PostBeginPlay()[/b])
|
||
|
|
||
|
And now we can implement all weapons drop in [b]ExamplePawn_Human.uc[/b]:
|
||
|
[code]
|
||
|
class TAWODPawn_Human extends KFPawn_Human;
|
||
|
|
||
|
public function ThrowActiveWeapon(optional bool bDestroyWeap)
|
||
|
{
|
||
|
local KFWeapon KFW;
|
||
|
|
||
|
if (Role < ROLE_Authority)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Health <= 0)
|
||
|
{
|
||
|
if (InvManager != None)
|
||
|
foreach KFP.InvManager.InventoryActors(class'KFWeapon', KFW)
|
||
|
if (KFW != None && KFW.bDropOnDeath && KFW.CanThrow())
|
||
|
KFP.TossInventory(KFW);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
super.ThrowActiveWeapon(bDestroyWeap);
|
||
|
}
|
||
|
}
|
||
|
[/code]
|
||
|
|
||
|
[Tips] Cloning DLC weapons
|
||
|
|
||
|
🛠[b] <UNDER_CONSTRUCTION> [/b]🛠
|