On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Ada18980

149
Posts
8
Topics
2,911
Followers
4
Following
A member registered Feb 19, 2022 · View creator page →

Creator of

Recent community posts

Training gains xp when you enter the perk room. The amount of xp is based on what percentage of the time you were wearing heels in the last level.

So youre in luck as I have reworked the community structure and now there is a mods section. Might take a while for people to post their mods there but there are a couple.

If you are having a hard time you can go to the challenge tab when starting a new game and give yourself some extra perk points.

In 5.2 you need to do the same in another elevator floor, then the two will be linked

In 5.3 (work in progress version) same thing, but there is also The Summit which is a special always available room

You can save, edit outfit, then reload

The Summit is reached by clearing an elevator hall. Once you’re there you can use facilities like the Recycler to turn mismatched restraints into restraints of your choice, train prisoners as rescue maids, put your prisoners on display, store items, etc

At least those are the goals, it’s quite a complex feature so it’s taking a lot of time.

Kinky Dungeon community » Mods » Tutorials · Created a new topic Tile Editor
(2 edits)

There is a map editor available in debug mode. The purpose of it is to allow me to easily make tiles that are used in worldgen. You can use it too though, if you’d like to make custom tiles in your mod.

The tiles you make won’t be visible ingame unless you hit Commit Editor Tiles. This is to keep your mistakes from crashing the game. If you want to reset your editors tile database with the builtin one, simply refresh the page and click Reset Tile Database.

In order to make them appear ingame from a mod, you can copy the individual tile (using “copy tile to clipboard” in the editor) and add them to the main array:

KDMapTilesList.MyTile = `{"name":"HallCross","Labels":{},"w":1,"h":1,"primInd":"udlr","index":{"1,1":"udlr"},"flexEdge":{},"flexEdgeSuper":{},"scale":7,"category":"urban","weight":10,"grid":"1110111\n1111111\n1111111\n0111110\n1111111\n1111111\n1111111\n","POI":[],"Keyring":[],"Jail":[{"x":28,"y":10,"type":"furniture","radius":1},{"x":29,"y":10,"type":"furniture","radius":1},{"x":41,"y":20,"type":"furniture","radius":1},{"x":11,"y":18,"type":"jail","radius":1}],"Tiles":{},"effectTiles":{},"Skin":{},"inaccessible":[{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"l","dir2":"r"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"l","dir2":"u"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"l","dir2":"d"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"r","dir2":"l"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"r","dir2":"u"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"r","dir2":"d"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"u","dir2":"l"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"u","dir2":"r"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"u","dir2":"d"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"d","dir2":"l"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"d","dir2":"r"},{"indX1":1,"indY1":1,"indX2":1,"indY2":1,"dir1":"d","dir2":"u"}],"tags":["urban"],"forbidTags":[],"requireTags":[],"indexTags":[],"maxTags":[],"bonusTags":[],"multTags":[],"notTags":[]}`

Basics

The basic format of world generation is as follows:

  1. Create a maze (using Primm’s algorithm)
  2. Convert the maze into a list of ‘indices’. An ‘index’ is basically a description for what type of entrances a tile has. Each junction in the maze becomes an index, and it is added to an array.

Other map types (like the ancient vault, bandit hideout, etc) use a different map generation algorithm, but this is the basic one.

The ‘tile index’ selector lets you choose which sides of the tile have entrances on them. The game uses this to fit the tiles together to stitch the maze together. It’s very important as otherwise there would be no guarantee the map is possible to traverse.  image.png 3) For each index, the map generator tries to find an appropriate tile that matches the index. For example, for an ‘up + down’ index it will look thru the tiles that have an up-down index, which are mostly vertical hallways.

  1. The game places those selected tiles on the map, then moves to a random new index to start the process anew.

Note: There are two new things you can tick on each index:

-Flex: The entrances on this tile dont need to match the maze generator so long as 1) The destination tile of the entrance is already accessible by a different route and 2) The map tile you are making has found at least one valid entrance

-Open Border: This is for tiles where all four corners are accessible. It skips check #1 of the flex feature, but doesn’t do anything if the index is not flex

