A Hat in Time
52 оценки
Death Wish Maker's Guide - Make Your Own Evil Contract!
От SuperInkLink и 1 сътрудника
Have you ever told Snatcher, "Gee, I wanna grow up to make as evil contracts as yours!" Well, look no further, as I've prepped here a guide to get you started on your Death Wish making journey!
2
   
Награда
Добавяне към любими
В любими
Премахване от любими
Getting Started
So you've decided to partake in the dark arts of Death Wish crafting. To fuel people's everlasting rampage through the map. Well, you've come to the right place. After all, I've made and played a couple of them.
One of the best places to see how the magic of how to make Death Wishes is, well, to look at where they came from! You can find their classes by going to the HatInTime folder > Development > Src > HatInTimeGameContent > Classes > Contracts > DeathWishes. Here, every single contract class is located for you to examine and copy over to your own mod as a framework for your Death Wish.
Depending on the complexity of the contract you wish to make, you could basically learn the basics from purely these. To cover the basic types of Death Wishes, including Speedrun, Jumps, and Rift Collapses, there are chapters in the guide for those. Here are the names of some key contracts, for helping you to decide what kinds of bonuses you'd want for yours:

Rift Collapses (Time remaining/grab every rift pon) - Hat_SnatcherContract_DeathWish_RiftCollapse
Security Breach (Tracking fees/getting caught) - Hat_SnatcherContract_DeathWish_DeadBirdStudioMoreGuards
Cruisin for a Bruisin (Tracking taskmasters) - Hat_SnatcherContract_DeathWish_EndlessTasks
Wound Up Windmill (Hatless and One Hit Hero) - Hat_SnatcherContract_DeathWish_FastWindmill
Mustache Gauntlet (Checking for player damage/tracking projectile usage) - Hat_SnatcherContract_DeathWish_HardCastle
Beat the Heat (Heat mechanics) - Hat_SnatcherContract_DeathWish_HeatingUpHarder
Bird Sanctuary (One Hit Hero/tracking enemy kills) - Hat_SnatcherContract_DeathWish_NiceBirdhouse
Mafia's Jumps (Basic jumps DW) - Hat_SnatcherContract_DeathWish_NoAPresses_MafiaAlien
She Speedran from Outer Space (Speedrun DW template) - Hat_SnatcherContract_DeathWish_Speedrun_MafiaAlien
Zero Jumps (Jump tracking/Candle script) - Hat_SnatcherContract_DeathWish_NoAPresses

There are plenty more to choose from, but working off a template Death Wish can teach you a lot about how they work. The basics will be covered here, of course.
Default Properties and What Means What
Firstly, once you have either a fresh UC script or a template DW put into your mod's Classes folder, you'll want to take a look at the default properties.

At the core of every Death Wish script, no matter the type, are the Default Properties. These are where you define most of the basic information about your Death Wish, including bonus creating, title cards, map position, stamp requirements, etc. For instance, this is So You're Back From Outer Space's Default Properties:

defaultproperties { UI_PosX = 0.28 UI_PosY = 0.47 Objectives(0) = (Title="Objective") Objectives(1) = (Title = "Kills", MaxTriggerCount = 7) Objectives(2) = (Title = "Speed", Title_EasyMode = "Speed_Easy") Conditions = ("Condition") TipLocalizedMessage = "Tip0" EasyTipLocalized = "EasyTip" AllowedMaps = ("mafia_town_night") ActInfo = Hat_ChapterActInfo'HatinTime_ChapterInfo.MafiaTown.MafiaTown_AlienChase' RequiredStamps = 2 HasEasyMode = true ParentDeathWishes.Add(class'Hat_SnatcherContract_DeathWish_HeatingUpHarder') TitleCard = Texture2D'HatInTime_Titlecards_DW.mafiatown_alien' }

For those who don't know what this all means, it may just look like a blob of snatcher goop. I'll explain what each variable and otherwise means.

UI_PosX/UI_PosY
This is what you use to set your Death Wish's location on the map. PosX is horizontal positioning, and PosY is vertical positioning. Here's a map grid to help you get the exact location you wish for your Death Wish to be (refer to the completed map above to attempt to look for any open spots):

Objectives(0) = (Title="Objective")
The main stamp of your Death Wish, the one that must be completed for bonuses to show up to the player.

Objectives(1) = (Title = "Kills", MaxTriggerCount = 7)
Objectives 1 and beyond will show up as bonuses on your contract, in the order you put them in as. In addition, this one has a MaxTriggerCount, which means it is a counter bonus, like killing the UFOs in So You're Back. The MaxTriggerCount basically states how many times this stamp needs to be "triggered" in order to be completed.

Objectives(2) = (Title = "Speed", Title_EasyMode = "Speed_Easy")
Another example of a bonus. One interesting thing about this one is the fact it has a different description if Peace and Tranquility (usually referred to as Easy Mode within scripts) is active. This is important for Localization, found later in the guide.

Conditions = ("Condition")
The contract "conditions", shown as the purple text under the main objective.
If you wish you have multiple conditions per contract, such as Boss Rush does, separate different quotes to use for your condition, such as:
Conditions = ("Condition1", "Condition2")

TipLocalizedMessage = "Tip0"
Snatcher's tip when asked for on the contract's spot in the Death Wish map. Include this for your contract to have a helpful tip or trick to tell to the player.

EasyTipLocalized = "EasyTip"
Snatcher's tip, but if Peace and Tranquility is enabled.

AllowedMaps = ("mafia_town_night")
This is where you tell Death Wish what maps to spawn/maintain the contract's events within. Such as conditions above, you can allow multiple maps with the power of commas.

ActInfo = Hat_ChapterActInfo'HatinTime_ChapterInfo.MafiaTown.MafiaTown_AlienChase'
The act your Death Wish uses and/or takes place in. If you're making a Death Wish for a simple modded time rift or whatnot, you don't NEED to create a new act info just for this; you can just use a base chapter act info. However, do mind that ActInfo will affect the following:
- Anything that checks for a specific act
- The location the player is in, displayed on the pause menu.
- If you don't specify a Death Wish specific titlecard later on, the Death Wish's titlecard will be whatever the act info's specified titlecard is, but with a purple tint, as seen with Cruisin' for a Bruisin'.
- You'll need to clear the specified act info before you can access the Death Wish. Snatcher prevents you from entering if you haven't.

RequiredStamps = 2
Very simple, being the required amount of Death Wish stamps needed to access your Death Wish for play. Try and find a good amount based on both map position relative to other Death Wishes, the overall difficulty of your Death Wish, and whatever else you feel necessary.