Available tilesets (as of this writing)

tmp - lost temple ore - orerry jng - jungle tmb - tomb grv - graveyard cat - catacombs lib - library cry - crystal cave bel - bellows DemonTransition - Edge of Reality shoppe - starting shop shrine - perk room cst - caldera/grotto vault - ancient vault (5.3 only)

You can use this feature to see how the tile would look in different regions

Updating your editor database

The map editor keeps a different database from the game’s internal database, so you may need to refresh it after a game update.

To merge new official map tiles into your editor after a game update:

  1. Copy your current tile array to back them up. Best to save them in a text file in case your computer crashes
  2. Reset your tile database. This will pull the game’s hardcoded stuff to your editor. Note that this will NOT work if you have already clicked Commit. Also, it will overwrite everything, so make sure it’s backed up
  3. Click Merge From Clipboard to merge the tiles you copied in (1) back into the database
  4. Save any tile

This will basically return your tiles to the most up-to-date slate then overwrite them with tiles from your previous editor setup to add back tiles you made yourself

image.png

Kinky Dungeon community » Mods » Tutorials · Created a new topic FAQ
(3 edits)

Question: My mods keep registering as a virus on other people’s computers. What do I do?

Answer: You have two options:

  1. Windows sees .js files in a zip as a virus for some reason. You can rename .js files to .ks files to prevent this.
  2. You can use 7zip to make the zip file instead of the native windows compression tool

Question: How can I upload mods so other people can download?

Answer: You can use MEGA or another external file host, or you can create a project in Itch.io and upload it there.

Question: Why can’t I see the mods forum on the discord?

Answer: Check the ⁠rules post

Question: How do I format the files in a mod?

Answer: A mod should be a .zip file, and if autoloading (offline version only) you should place it in the Mods/ folder Make sure that the mod files are in the root of the zip file. For example, if your mod is called MyMod.zip, make sure there is NOT a MyMod folder that contains all the files. If you do this you will not be able to load custom assets.

Question: How do I add a new perk in its own category?

Answer: In a .ks file, your code should look like this:

KDCategoriesStart.push(
    {name: "MyPerks", buffs: [], debuffs: [],}
    );
addTextKey("KDCategoryMyPerks","My Perks");
addTextKey("KinkyDungeonStatMyFirstPerk","My First Perk");
addTextKey("KinkyDungeonStatDescMyFirstPerk","Spend perk points to asset dominance.");
KinkyDungeonStatsPresets.MyFirstPerk= {startPriority: 100, category: "MyFirstPerk", id: "MyFirstPerk", cost: 2, tags: ["start"]};

KDPerkStart.MyFirstPerk= () =>
{
    // Insert code to run at the beginning of the game
};

KDLoadPerks(); // needed to reload the perk list

Question: How do I run code at the start of the game without a perk? Answer: Simplest way is to add an event that occurs during quest handling, checks if a flag is set, and if it isn’t, run your code and set the flag. In earlier versions this is the way to add an event:

// Initialize postQuest if it isn't already present
if (!KDEventMapGeneric.postQuest) KDEventMapGeneric.postQuest = {};
KDEventMapGeneric.postQuest.myEvent = (e, data) => {
  if (!KinkyDungeonFlags.get("myEventFlag")) {
    // Run my code
    KinkyDungeonSetFlag("myEventFlag", -1);
  }
}

in versions 5.3 and after you can use KDAddEvent() instead:

KDAddEvent(KDEventMapGeneric, "postQuest", "myEvent", (e, data) => {
  if (!KinkyDungeonFlags.get("myEventFlag")) {
    // Run my code
    KinkyDungeonSetFlag("myEventFlag", -1);
  }
});

Question: How do I check if the player has a perk?

Answer: You can use KinkyDungeonStatsChoice.get("MyPerk"), or in 5.3 and beyond: HasPerk("MyPerk")

(1 edit)

For modding purposes, the game source is available here: https://github.com/Ada18980/KinkiestDungeon/tree/newartwork