ParentDeathWishes.Add(*insert contract class here*)
Another unlock requirement, this time being the Death Wishes needed to be completed to access yours. If the ParentDeathWishes' main stamps are all not completed, you can't access yours. Usually it's a good idea to add parent Death Wishes based on map position and theming. Refer to the base game contract list if necessary.

HasEasyMode = true
Whether or not Peace and Tranquility can be enabled in your Death Wish. If you want certain things to be determined by whether or not P&T is active, use the function IsDeathWishEasyMode().

TitleCard = Texture2D'HatInTime_Titlecards_DW.mafiatown_alien'
A specified title card for your Death Wish. As said before, if you do not include this, Death Wish will simply add a purple hue over whatever title card the act info uses.

StartCheckpoint
To put it simply, what point of the level you start at, based on checkpoint numbers. For example, if I wanted to start the player inside the Windmill, I'd put a
StartCheckpoint = 1

BlockAllCheckpoints
It's as simple as the name describes it as. If you define this to be true, no checkpoints can be set within the level. Do note this can occasionally cause jankness, so refer to the PlayerDeath event described later on if you want an alternative.
Optional: 3 Lives
Ah yes, the 3 lives mechanic, known to frustrate many players by booting them all the way back to the hub if they run out of lives. Though this can be done correctly, use them with caution, such as giving the player a limited amount of retries for a platforming Death Wish that still uses checkpoints.

There are 3 variables that affect how the system works, all of which can be defined in DefaultProperties:
Condition_3Lives = true AlwaysUseLives = true PenaltyWaitTimeInSeconds = 60

Each one goes as follows:

Condition_3Lives
Setting this to true enables the lives system for the contract.

AlwaysUseLives
If this is set to false, after the initial clear of the contract, all subsequent attempts afterwards ditch the lives system. Setting it to true will always use lives, no matter how many times you've cleared the Death Wish.

PenaltyWaitTimeInSeconds
The timer system used to prevent the player from re-entering the Death Wish after running out of lives until the timer expires.
IMPORTANT NOTE: The lockout timer will also be activated if the player leaves the Death Wish if lives are active, no matter how many they had left!

Have fun making players rage at that evil evil Game Over screen!

Functions for Success
A proper Death Wish uses many important functions and events to work properly. Here are some of your most important functions and what they do:

TriggerObjective(0)
This basically "completes" a stamp for the Death Wish, triggering progress on said stamp when it is called. If a stamp uses MaxTriggerCount (created in DefaultProperties), it simply adds 1 to its progress counter and does the little "ding"!
SetObjectiveFailed(0, true/false)
Fairly self explanatory. If this is called as true, the targetted bonus will be failed if it isn't already, basically making it so it can no longer be obtained until it is un-failed. Calling it as false unfails the stamp. It's generally advised to unfail most of the stamps at the start of the Death Wish, so players don't get locked out of an objective.

ResetObjectiveProgress(0)
If an objective is a counter stamp (uses MaxTriggerCount), this resets progress on it back to 0. Two instances of using this include:
- At the start of a Death Wish, so players can't farm early points to the counter by doing part of the objective and then dying.
- If a mission uses a score system, such as Cruisin' for a Bruisin'. When the mission starts, it's a good idea to reset objective progress to 0, as players can restart the mission infinitely without dying.

IsObjectiveCompleted(0)
You can use this to check if your main objective or bonuses are completed. 0 is your main objective as usual, while 1+ is your bonuses. Cruisin' for a Bruisin', for example, raises its task requirement to earn the timepiece from 50 to 70 if the main stamp and first bonus are completed, to make it easier to grind out the 70 task objective.

if (IsExcluded()) return;
You'll see this at the start of almost every function in Death Wish, but what does it exactly do? Well, the entire of IsExcluded is this:
static function bool IsExcluded() { if (!IsOnlineItemAvailable()) return true; if (`GameManager.GetCurrentMapFilename() == `GameManager.TitleScreenMapName) return true; if (`GameManager.GetCurrentMapFilename() == `GameManager.HubMapName) return true; if (`GameManager.GetCurrentMapFilename() == `GameManager.CreditsMapName) return true; if (`GameManager.GetCurrentMapFilename() == `GameManager.CreditsMapName_DLC) return true; if (!IsAllowedMap(`GameManager.GetCurrentMapFilename())) { return true; } if (default.AllowedActs.Length > 0 && default.AllowedActs.Find(`GameManager.GetCurrentAct()) == INDEX_NONE) return true; if (IsLockedOut()) return true; return false; }
...to sum it up in an understandable manner, it basically means that the main reasons a Death Wish is "excluded" if:
- The player is on the title screen, spaceship, and credits.
- The player is not on a map specified in AllowedMaps, or an act specified by AllowedActs in a similar manner.
- The Death Wish is locked out by the timer, and you're somehow in it.
The main purpose of including this before nearly everything in a contract script is to make sure a player is where you want them to be for the Death Wish, to prevent them completing the Death Wish by entering a time rift and such.

KillEveryone()
Depending on the conditions of your Death Wish, you may want to commit myurder on every player if they do something wrong, like jumping too many times. Well, just include this function in your script, and you can call it whenever you want the players to all die!
function KillEveryone() { local Player ply; local Array<Player> GamePlayers; GamePlayers = class'Engine'.static.GetEngine().GamePlayers; foreach GamePlayers(ply) ply.Actor.Pawn.Died(None, None, ply.Actor.Pawn.Location); }

IsActive()
In some enemy or boss scripts, how do you make them their DW versions if your Death Wish is currently enabled? There's two different methods you can do.
1) IsActive - Specifically checks for your DW, and that DW alone. Great for Death Wish boss battles.
EXVersion = EXVersion || class'Hat_SnatcherContract_DeathWish_Example'.static.IsActive();
2) DWDifficulty - Used to make all enemies stronger in a Death Wish, like how Mad Crows are twice as fast. DWDifficulty is true for any level contracts and candles that have Condition_Difficulty set to true.
DeathWishVersion = DeathWishVersion || `GameManager.DWDifficulty;


If you're making a basic Death Wish, these are the extents of the functions to use. However, there is another key component for Death Wish scripts, known as...
Events and What to Manage
Game Events Inferface, also known as GEI, is implemented for Death Wishes, and is used to call almost all major events that can happen in a Death Wish. Almost every check, fail, and more all stems from one of the many available functions from GEI.

The following is a deep explanation of what events to use and are important for making a Death Wish work:

OnPostInitGame()
OnPostInitGame is essentially the very start of the Death Wish, as soon as the level loads and is ready. You normally use this section to restore objectives to 0, make them un-failed, and potentially do a few changes to the level from script. Here are some of the most useful ones:
function OnPostInitGame() { Super.OnPostInitGame(); SetObjectiveFailed(0, false); SetObjectiveFailed(1, false); SetObjectiveFailed(2, false); ResetObjectiveProgress(2); SpawnExamples(); }
Most Death Wishes include this purely for restarting stamps alone, but you can do a few more things within here, depending on the Death Wish.

OnPostLevelIntro()
Another function called early in a Death Wish, this time after a mission intro is completed and the player gains control. Usually, you'd not need to do too much here that you couldn't do with PostInit, but there are a few possibilities you can utilize with it:
- Getting the player in a readying pose, seen in speedruns.
- Starting a time limit purely from script.
- Things you don't want to be seen in a mission intro.
IMPORTANT NOTE: For levels that don't use a mission intro, such as purple rifts, this function will NOT be called! Use PostInit instead!

OnTimePieceCollected(String Identifier)
Whenever you grab a timepiece, this is called. The Identifier is the timepiece's identifier, so if your Death Wish is a "get the timepiece" Death Wish, it's a good idea to check for that specific timepiece's identifier to make sure the player grabbed the proper one. For example,
function OnTimePieceCollected(String Identifier) { if (Identifier == "ExampleTP") { TriggerObjective(0); TriggerObjective(1); TriggerObjective(2); } }

OnPawnCombatDeath(Pawn PawnCombat, Controller Killer, class<Object> DamageType, Vector HitLocation)
There's a lot to unpack with this one. OnPawnCombatDeath basically allows you to check for when an enemy dies, by any cause. Here's what each variable does and what you can check for:

PawnCombat - The pawn that died. You can use an IsA check to check for a certain type of enemy, such as Bird Sanctuary does.
function OnPawnCombatDeath(Pawn PawnCombat, Controller Killer, class<Object> DamageType, Vector HitLocation) { if (IsExcluded()) return; if (PawnCombat.IsA('Hat_Enemy_Mobster')) { TriggerObjective(2); } }
Killer - Who killed the enemy? This should state if anyone did! So if you want the check to only go through if a PLAYER killed the enemy, you'd include this in the check.

DamageType - Wanna make it so you have to trick Mafia into falling into lava for a bonus? Well, this is your friend! An example of such would be:
function OnPawnCombatDeath(Pawn PawnCombat, Controller Killer, class<Object> DamageType, Vector HitLocation) { if (IsExcluded()) return; if (PawnCombat.IsA('Hat_Enemy_Mobster') && DamageType == class'Hat_DamageType_Lava') { TriggerObjective(2); } }

HitLocation - If you wanna know WHERE they died. I have never seen this utilized before, but you could potentially try and use it to make the enemy have to die in a specific ... uh ... zone?

OnPostPawnCombatTakeDamage(Pawn PawnCombat, int Damage, Controller InstigatedBy, Vector HitLocation, Vector Momentum, class<Object> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
Very similar to PawnCombatDeath in a lot of ways, though you can utilize this to check for specific PLAYER damage, such as how Mustache Gauntlet checks if the player gets burned, or have a check for if the player dropped to 1 hp.
function OnPostPawnCombatTakeDamage(Pawn PawnCombat, int Damage, Controller InstigatedBy, Vector HitLocation, Vector Momentum, class<Object> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { if (IsExcluded()) return; if (PawnCombat.IsA('Hat_Player')) { if (DamageCauser.IsA('Hat_Enemy_MadCrow_Fire') || DamageType == class'Hat_DamageType_Fire') { SetObjectiveFailed(1, true); //You got burned boi { if (PawnCombat.Health <= 1) { SetObjectiveFailed(2, true); //Smh not a 3 hp gamer } } }
Or...if you want to make the player only use stomps to damage the enemy, you could do something like:
function OnPostPawnCombatTakeDamage(Pawn PawnCombat, int Damage, Controller InstigatedBy, Vector HitLocation, Vector Momentum, class<Object> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser) { if (IsExcluded()) return; if (PlayerController(InstigatedBy) == None) return; if (DamageType == class'Hat_DamageType_Jump') return; SetObjectiveFailed(1, true); }

OnPreStatusEffectAdded(Pawn PawnCombat, out class<Object> StatusEffect, optional out float OverrideDuration)
Check if a status effect is added to any player or enemy. Useful for certain types of bonuses. Don't want the player to get tripped up by a disco ball? Well, this is where you'd use that to your evil needs!
function OnPreStatusEffectAdded(Pawn PawnCombat, out class<Object> StatusEffect, optional out float OverrideDuration) { if (IsExcluded()) return; if (!PawnCombat.IsA('Hat_Player')) return; if (StatusEffect != class'Hat_StatusEffect_Tripping') return; SetObjectiveFailed(2, true); }

OnAbilityUsed(Pawn Player, Inventory Ability)
This function is called whenever an "ability" is used by a player. For bonuses that involve limited usage of hats, this function is the key to making them work.
IMPORTANT: Hookshot calls this function, so you typically want to run a check to exclude it so using a hookpoint cannot fail a bonus. Excluding basic hat is also a good idea so players have a "safe hat" to have equipped in case they mispress.
A typical hatless bonus would look like:
function OnAbilityUsed(Pawn Player, Inventory Ability) { if (IsExcluded()) return; if (Ability.IsA('Hat_Ability_Help')) return; if (Ability.IsA('Hat_Ability_Hookshot')) return; if (!Player.IsA('Hat_Player')) return; SetObjectiveFailed(1, true); }

OnWeaponBadgeUsed(Pawn Player, class<Inventory> Badge)
Basically, Projectile Badge. It usually fails hatless bonuses in most base game Death Wishes, as well as no projectile bonuses, so including it is simple and easy.
function OnWeaponBadgeUsed(Pawn Player, class<Inventory> Badge) { if (IsExcluded()) return; SetObjectiveFailed(1, true); }

OnCollectedCollectible(Object InCollectible)
Any collectible that's picked up in a level will call this function. Pons, health pons, rift tokens, anything can call it. If you'd want a player to pick up a certain amount of pons, you can do something like:
function OnCollectedCollectible(Object InCollectible) { if (IsExcluded()) return; if (Hat_Collectible_EnergyBit(InCollectible) != None) TriggerObjective(1); }

There are a lot more events available for use, but these are the most commonly used ones. If you wish to see the complete list of GEI events, look into Development > Src > HatInTimeGame > Classes > Modding, and open Hat_GameEventsInterface, and you'll be greeted with the full list of events.
Optional: Mini Missions
Mini Missions include things like death timers, ship shape taskmasters, and Big Parade's progression system. For certain bonuses and effects, using the following GEI functions will allow you to complete and fail them:

OnMiniMissionBegin(Object MiniMission)
Called whenever a Mini Mission is started, like the start of Train Rush's detonation sequence, or agreeing to Captain's request in Ship Shape. For taskmasters in particular, you can use this function to reset stamp progress and restore them from failure to make it so the player doesn't have to die every time. If you're particularly knowledgeable on how taskmasters work, you can also modify values of the taskmaster that's started to save a Kismet block or two.
function OnMiniMissionBegin(Object MiniMission) { if (IsExcluded()) return; if (Hat_MiniMissionTaskMaster(MiniMission) == None) return; ResetObjectiveProgress(0); ResetObjectiveProgress(1); ResetObjectiveProgress(2); SetObjectiveFailed(0, false); SetObjectiveFailed(1, false); SetObjectiveFailed(2, false); //This is literally how many tasks are spawned in Ship Shap but Gud lol Hat_MiniMissionTaskMaster(MiniMission).TasksPerMinute = 50; }

OnMiniMissionComplete(Object MiniMission)
If a mission is completed, either by fulfilling requirements like completing every task in Ship Shape, or through Kismet, this is called. You can either use this as an alternative to completing stamps rather than through OnTimePieceCollected, or as a way to punish the player for not completing an objective during the mission by failing it.
function OnMiniMissionComplete(Object MiniMission) { if (IsExcluded()) return; if (Hat_MiniMissionTaskMaster(MiniMission) == None) return; TriggerObjective(0); TriggerObjective(1); //What, you DIDN'T take time to complete this while focusing on a taskmaster? SetObjectiveFailed(2, true); }

OnMiniMissionFail(Object MiniMission)
On the other end of the Mini Mission finishing scale, failing a mission is exactly as read. You can use this to kill the player if they fail, or failing a bonus through a hidden timer.
function OnMiniMissionFail(Object MiniMission) { if (IsExcluded()) return; if (Hat_MiniMissionTimeLimit_Base(MiniMission) == None) return; SetObjectiveFailed(0, true); SetObjectiveFailed(1, true); SetObjectiveFailed(2, true); KillEveryone(); }

OnMiniMissionCancel(Object MiniMission)
Unused by anything as far as I can tell, but you could definitely use this in case a player decides to quit a taskmaster or such.

OnMiniMissionGenericEvent(Object MiniMission, String id)
Most notably used for taskmasters, this can help you change values in a Death Wish script based on what happened. Cruisin' uses this for two different things: checking for when a task is completed, and checking when a task is expired.
function OnMiniMissionGenericEvent(Object MiniMission, String id) { if (IsExcluded()) return; if (Hat_MiniMissionTaskMaster(MiniMission) == None) return; if (id == "score") { TriggerObjective(0); TriggerObjective(1); TriggerObjective(2); } else if (id == "miss") { SetObjectiveFailed(1, true); } }

OnMiniMissionTimeLimitSecond(Object MiniMission, float TimeLeft)
While GenericEvent is used for taskmasters, this is only for timers, mainly as a way to track how much time is left on the clock. Rift collapses, for example, use this for their "30 seconds remaining" bonuses.
function OnMiniMissionTimeLimitSecond(Object MiniMission, float TimeLeft) { if (IsExcluded()) return; if (!MiniMission.Isa('Hat_MiniMissionTimeLimit_Base')) return; if (!Hat_MiniMissionTimeLimit_Base(MiniMission).DeathTimer) return; if (TimeLeft < IsDeathWishEasyMode() ? 15 : 30) SetObjectiveFailed(ObjFaster, true); }
Kismetting Your Death Wish
So far, we've been mostly discussing script work for Death Wish, but there's a lot you can do through Kismet as well to beef up your level!

The best way to change up the mechanics of your mod level is one simple condition: Has Contract. If you compiled scripts for your Death Wish, you can select it to be the contract used for this node, and with that, you can basically change the entirety of your level's Kismet for a Death Wish through use of a singular condition.


Exchange matinees for faster versions, remove/add certain actors, heck, maybe even have a different mission intro! The possibilities are limitless!


Permitted/Forbidden Death Wishes
One other thing you can modify within the Editor itself is a property commonly found in actors such as enemies: Permitted and Forbidden Death Wishes. If you specify a permitted Death Wish, that actor can only appear and do its job if the Death Wish is active, and vice versa for Forbidden. If a forbidden Death Wish is active, the actor disappears.

Kismetting BONUSES?!
Normally, you can't mess with objectives too much from Kismet. But fortunately, I've made a Kismet node that allows you to trigger, fail, unfail, or reset progress on any objective! Simply add the script to your Classes folder (you may want to rename the file and/or description), compile, and boom, you can select this node under Kismet!
Download link for the node: https://hat.ovh/s/43
Localization
So you've gotten your Death Wish all together, applied all the changes to your Kismet, and you've compiled and cooked in a burst of excitement. You open your game to test, and...the descriptions show up as a bunch of garbled text.


What the peck happened?

Well, there's this thing called Localization that exists. If you've made costumes, dyes, or remixes before, you should know how it goes, but for those who don't, here's what you need to do.

Firstly, within your mod's folder, create a Localization folder. Within that, create an INT (basically means English) folder, and finally, you'll need to create an INT file type. Either copy one from a base game/mod file, or create one using Notepad.


Within that, you'll need to create a structure such as this:
[Ink_SnatcherContract_DeathWish_Example] Title = Don't Die ShortTitle = Don't Die Objective = Make a thing NoStress = Don't get anxiety...or ELSE PanicAttack = Make it while also beating Seal the Deal Condition = The mental health has become unstable! Tip0 = Kid. Get out of the chair. Take a break. Get some water.

Here's what each line represents:
Ink_SnatcherContract_DeathWish_Example - Your Death Wish script's file name.
Title - The name of your Death Wish.
ShortTitle - You can basically leave this the same as your title, unless you have a REALLY long name that can use condensing.
Objective - The main stamp of your Death Wish's description
NoStress/PanicAttack - Your Death Wish bonus descriptions, with the names to the left of the = being the names you gave them within your DW script. Add/remove lines depending on the amount of bonuses.
Condition - The Death Wish's special condition description. Note: If you have multiple conditions, add separate lines for each.
Tip0 - Snatcher's tip when asked for on the DW map.

If you want to do fancy things with Snatcher's tip dialogue, please refer to Weegee's guide on Conversation Trees; they work the same as here.
https://steamproxy.net/sharedfiles/filedetails/?id=2330519657

Once you think your Localization is ready, compile and cook and make sure it all shows up properly ingame! Localize your mods or be smitten by gods!

Example file if you can't create the INT file on your own:
https://cdn.discordapp.com/attachments/792194071787077635/793910512881631252/contracts_deathwish.int
"Clear this act first"
One thing you may have noticed is the fact that you can't even enter your own mod's Death Wish, with Snatcher babbling that you need to clear that mod's act first, if it happens to use a custom act info that is. You can choose to simply not lock a player out, or run a specific check for mod timepieces.

JUST LET ME IN!
If you don't wanna deal with any of that, simply add this function to your script, and you should be allowed into the Death Wish, no matter what:
static function bool IsActIncomplete() { return false; }

An alternative is setting IsCommunity to true within the DefaultProperties. There's two side effects of this:
- The snatcher stamp icon on the map appears slightly smaller than normal.
- Instead of "Death Wish" appearing above the level's name on the load screen, it displays "Community Map" instead.
Whether or not these two effects are of any importance to you is purely up to personal preference, but this variable DOES prevent getting blocked from entering your Death Wish, so it's your choice.

Alternatively...

I want to actually track act completion!
Mods use a different measure of their timepiece identifiers being saved. Like before, we'll be modifying the IsActIncomplete() function.

This time, we'll be checking for the mod timepiece identifiers, which follow a setup of "Mod:MODFOLDERNAME:ActualIdentifier"

One quirk with the game is that the mod folder name changes for the workshop version, so we use a function to grab it no matter what version is being used.

static final function string GetMyModPackageName() { local array<GameModInfo> modList; local GameModInfo modInfo; modList = class'GameMod'.static.GetModList(); foreach modList(modInfo) { if (modInfo.ScriptPackageName ~= string(default.Outer.Name)) return modInfo.PackageName; } return string(default.Outer.Name); } static function bool IsActIncomplete() { if (`SaveManager.HasTimePiece("Mod:" $ GetMyModPackageName() $ ":Your Timepiece Identifier")) return false; return (Super.IsActIncomplete()); }