The clothing template for custom assets is available on the game page in the Demo section.

Kinky Dungeon community » Mods » Tutorials · Created a new topic Enchantments
(2 edits)

Welcome to the enchantments tutorial. Kinky Dungeon 4.35 added a new feature that allows you to create enchanted variants of items. These can be either restraints or armor–they are treated identically as far as wearing them goes. Enchantments typically provide an effect and also change the tooltip using an event (more on that later)

If you want to make your own custom enchantments, the process is simple. There are a few places to start. The first is understanding the enchantment list:

/**
 * Contains a list of enchantment variant types
 * Can be modified dynamically so mods can add basic curses
 */
let KDEnchantVariantList = {
     "Common": [
        "Evasion",
        "Sneak",
        "Accuracy",
        "SpellWard",
        "BondageResist",
        "DamageResist",
        "DamageBuff",
        "ManaCost",
        "ManaRegen",
        "BaseDamageBuffMelee",
    ],
    "Gold": [
        "ElementalEcho",
        "ElementalDmg",
        "BaseDamageBuffMelee",
        "BaseDamageBuffMagic",
        "ManaRegenOnKill",
        "DamageBuff",
        "ManaCost",
    ],
};

This list contains what are effectively “loot tables” but for enchantments instead. The “Common” enchantment list contains a list of enchantments that are applied to items from relatively low level loot, while the “ Gold” list contains enchantments that are meant to be more rare and special and are therefore added only to gold chests. To start with, in your mod you need to add your enchantment to this list.

KDEnchantVariantList.Common.push("MyEnchantment")

Next, you have to create your enchantment tooltip, otherwise the player will not know what it does. To start, add the text key:

addTextKey("KDVariableModifier_MyEnchantment", "Halo: AMNT fire damage to nearby enemies");

Of note is the first line, which is formatted so the default tooltip system will detect it. The second is what appears ingame. AMNT is replaced with a variable you supply as part of the enchantment

Third, you must define the enchantment itself. The enchantment object is contained in KDEventEnchantmentModular. This list contains the data that makes enchantments work–an enchantment definition contains 3 things: a level (which defines how powerful it is, usually 1-10 for low level enchantments, 11-20 for medium, and 20+ for very rare unique stuff) a weight function (returns the weight for the system to calculate the rarity of the enchantment) an event list (a list of special code injections that the game appends to any events the restraint/armor has to begin with)

For our enchantment, we will make it deal fire damage to nearby enemies. This is the code to add the enchantment:

KDEventEnchantmentModular.MyEnchantment = {
            2: null, //consumable
            1: null, //weapon
            0: /*restraint*/{
        level: 9, // It's fairly powerful but also in the common list, so 9 should fit
        weight: (item) => {
            return 3; // It should be rare since it is quite powerful.
        },
        events: (item, Loot, curse, primaryEnchantment) => {
            let power = Math.max(KDGetItemPower(item), 4); // This line doesn't need to be modified
                        //but if you want the enchantment to have a strong minimum bar despite the restraint power,
                        //increase 4 to something higher
            let amt = 2 + Math.round(KDRandom() * 3 * Math.pow(power, 0.75));
                        // The exponent helps keep high level restraints from being too powerful.
                        // The randomness is there to make the 
                        // The 2 is the base amount which is always added to the damage amount
                        // The 3 is the amount of damage per unit of restraint power
                        // Since the minimum is 4 power, the minimum damage per turn this restraint deals
                        // will be 2 to 14 damage per turn. 
            amt = KDGenericMultEnchantmentAmount(amt, item, Loot, curse, primaryEnchantment); // This line doesnt need to be modified
 
            return [ // This is the event list
                // This is the main line that gives the enchantment its effect
                // Note that in KD all damage values are internally 1/10 of what they are displayed as, so we divide by 10
                // Note: This event does not exist! We will add it next section
                {trigger: "tick", type: "damageEnemiesNearby", power: amt/10, damage: "fire", dist: 1.5, inheritLinked: true, sfx: "FireSpell"},
                // This is the tooltip. msg should be your enchantment name, colors should be set to what you want
                {trigger: "inventoryTooltip", type: "varModifier", msg: "MyEnchantment", power: amt, color: "#ff0000", bgcolor: "#ff5555"},
                // This is the the event that gives the icon a glow in the inventory. We gave it an orange color
                // power should be set to the enchantment's power
                {trigger: "icon", type: "tintIcon", power: 9, color: "#ffaa44"},
            ];}
  },
};