If all works, you should now be able to lock players out of a Death Wish if they haven't completed a mod act!

IMPORTANT: Make sure your act uses Localization for its names and such, otherwise Snootch will give you a long blob of Localization likely!
Optional: Rewards
Wanna make playing your Death Wish more worth it outside of fun or challenge factor? Simple, add a completion reward!

Death Wishes can accept dyes, outfits, remixes, badges, practically any important collectible as rewards, and typically, they'll give that reward once the main objective has been completed. To add a reward to your Death Wish, simply add this line of code to your Default Properties:
CompletionReward = class'YourCollectibleHere'

And boom, that reward should show up on the map ez pz! Just make sure dyes or remixes can't appear in the roulette so you can't obtain them outside of your contract.

More difficult rewards?

What if...you want the player to do a little more to obtain your contract's reward? Well, currently, completion reward availability is handled through the following function:
static function bool IsRewardAvailable() { return default.CompletionReward != None && IsContractComplete(); }

IsContractComplete() returns true if the main objective is completed for the contract. But what if you wanted to break the mold and have the reward only be available if you have EVERY stamp for your contract completed? Well, just add this function to your Death Wish script, and it'll override how availability works normally:
static function bool IsRewardAvailable() { return default.CompletionReward != None && IsContractPerfected(); }