Now, this enchantment will appear in your regular chests when you get an armor item. You may want to set the weight to 9999 to help test it. However, it won’t do anything! This is because the event damageEnemiesNearby does not exist yet! To add an event, we will have to modify the inventory item event list. Here is the code:

KDEventMapInventory.tick.damageEnemiesNearby = (e, item, data) => {
  // This gets the enemies near the player
  let enemies = KDNearbyEnemies(KinkyDungeonPlayerEntity.x, KinkyDungeonPlayerEntity.y, e.dist || 1.5);
  for (let enemy of enemies) {
    // This checks if they are hostile and not already down
    if ((!e.chance || KDRandom() < e.chance) && enemy.hp > 0 && !KDHelpless(enemy) && KDHostile(enemy)) {
      // This deals damage
      KinkyDungeonDamageEnemy(enemy, {
        type: e.damage,
        damage: e.power,
        }, false, true, undefined, undefined, KinkyDungeonPlayerEntity);
      // This plays a sound effect if desired
      KinkyDungeonPlaySound(KinkyDungeonRootDirectory + "Audio/" + e.sfx + ".ogg");
    }
  }
}

This should cover how to add a new enchantment with a new effect. If you want to apply the enchantment directly to an item, you can use KDGiveInventoryVariant(variant) or KDEquipInventoryVariant(variant) and define variant like so:

KDGiveInventoryVariant({
  template: "TrapGag", // Make it a ball gag
  events: KDEventEnchantmentModular.MyEnchantment.types[0].events("TrapGag", {amtMult: 2.0}), // You can make it extra powerful with amtMult
});
(3 edits)

For something simple, like a zombie that chases the player and adds existing restraints (we’ll have it add maid restraints as an example), you just need: -A sprite (place a .png in the Enemies/ folder) -A few text entries in Text_KinkyDungeon.csv -An entry in KinkyDungeonEnemiesList.js

Step 1: adding a sprite

This process is entirely up to you on how you do it. I use Aseprite (paid) to edit files but you can use Paint.net (free), graphics gale (free), or even just MS paint. There are also online pixel art tools available. The only restriction is that the sprite must be 72x72 and the game does adhere to a strict chibi style, so you may want to use something like Aseprite or Graphics Gale or some other pixel art centric program that lets you use a limited color palette.

Step 2: Text entries

This part is also really simple. All enemies need the following two entries by default. Our enemy will be called MaidZombie in the code, but the name in the code is different from the name that appears ingame. The NameENEMYNAME entry determines the name in the game, and the KillENEMYNAME entry shows when the enemy is defeated.

NameMaidZombie,Maid Zombie
KillMaidZombie,"The maid zombie does a short curtsey and runs away."

Next, you will need entries for the enemy attacking you. This part is similar, but you have to leave RestraintAdded and DamageDealt unchanged in the examples as these specific keywords are replaced with the actual values ingame.

AttackMaidZombieBind,"The maid zombie binds you! (+RestraintAdded) (DamageTaken)"
AttackMaidZombie,"The maid zombie tickles you! (DamageTaken)"

Note that AttackENEMYNAMEBind is what shows when the enemy applies a restraint, and AttackENEMYNAME is simply what happens when the enemy can’t apply a restraint (because you are too bound or too high stamina). Keep the text in quotes to avoid issues with commas breaking the csv system.

Step 3: JSON

This is the most technically complicated part. If you use an IDE like Visual Studio Code (which I highly recommend, it’s basically a free text editor except you can hover over fields to get a description of what they do), it’s a little easier. But if you don’t use an IDE, you can check KDTypeDefs.ts (specifically the enemy entry) for some limited descriptions.

Anyway, let’s look at our JSON entry for a basic enemy:

  // Name: Self explanatory
  // bound: The sprite in EnemiesBound/ that the enemy uses when the binding bar goes to max. This also marks the enemy as humanoid (so they can help you out of binds) and bindable.
  // playLine: The enemy's 'voice' basically. Search 'KinkyDungeonRemindJailPlayTemplate0' in the text definitions if you want to make your own. Replace 'Template' with your playLine
  // clusterWith: When this enemy is created, it will have a chance to try to create enemies that share a tag defined by clusterWith.
  // In this case, it will try to generate other enemies with the zombie tag.
  {name: "MaidZombie", bound: "Zombie", playLine: "Zombie", clusterWith: "zombie",

 
  // followRange: The distance the AI will try to approach the player to. 1 is melee. 2, 2.5, 3, all work for a ranged enemy.
  // AI: There are several AI types.
    // hunt means the enemy will search for the player and chase.
    // wander means the enemy will move around but wont chase.
    // guard means the enemy will hold position and chase the player, but only to about 2x the vision range
  // guardChance: This enemy has a 40% chance of spawning with the 'guard' AI instead of the 'hunt' AI.
  followRange: 1, AI: "hunt", guardChance: 0.4,
  // These are the enemy's tags. Check other enemies to get an idea of what values to put here.
  // Of note is that 'ignoreharmless' makes the enemy ignore you when you are out of SP
  // Replace 'ignoreharmless' with 'leashing' if you want the enemy to leash the player to jail
  // 'opendoors' allows the enemy to open doors
  // 'closedoors' mmakes the enemy sometimes close doors behind herself
  // 'ribbonRestraints' means the enemy will use ribbon restraints. This is a tag declared on the ribbon restraints themselves
  tags: KDMapInit(["ignoreharmless", "zombie", "melee", "fireweakness", "ribbonRestraints", "meleeweakness"]),
  // evasion: Positive values improve evasiveness (formula for hit chance is 1/(1+evasion)). Negative values work the opposite way, multiplying the hit chance by (1 - evasion)
  // armor: flat value subtracted from attacks. Note that all hp, armor values are multiplied by 10 ingame
  // maxhp: enemy hp. Multiplied by 10, so this zombie will have 100 hp displayed ingame
  // visionRadius: sight radius. 5 is for nearsighted enemies, 8 is for observant, 9 and 10 are truly extraordinary/bosses
  evasion: -1, armor: 1, maxhp: 10, visionRadius: 5, 
  // minLevel: Minimum floor enemy appears on
  // weight: basic weight for spawning the enemy. The final enemy selected to put on a square is a weighted average of every enemy in the game
  minLevel:1, weight:6,
  // movePoints: How many turns between moves. 3 means that the enemy waits 2 turns between moves. 1 means it moves every turn.
  // attackPoints: How many turns per attack. 3 means the enemy takes 3 turns to attack the player. 1 means it's instant.
  // attack: type of attack. This is just a string, so you add the keywords together
    // Melee means the enemy will use warning tiles. Spell means the enemy will cast spells. These are not exclusive
    // Bind means the enemy will bind the player if it hits
    // Will means the enemy will drain Willpower (stamina, old name for it in the code) even if it binds you. Otherwise it will only drain fullBoundBonus sp on a hit if the player is fully bound
    // Lock makes the enemy apply one lock if able, instead of binding. LockAll makes the enemy lock all eligible restraints at once.
  // attackWidth: Attack width, basically how much to the side the attack goes. Decimal values are OK. Note that only one target will be hit regardless of area size.
  // attackRange: Range of the melee attack. This can be used for ranged melee attacks
  // tilesMinRange: This is how much the attack range is 'offset' forward. Usually it is set to 1 for enemies with wide attacks so the attack doesn't reach behind them.
  // power: Damage the enemy does. If attack type includes 'Will' it will do this damage every turn it hits the player
  // dmgType: Damage type. Different types have different effects on stamina, distraction, mana, etc
  // fullBoundBonus: This is added to the power if the player can't be further bound, dealing bonus damage
  movePoints: 3, attackPoints: 3, attack: "MeleeBind", attackWidth: 1, attackRange: 1, power: 1, dmgType: "grope", fullBoundBonus: 3,
  // terrainTags are modifiers to the enemy's spawn weight.
    // secondhalf and lastthird are sort of legacy. secondhalf applies on floors with a single digit ending in 2, 3, 4, 5, while lastthird applies on 4, 5
    // some enemies have something like 'conjureRage' or 'ropeAnger'. Anger is the less serious version of rage, and basically this is used to make some enemies appear more when a goddess is low rep
  terrainTags: {"secondhalf":10, "lastthird":14},
  // These are the tilesets from KinkyDungeonMapParams.js that the enemy appears on. It is a list[] converted to a map on runtime.
  // You can replace 'floors' with 'allFloors: true' to make her appear on all tilesets
  floors:KDMapInit(["grv"]),
  // This is a list [] of loot. Add multiple entries if desired, follow the format as in other enemies. This one drops only gold.
  dropTable: [{name: "Gold", amountMin: 5, amountMax: 10, weight: 10}]},