Unlike IsContractComplete(), IsContractPerfected() only returns true if every stamp is completed, which turns its icon on the map to gold. So if you want players to go that extra mile, well here ya go!
Candles
Candles are very interesting for Death Wish. The variety of them makes explaining how to make one challenging, so I'll mostly be going over basic stuff with this section.

First and foremost, your candle will not be extending of the regular Hat_SnatcherContract_DeathWish, but instead, it extends of Hat_SnatcherContract_DeathWish_Passive, which is the base candle class.

Since candles can vary wildly in conditions, here's a few important functions to have:

"Any level" Candles

These candles allow you to go into any level to increment the timepiece completion counter, such as Zero Jumps. To make sure players can't redo the same level over and over, it uses these two functions:
static function bool CheckAndSetUnobtainedTimePiece(string Identifier) { local string bitid; bitid = GetObjectiveBitID() $ TimePieceIDPrefix $ Identifier; if (class'Hat_SaveBitHelper'.static.HasLevelBit(bitid, 1, default.ObjectiveMapName)) return false; class'Hat_SaveBitHelper'.static.SetLevelBits(bitid, 1, default.ObjectiveMapName); return true; } static function SoftResetObjectives() { local string bitid_prefix; Super.SoftResetObjectives(); bitid_prefix = GetObjectiveBitID() $ TimePieceIDPrefix; class'Hat_SaveBitHelper_Base'.static.ResetLevelBitsByPrefix(bitid_prefix, default.ObjectiveMapName); }

In your TimePieceCollected function, simply call this before triggering any objectives related to the counter, and it should all work.
function OnTimePieceCollected(string Identifier) { if (!CheckAndSetUnobtainedTimePiece(Identifier)) return; TriggerObjective(0); TriggerObjective(1); TriggerObjective(2); }