Here’s a few extra, advanced tags:

spells: [] -- a list of spell names. The enemy chooses one at random (that it can cast), and you have to add 'Spell' to its attack: "" field

followLeashedOnly: boolean -- This confusingly named tags causes the enemy to, IF the player is being leashed, only FOLLOW her. As opposed to casting spells, etc, at her (and possibly the leasher) as she is being dragged off.

spellRdy: boolean -- Shows a spell indicator when the enemy's spell cooldown is ready

spellCooldownMult: 2, spellCooldownMod: 1 -- Normally enemies use the manacost of a spell to determine spell cooldown. This lets some enemies made modifications to all of their spells (multiplying first, then modifying)

spellResist: number -- Spell resistance is similar to evasion but instead of multiplying hit chance, simply multiplies damage taken from magic damage types. It's similar to the magicresist tag but allows for more control. 

projectileTargeting: boolean -- This makes it so the enemy will set its followRange to 1 if the player is blocked. This is used for ranged attackers including spellcasters to make them walk forward instead of standing there

projectileAttack: boolean -- This makes it so an enemy can't attack you from behind a wall. Meant to reduce frustration from being hit by a shuriken through a wall or being pulled through a wall by a frog's tongue

dontKiteWhenDisabled: boolean -- This makes it so the enemy stops kiting the player when the player is stunned. Maidforce ninja uses this behavior so she moves in to bind when her stun grenade hits you, but backs off when it wears off (she has followRange: 1 and kite: 1.5)

kite: number -- This makes it so an enemy kites you until it's greater than the specified distance. 1.5 is sufficient for most enemies but some have a longer minimum range on their spells (like enemies who shoot rubber bullets) 

blindSight: number -- This is similar to vision radius but also sees thru walls
alwaysEvade: boolean -- This is used on ghosts to make them always dodge melee attacks if they aren't made with a magic weapon

bypass: boolean -- makes an enemy slip things like plugs, etc, underneath chastity belts

summon -- is a complex tag that allows you to spawn additional enemies when the enemy is created at mapgen. See the ropekraken for example

ondeath -- see the mushrooms for an example of casting a spell on death, see the bigslime for example of creating additional enemies ondeath. You can also use it to trigger a dialogue (see Fuuka) or to start or end a quest (see the DragonLeaderDuelist)

specialdialogue: string-- starts a dialogue when you click on her instead of the generic ally dialogue. Generally used on enemies that belong to an always neutral faction, like Prisoner or Player. If you use it on an enemy faction then you still cant talk to them while they are hostile, you will have to use a dialoguetrigger instead which is a different can of worms but let me know if you'd rather have a tag that overrides this (allowspecialdialoguewhilehostile)
(3 edits)

Hi! This tutorial covers preloading asset textures, which is necessary if you want the game to properly load assets! There are two ways to preload:

  1. using a texture packer (such as TexturePacker) to create a texture atlas, and then loading the atlas
  2. Loading each sprite manually as part of a list

1) Using a texturepacker

This one is relatively simple in terms of steps, but requires that you figure out a texture packing solution. I use texturepacker mainly out of habit, but I believe there are many free options as well. The game uses pixi.js, so make sure your packer is outputting .json atlases that are compatible with pixi.js.

Once youve generated your atlas (make sure the root folder is set properly to the Models/ folder, so that filenames start with Models/) you can load the atlas:

// Note: Requires Kinky Dungeon 5.1.7 or later
PIXI.Assets.load({
   src: "TextureAtlas/livingCageAtlas.json",
   loadParser: 'modAtlasLoader'
});

With this method make sure to include the .png and .json produced by the atlas. You can put them in the TextureAtlas folder in the root of your mod zip.

You must also include the original asset files in the “Models/” folder as the game is not smart enough to create blobs for the texture assets yet 😦

2) Loading each sprite manually

For this I use a list to make things easier:

let FileList = [
  "Item.png"
]

for (let dataFile of FileList ) {
PIXI.Texture.fromURL(KDModFiles["Models/" + dataFile], {
  resourceOptions: {
    scale: 0.5 // You can change the scale to reduce the size of the file and improve performance
  }
});
}

The problem with this method is that performance is worse as it also loads a bunch of transparency data. Texturepacker strips the excess area which makes rendering much faster as the engine doesnt have to render empty space. But you don’t need a texturepacker. This works well for assets close to the face as you can crop out most of the image while preserving offset data.

With this method you have to keep your files in the Models/ folder in the root of your zip. You could theoretically put them in Data as well but Models is the established location for them

Kinky Dungeon community » Mods » Tutorials · Created a new topic Buffs
(2 edits)

Buffs are temporary status effects that can affect the player or enemies. They’re often used for other things too, for example buffs are used to store the current value of the player’s shields and the amount of Battle Rhythm the fighter class has. Here is an example buff, the Drenched debuff:

{id: "Drenched", type: "fireDamageResist", aura: "#59a0d1", aurasprite: "Drenched", power: 0.425, player: true, duration: 20, enemies: true, events: [
    {type: "RemoveDrench", duration: 1, trigger: "tick"},
    {type: "ApplyConduction", duration: 1, trigger: "tick", kind: "invis"},
    {type: "ApplyConduction", duration: 1, trigger: "tickAfter", kind: "invis"},
]};

Here’s a non-exhaustive list of properties a buff can have:

id: The name of the buff. if there is a buff icon, the icon file is named ("buff" + id) and is located in the Buffs/buff folder (CASE SENSITIVE!)

type: The stat that the buff increases. This is linear. For example, this one adds 0.425 to fireDamageResist.
You can get the resulting buff using KDEntityBuffedStat(entity, buff), e.g. KDEntityBuffedStat(KinkyDungeonPlayerEntity, "fireDamageResist")
entity can also be an enemy

aura: The color of the buff ring around the player. This will also cause the buff to show up in the player's buff list. Set this to undefined if you want the buff to be hidden and invisible

aurasprite: Set to "Null" if you don't want a ring around the player. You can also add your own aura sprite to the Aura folder.

buffSprite: set to True if you want to use a custom sprite for the buff sprite in the buff list. This needs to be declared to prevent crashes or glitches from buffs without a sprite

noAuraColor: Do not colorize the ring around the player
text: The text displayed on the buff icon in the buff list

buffTextReplace: You can use a javascript object to replace certain text in the tooltip as well. E.g. {PERCENT: "100%"} will replace the string 'PERCENT' with '100%'

power: The amount this buff increases the stat by

duration: An integer that describes how long the buff lasts

player: If this buff is attached to a spell, it determines whether the buff is applied to the player if the player is in a spell's radius

enemies: If this buff is attached to a spell, it determines whether the buff is applied to a NPCs in the radius

noAlly: Does not affect NPCs that are allied to the player, only for application by spells

onlyAlly: Only affects NPCs that are allied to the player, only for application by spells

range: For application by spells, the radius this buff is applied at

tags: A list of tags that apply to the buff. There are helpful functions like KinkyDungeonRemoveBuffsWithTag which can use these

events: List of events that apply to the buff

hideHelpless: hide the buff if the enemy is helpless

To apply a buff you can use:

KinkyDungeonApplyBuffToEntity(entity, buff);

Buffs have a ‘type’ and a ‘power’. This allows you to get the total value of a buffed stat. For example, if you want to get the player’s total fire damage resistance:

KDEntityBuffedStat(KDPlayer(), "fireDamageResist")

To convert linear values into a multiplier (for example, for damage resist), you can use KinkyDungeonMultiplicativeStat(number). This applies a simple formula. The function returns 1 if you give it 0. Positive values return a value between 0 and 1, while negative values are added to 1. This is the basic formula used to calculate damage resistances in the game.

Sadly Im not the one who makes the mods. There has been talk on finding a place to host mods but a lot of sites do have limits on what nsfw files can be hosted.

You can enter the wardrobe from the title menu. Its retroactive as the game doesnt store your outfit in the save code, but in a different location

You need to react in the rules post :)

It is in :)

You can find them all over some levels, especially ancient tombs

Im planning to put together a roadmap at some point.

To put it simply, theres a lot of interconnected parts in the next update and its taking a while to get everything to a state where theres enough content to keep all the pieces interesting.

Theres a player base area (the summit). Theres facilities like a restraint recycler. There still needs to be more to flesh out some of the new doll processing room mechanics.

What kind of voicing?

Theres too much dialogue to voice and most of it isnt necessary to voice.

Gag moans would be nice I guess

Are you running with an option that clears data when you close the browser?

Ah sorry, will have to check.

Tracking bugs is a bit tough

It may be due to cache settings, if your phone deletes the cache after closing the tab it will wipe game data.

As for crashes, unfortunately I dont have an iphone to test on but there are some optimizations Ive been looking at to hopefully reduce the footprint.

Oops! An oversight :)

Each boss has like, over a hundred lines of dialogue so its easy to miss. Thank you.

It was a past attempt at figuring out a mac version.

Because I dont have an apple device its quite difficult for me to test. So I havent been able to get it working yet.

Tbh an apk would just be an integrated browser. The main advantage would be offline play which is a reasonable ask. Maybe I can figure it out at some point.

You must click the ball gag on the menu logo :)

So just a note on this.

For 5.3 I am working on a rework of the doll room prison that you get put into for the final area. Its been a major pain to balance and fix bugs.

So the simple answer to “why not” is that I am working on more end state scenarios but developing prison scenarios takes much more time for less content.

So basically it will take time :)

Perhaps eventually, I am not well versed in Android but there is electron support for android so it may be possible.

Yeah besides debug mode you get it once per level.

It does autosave though as long as you are not in incognito.

Otherwise I recommend booping the gag in the menu and then using debug mode options in the pause menu to get a save code

Are you in Roguelike mode?

Have you tried reducing gfx settings?

How much more free can it get than free… Lol.

Party members do get kidnapped :) at least you are supposed to be able to find them again later on

You can add party members though

(1 edit)

Wiggling? I have not heard of this bug…

Or if you mean loading is broken, try toggling the “load hi res textures” option on and off (restart both times)

Thanks for reporting, someone else notified me and I was able to fix the old version >.<

Ah yes sorry.

Thats really weird… Im glad you found a solution at least? >.<

(1 edit)

I am not familiar with current embargos. Does Patreon work?

This is really strange.

I’d check a couple of things:

  1. Is your system 64 bit? I assume so, did you get the 64 bit version?
  2. Is your system ARM64 instead of 64 bit? ARM cant emulate x64 executables

This game isn’t a java game, it is html5 and javascript. I don’t believe Java is required. Electron.js is the runtime, which should package all the needed stuff with the executable. Electron.js doesnt really give a specific set of system requirements so Im not sure why this would happen.

How much VRAM does your graphics unit have?

(1 edit)

Ah yes I remember this bug thanks for calling it out! And good call on the sales haha