Snatcher Coins
Snatcher Coins involve two classes: a very simple Death Wish script, and one token script. The token script basically adds the Death Wish as a permitted Death Wish, which allows the token to be collected if the candle is active.
class Hat_Collectible_DeathWishLevelToken_Example extends Hat_Collectible_DeathWishLevelToken `RemoveUntilDLC placeable; defaultproperties { `if(`isdefined(WITH_DLC1)) PermittedDeathWishes.Add(class'Hat_SnatcherContract_DeathWish_Tokens_Example'); `endif }
As for the DW script, it simply extends off Mafia Town's token DW, so you barely have to do anything at all.
class Hat_SnatcherContract_DeathWish_Tokens_Example extends Hat_SnatcherContract_DeathWish_Tokens_MafiaTown `RemoveUntilDLC; defaultproperties { UI_PosX = 0.42 UI_PosY = 0.69 AllowedMaps = ("YourModMap") ParentDeathWishes.Empty //So it doesn't use the same Parents as Mafia Town's token DW ParentDeathWishes.Add(class'ParentDWHere') }
(As a side note, make your Snatcher Coins actually interesting to grab. Make the player figure out a platforming puzzle to reach the coin, or have a somewhat hidden location, and not just have the coin be lying around randomly.)
Template Death Wish: Speedrun
Speedrun Death Wishes are by FAR the the simplest Death Wish you can script. Most of Hat_SnatcherContract_DeathWish_Speedrun already handles everything you need to make, and most speedrun Death Wishes simply extend off that, and it's a fill-in-the-blank from there.

class Hat_SnatcherContract_DeathWish_Speedrun_Example extends Hat_SnatcherContract_DeathWish_Speedrun `RemoveUntilDLC; defaultproperties { UI_PosX = Horizontal position; UI_PosY = Vertical position; AllowedMaps = ("YourMapNameHere"); ParentDeathWishes.Add(class'ContractClassGoesHere') ActInfo = Your Act Info Here TipLocalizedMessage = "Tip0" TimePieceEnd = "Ending timepiece" Goal0 = (2,00) //The main objective's time. Make it the easiest one to beat. Goal1 = (1,30) //Bonus 1's time. Make it a little harder. Goal2 = (1,15) //Bonus 2's time. Make it the hardest one to beat. BlockAllCheckpoints = false //So recovery volumes and such work properly. RequiredStamps = 10 TitleCard = Whatever you want } function OnPostLevelIntro() { Super.OnPostLevelIntro(); InitSpeedTimer(true); } //In case Kismet or otherwise requires setting a checkpoint, we use this to reset the player checkpoint. function OnPlayerDeath(Pawn Player) { Super.OnPlayerDeath(Player); `GameManager.SetCurrentCheckpoint(0, false); }

What if there's a map transition mid level, like with Speedrun Well? Well, it's simple to boot the player to the original map, using this function:
function OnPreRestartMap(out String MapName) { Super.OnPreRestartMap(MapName); if (MapName == locs("subcon_cave")) MapName = locs("subconforest"); }
So if you have a map change mid level, check for that secondary map, and set it to your original map if you're in it.

Template download: https://cdn.discordapp.com/attachments/792194071787077635/792536151453859850/Hat_SnatcherContract_DeathWish_Speedrun_Example.uc
Template Death Wish: Jumps
The template this will be describing about is based on how Mafia's Jumps works, with there being 3 jump goals you can make it under. Jumps Death Wishes may look more intimidating than Speedruns are, but in reality, they're quite simple to make.

Generally, when creating a Jumps Death Wish, you'll want to extend off of Zero Jumps' script in order to have Hat Kid auto-climb ledges without it counting as a jump. (Extending Mafia's Jumps does not work for this.) When doing so, make sure to override the stuff that candles set up, like the stamp icons and DW Difficulty being set to false! (Shown in the template script below)

Firstly, your Jumps Goals.

Min is your hardest bonus, requiring the lowest amount of jumps, while Max is the main objective, at the most lenient, with Easy versions for Peace and Tranquility. Your DefaultProperties can be filled out as normal, but with a new line defining the number particle that shows up when you jump, and optionally, a sound as well.

Jumps Death Wishes track a player jump through the GEI Function known as OnPlayerPressedJumpButton(Pawn Player). Once it is triggered, your jump counter increments, plays the particle/sound, and checks if it should fail stamps and/or kill the player.
function OnPlayerPressedJumpButton(Pawn Player) { local int NumJumps; local WorldInfo wi; local ParticleSystemComponent psc; if (IsExcluded()) return; if (Player.Controller == None) return; if (!Player.Controller.IsA('PlayerController')) return; wi = class'WorldInfo'.static.GetWorldInfo(); NumJumps = GetJumpCount(); NumJumps++; class'Hat_SaveBitHelper'.static.SetActBits(GetObjectiveBitID() $ "_Jumps", NumJumps); if (NumberParticle != None && NumJumps < 10) { psc = wi.MyEmitterPool.SpawnEmitter(NumberParticle, Player.Location + vect(0,0,30)); psc.SetScale(0.5); psc.SetFloatParameter('Number', (NumJumps-0.5f)/9.f); } if (JumpSound != None) { Player.PlaySound(JumpSound); } if (NumJumps > (IsDeathWishEasyMode() ? MaxGoalEasy : MaxGoal)) { SetObjectiveFailed(0, true); wi.Game.SetTimer(0.5, true, NameOf(KillEveryone), self); } if (NumJumps > (IsDeathWishEasyMode() ? MidGoalEasy : MidGoal)) SetObjectiveFailed(1, true); if (NumJumps > (IsDeathWishEasyMode() ? MinGoalEasy : MinGoal)) SetObjectiveFailed(2, true); }

GetJumpCount relies on act bits, which are values that can carry over to any script or function while a player is in the level. They reset upon leaving.
static function int GetJumpCount() { return class'Hat_SaveBitHelper'.static.GetActBits(GetObjectiveBitID()$"_Jumps"); }

While the rest of the Death Wish script seems fairly normal, there IS one additional script you'll need: an HUD Element. This displays the amount of jumps the player has done so far. And since the base game versions of the HUD are tailored specifically to Mafia's Jumps, we'll need to create our own here.

If you look into Hat_HUDElementNoAPresses, you'll find that most of it can actually be copied over, only changing Hat_SnatcherContract_DeathWish_NoAPresses_MafiaAlien to your DW script's name instead. Here's a downloadable template of the HUD Element to use in your mod: https://cdn.discordapp.com/attachments/792194071787077635/792550479166177290/Hat_HUDElement_NoAPresses_Template.uc

After you've set up everything about your HUD Element, how do you open it within the script? Well, PostInitGame!
function OnPostInitGame() { Super.OnPostInitGame(); if (IsExcluded()) return; SetObjectiveFailed(0, false); SetObjectiveFailed(1, false); SetObjectiveFailed(2, false); Hat_HUD(class'WorldInfo'.static.GetWorldInfo().GetALocalPlayerController().myHUD).OpenHUD(class'YourHUDElementHere'); class'Hat_SaveBitHelper'.static.SetActBits(GetObjectiveBitID() $ "_Jumps", 0); }
Note: If your Jumps DW features a map transition, like say a Jumps DW of Queen Vanessa's Manor, you'll want to run a map check for the original map when setting the act bit back to 0. Only set it to 0 if it's the original map, otherwise the player could cheese part of your Death Wish by simply making their way to the map transition and getting an easy 0 jumps once they're on the new map. Use a similar method to how Speedrun Death Wishes reset to the original map to set the player back to the first map.

If you DON'T have to worry about those shenanigans, then you're almost good to go! Check this template DW script and make sure you have all the remaining functions that weren't mentioned here, such as OnTimePieceCollected: https://cdn.discordapp.com/attachments/792194071787077635/1131798111430398004/Hat_SnatcherContract_DeathWish_NoAPresses_Template.uc
Template Death Wish: Rift Collapse
RIFT COLLAPSE OR YOUR KNEECAPS-

*ahem* I mean, rift collapses are one of the simplest ways to make a Death Wish for a purple rift. Like with speedruns, there is a base script you can extend off of that does most of the work for you already.

Hat_SnatcherContract_DeathWish_RiftCollapse handles most of the effects for a collapse, including the trail the player leaves, the mirror effect, and the wibbly wobbly timey wimey effect on the level. All you need to do yourself is start the timer with either Kismet or script.

Firstly, here's what your DefaultProperties look like. There's barely anything you need to do that the base class doesn't do already:
defaultproperties { UI_PosX = MapPositionX; UI_PosY = MapPositionY; AllowedMaps = ("YourFunniPurpleRift") ActInfo = Your act info; ParentDeathWishes.Add(class'ParentDeathWishGoesHere') RequiredStamps = 10 }

Only other thing you need to add is an override to DW's titlecard grabbing function. For some reason, it tends to bug out and not use the true rift collapse titlecard, wanting to use a purple-ified version of the normal one. This fixes that:
//Cause DW is dumb and doesn't wanna static function Texture2D GetTitleCard() { return Texture2D'HatInTime_Titlecards_DW.caverift'; }

After that, you have two options to set your timer up.

Through Kismet

I prefer this option personally, but it does require an Editor boot, so your choice.

Before you open the Editor, make sure to compile scripts so your rift collapse contract can appear in Has Contract.

Once your level is loaded, open up Kismet, and place a Player Spawn event. Then, place a Has Contract condition with your contract has the specified one. Finally, place a Mini Mission action node. Connect them all like this:

Within the MiniMission action itself, set it up to be a Hat_MiniMissionTimeLimit_RiftCollapse. Most of the settings work by default, but here's a couple I'd change:
TimeLimit/IntroTime - Obviously, you need the player to have enough time to beat your collapse, but not too much so they can breeze through it. It can be difficult judging the time amount since you know your level the best, so an ideal tactic to decide a good amount of time is to go through your rift collapse, collecting every pon, using no major exploits, and then add 30 or so seconds on top of that at the end. Depending on the length of your rift and your general skill level, that 30 may need to be raised or lowered.
Use Real Time - Check this to be true so players can't spam timestop to completely cheese the collapse.
TimeScale - This is at 0.75 by default, which means a second on the timer is actually 1.33 real time seconds, which was how the nerf to rift collapses was made. I'd recommend just setting it back to 1, mainly so calculating a good amount of time to give the player is easier.
Death Wish Timer - Even though Death Wish automatically converts the normal red clock timer to the purple-y death wish timer, I like to check this to be true. Why? Well, if you connect Has Contract's output to be FALSE to start the Mini Mission temporarily, you can test the time you get for your collapse in-editor, saving a compile + cook. Having a death wish timer just simulates the experience a little more.

After you've set up your MiniMission to your liking, there's one more thing you need to do while the Editor is open: Music Trees.

Basically, in your rift's Music Tree, you need to split off your normal level music with a RiftCollapse parameter, and split the Death Wish section with a RiftCollapse_Timeout parameter so you get the panicked 10 seconds left music.

And that's all you need to make a rift collapse! One extra thing you could do is rearrange the rift pons using Destroys/Toggle Hiddens to change out pons, just to make it a TAD bit more interesting.

An example DW script template of making a rift collapse this way:
https://cdn.discordapp.com/attachments/792194071787077635/886067975516471296/Ink_SnatcherContract_DeathWish_RiftCollapse_Template.uc

Through Script

Unless you're making a rift collapse for a base game rift (and they've all gotten rift collapses now, so no?), there's no real beneficial factor to setting up a timer through script, as you still have to open the Editor to set up the Music Tree anyways. If you ARE still curious, however, here's how it works.

First, you'll want to create a class that recreates a timer. Go about this in any way you'd like, copying over Hat_MiniMissionTimeLimit and all its HUD and functionality and whatnot. There IS one quirk that can occur with script timers for whatever reason. UseRealTime is normally left checked so timestop can't make the mission easy, but for some odd reason, using timestop while a script timer with UseRealTime checked to true is active can make the timer rapidly speed up. So if you encounter this weird issue, set UseRealTime to false. Timestop still shouldn't affect it somehow.

You'll also want to make sure the music tree functionality is identical to Hat_MiniMissionTimeLimit_RiftCollapse, so the parameters can remain the same:
MusicParameter = "RiftCollapse" MusicParameterEnd = "RiftCollapse_TimeOut"

After you've set up everything with your timer class, you'll want to start it via your Death Wish's PostInit, assuming the rift doesn't use a mission intro. Simply add a function like this into your script and call it from PostInit:
function StartMiniMission() { local Hat_GameEventsInterface GEI; MiniMission = new class'Hat_MiniMission_Timer_Example'; MiniMission.OnPreBegin(); MiniMission.IsActivated = true; MiniMission.OnBegin(); foreach `GameEventsArray(GEI) GEI.OnMiniMissionBegin(MiniMission); }

And there you go, you went through the convoluted process of starting a rift collapse timer via script! If you need an example of starting the timer this way, look into scripts such as Rift Collapse: Rhythm Jump Studio for examples of how their timers are started. It's not difficult per se; just a lot more tedious than just doing it via Kismet.
Advanced: Spawning, foreaches, and more!
When it comes to making Death Wishes for base game levels, we’re severely more limited due to not being able to add Kismet specifically for the Death Wish, so we have to do EVERYTHING from script. As a few DWs have proved, however, like In Close Quarters, Rush of Excitement, and Mafia Murdering Mania, you CAN still change things substantially. It just requires a lot more effort than Kismet.

Here’s a few things you can use to make base game level Death Wishes just a tad more interesting. A lot of it has some complications and issues to watch out for, so be careful!

Spawning
We’re able to spawn certain actors into the level after PostInit to make traversing just slightly more difficult. Here’s how a simple spawn would work normally:
`GameManager.Spawn(class'Ink_ExampleClass',,, Vect(X, Y, Z), Rot(Pitch, Yaw, Roll),,true);
(The true at the end makes it so the spawn is able to collide with actors as it’s spawned in)

Spawning isn’t always as simple as it seems, however, as there’s a few things you need to watch out for.

Complication #1: AlwaysLoaded
If you’re into script stuff, you should know how much of a pain AlwaysLoaded can be. If an actor is not “AlwaysLoaded”, you can’t extend a class off it, spawn it, or reference it properly in a script. If an actor you’re trying to spawn in is not AlwaysLoaded and refuses to spawn, you almost have no choice but to make a custom class copy of it, and anything else necessary.

Complication #2: bNoDelete
Almost equally as annoying, bNoDelete basically tells the actor they can’t be spawned in or destroyed, no matter what. Like with AlwaysLoaded, if an actor you’re trying to spawn has this set to true, you HAVE to make a custom class to remove it.

Complication #3: Controllers
Not the video game controllers, but rather more so the AI of an enemy or likewise. When you spawn in an enemy, you ALSO have to spawn in their controller; otherwise they can’t behave properly. Not nearly as annoying as the first two complications; just something to remember.
local Ink_Enemy_ExplosiveRumbi_Example Explorumbi; Explorumbi = `GameManager.Spawn(class'Ink_Enemy_ExplosiveRumbi_Example',,, v, r,, true); Explorumbi.SpawnDefaultController();

If you can get past all 3 complications, then you’re set on spawning things into the map! To get their position and rotation values, go into the cooked map in the Editor, place your actor (it won’t save so it can’t hurt anything), and copy over the numbers.
Note: Rotation in script does not work in degrees. It works in Unreal Rotation Units. To translate the degree values to URUs, right click the property value and select “Copy Selected Property to Clipboard”. When you paste it, the value should now be in URUs to use.

Foreaches
So you can spawn things in, but what about modifying existing actors? To change/remove specific actors in a map, you do a foreach within the DW script:
local Hat_PowerPonField PPF; foreach `GameManager.DynamicActors(class'Hat_PowerPonField', PPF) { if (PPF.IsA('Hat_PowerPonField')) { PPF.Destroy(); } }
Using foreaches, you can remove or modify elements of a level entirely. However, similar complications to spawning can arise.

AlwaysLoaded
If an actor you choose to target is not AlwaysLoaded, multiple things happen.
- The foreach itself CANNOT target the actor, or else it will completely EVAPORATE other instances of that actor within mod levels. To get around this, target Actor with the foreach, and use the IsA check to find your actor you wish to manipulate. IsA checks in general do not cause AlwaysLoaded issues, so you can use them safely.
local Actor a; foreach `GameManager.DynamicActors(class'Actor', a) { if (a.IsA('YourTargetClass')) { //Do stuff } }

- You cannot edit property values related to that actor. For example, Snatcher’s boss class is not AlwaysLoaded, so if I target him, I cannot edit any values related to Snatcher. Toilet’s base class, however, is, and Snatcher extends off of that, so I could edit Toilet’s properties if I wished to.

bNoDelete
Same issues as before, not being able to Destroy() the class to remove it cleanly. There is a small alternative you can do, though not as efficient:
if (class.IsA('BNoDeleteClassHeck')) { class.SetCollisionType(COLLIDE_NoCollision); class.SetHidden(true); class.SetDrawScale3D(Vect(0, 0, 0)); }

Kismet modifying
Wait, I thought you couldn’t change Kismet, right? Well, kinda. If you’ve seen Rush of Excitement before, you’d know Empress there is much deadlier than usual. Well, RoE Empress actually isn’t a modification of the existing ones in-level, but new ones spawned in and had the old ones swap out their Object variable references in Kismet! Basically, any Kismet relating to the old, slow Empresses is given to the newly spawned in ones.

How so, you may ask?
function ChangeEmpressObject(Hat_Hazard_RocketLauncher OldEmpress, Ink_Hazard_RocketLauncher_Deadly NewEmpress) { local int i; local Sequence GameSeq; local array<SequenceObject> AllSeqObjects; local WorldInfo w; w = class'Worldinfo'.static.GetWorldInfo(); GameSeq = w.GetGameSequence(); if(GameSeq != None) { GameSeq.FindSeqObjectsByClass(class'SeqVar_Object', true, AllSeqObjects); for(i=0; i < AllSeqObjects.Length; i++) { if (SeqVar_Object(AllSeqObjects(i)).GetObjectValue() == OldEmpress) { SeqVar_Object(AllSeqObjects(i)).SetObjectValue(NewEmpress); } } } OldEmpress.Destroy(); }
Editing note: the (i)s are actually in brackets, and not parenthesis; there was just a formatting issue with Steam.

Using functions similar to this, you can do some basic Kismet modifying...from script! Mostly just as a way to improve making things more difficult, but this little trick does help you get away with a few things you couldn’t normally do. More niche than the other two modifying tricks, but still good to know.

With these tricks, you can step up your base game modifying to the next level!
Making Your Death Wish Special
So you've done it, you've made your Death Wish! You've proven that you can at least script a LITTLE bit, but before you publish the DW to your mod, I have a question for you.

Is it fun? Is it interesting?

As shown by the Death Wish map at the start, we have an absolutely monstrous amount of Death Wishes now. And not all of them are big winners. Sometimes, Death Wishes can blend in with each other for not doing much but placing a small restriction on the player. This can work, like with the candles of Death Wish Plus and such, but other times, you get the 100th Jumps Death Wish.

And on the other hand, you have some "memorable" Death Wishes, but not in a good way. Death Wishes that are so blatantly unfair that you actively grow to hate them. I won't say names, but there are a few of those that exist.

To make sure your Death Wish works as, well, a Death Wish, I recommend getting a few playtesters of varying experience and skill level with the game to try your DW. Something you may find obvious may not be so easy to someone else to figure out, like a major skip in a speedrun DW. Snatcher's tip is very flexible for pointing out a trick to use, so use it well if necessary.

As for making it interesting, try to add at least a few elements into it. Say, for a rift collapse, if you just have the timer, it's nothing more than a purple rift with a timer slapped onto it. Try mixing up the pons in at least unique places to make the player rethink how they play the rift, or adding a few extra elements to the level itself. The little things can make a big difference in making people wanna replay your DW!

And most importantly, remember:


If you think your Death Wish meets all standards, then send it out into the wild that is the Workshop! Release your chaotic evil!
10 коментара
HolyAlbey 13 авг. в 8:40 
va-nessa mod n-eed-ed
SuperInkLink  [автор] 29 септ. 2023 в 18:36 
Program Files (86) > Steam > steamapps > common > HatInTime > Should be there (make sure the editor's installed)
deborasantos142009 29 септ. 2023 в 16:49 
WHERE DOES THE DEVELOMENT FILE IS
SuperInkLink  [автор] 3 дек. 2021 в 16:11 
Oh, silly me. I haven't updated that photo.
RanDumb 3 дек. 2021 в 13:55 
funny thing is, I HAVE MORE DEATHWISHES THEN YOU DO. (im a masochist, i have about 100 more snatcher stamps worth of deathwishes then u do lol)
313phant 11 юни 2021 в 22:52 
oh i found out myself
313phant 11 юни 2021 в 8:05 
Could you help me with these problems?:
I don't know which folder to put scripting in.
I don't know map names.
I don't know act names.
certifiednoob48 12 ян. 2021 в 0:30 
Wow this took a lot of effort
tennoji 3 ян. 2021 в 20:24 
i dont think i should be allowed to know how to do this
TheGreatAndPowerfulWeegee 1 ян. 2021 в 9:05 
Really cool guide and a good resource to refer to when adding challenges to your levels! :necroheart: