From f88fecd6fd60fc7bcadc36a88f727a9ce6dfacef Mon Sep 17 00:00:00 2001 From: Kirsty <kirsty.degreesoflewdity@gmail.com> Date: Sun, 12 Jan 2025 12:06:24 -0500 Subject: [PATCH] Combat hair gradients. --- .../05-renderer/18-combat-renderer.js | 86 +++++++++++--- .../05-renderer/21-npc-options.js | 6 + .../05-renderer/21-player-options.js | 14 +-- game/04-Variables/colours.js | 110 +++++++++++++++++- types/npc.d.ts | 2 +- 5 files changed, 195 insertions(+), 23 deletions(-) diff --git a/game/03-JavaScript/05-renderer/18-combat-renderer.js b/game/03-JavaScript/05-renderer/18-combat-renderer.js index 8f59b6b9bb..b5f153275a 100644 --- a/game/03-JavaScript/05-renderer/18-combat-renderer.js +++ b/game/03-JavaScript/05-renderer/18-combat-renderer.js @@ -384,7 +384,7 @@ class CombatRenderer { * @returns {Partial<CompositeLayerSpec>} */ static createHairColourGradient(hairPart, gradient, hairType, hairLength, prefilterName) { - const combatHair = CombatRenderer.getHairGradientType(hairType); + const combatHair = CombatRenderer.getHairGradientType(hairType, gradient); const filterPrototypeLibrary = setup.colours.hairgradients_prototypes[hairPart][gradient.style]; const filterPrototype = filterPrototypeLibrary[combatHair] || filterPrototypeLibrary.all; /** @type {Partial<CompositeLayerSpec>} */ @@ -656,14 +656,61 @@ class CombatRenderer { return CombatRenderer.lookupColour(setup.colours.hair_map, V.haircolour, "hair", "hair_custom", "hair"); } if (["wide flaps", "hime", "curtain", "mohawk"].includes(V.fringetype)) { + return this.getFringeFilter(); + } + if (V.hairColourGradient.style === "split") { + const index = V.position === "missionary" ? 0 : 1; + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[index], "hair", "hair_custom", "hair"); + } + return CombatRenderer.createHairColourGradient( + "sides", + V.hairColourGradient, + CombatRenderer.getHairSideType(), + hairLengthStringToNumber(V.hairlengthstage), + "hair" + ); + } + + /** @returns {string} */ + static getHairLength() { + if (["wide flaps", "hime", "curtain", "mohawk"].includes(V.fringetype)) { + return V.fringelengthstage; + } + return V.hairlengthstage; + } + + /** + * @param {TransformationParts} part + * @returns {Partial<CompositeLayerSpec>} + */ + static getPartFilter(part) { + if (V.hairColourGradient.style === "split") { + if (["tail", "pubes"].includes(part) || (["wings", "ears"].includes(part) && CombatRenderer.getPosition(V.position) === "missionary")) { + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[0], "hair", "hair_custom", "hair"); + } + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[1], "hair", "hair_custom", "hair"); + } + if (V.hairColourGradient.style === "high-ombre") { + if (["tail", "pubes", "wings"].includes(part)) { + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[0], "hair", "hair_custom", "hair"); + } + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[1], "hair", "hair_custom", "hair"); + } + if (V.hairColourGradient.style === "low-ombre") { + if (["tail"].includes(part)) { + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[0], "hair", "hair_custom", "hair"); + } return CombatRenderer.createHairColourGradient( - "fringe", - V.hairFringeColourGradient, - CombatRenderer.getHairFringeType(), - hairLengthStringToNumber(V.fringelengthstage), + "sides", + V.hairColourGradient, + CombatRenderer.getHairSideType(), + hairLengthStringToNumber(V.hairlengthstage), "hair" ); } + if (V.hairColourGradient.style === "face-frame") { + return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairColourGradient.colours[1], "hair", "hair_custom", "hair"); + } return CombatRenderer.createHairColourGradient( "sides", V.hairColourGradient, @@ -680,19 +727,26 @@ class CombatRenderer { if (V.hairFringeColourStyle === "simple") { return CombatRenderer.lookupColour(setup.colours.hair_map, V.hairfringecolour || V.haircolour, "hair_fringe", "hair_fringe_custom", "hair_fringe"); } + if (V.hairFringeColourGradient.style === "split" && this.getFringeType() !== "mohawk") { + const index = V.position === "missionary" ? 0 : 1; + return CombatRenderer.lookupColour( + setup.colours.hair_map, + V.hairFringeColourGradient.colours[index], + "hair_fringe", + "hair_fringe_custom", + "hair_fringe" + ); + } return CombatRenderer.createHairColourGradient( "fringe", V.hairFringeColourGradient || V.hairColourGradient, CombatRenderer.getHairFringeType(), hairLengthStringToNumber(V.fringelengthstage), - "fringe" + "hair" ); } static getFringeType() { - if (V.hairtype === "short") { - return "short"; - } if (V.fringetype === "wide flaps") { return "wide-flaps"; } @@ -711,17 +765,21 @@ class CombatRenderer { if (V.hairtype === "layered bob") { return "layered-bob"; } + if (["shaved", "short"].includes(V.hairtype) || (V.hairtype === "default" && V.hairlengthstage === "short")) { + return "short"; + } return "default"; } /** * @param {string} hairType + * @param {Gradient} gradient */ - static getHairGradientType(hairType) { + static getHairGradientType(hairType, gradient) { if (V.fringetype === "mohawk") { return V.position === "missionary" ? "combatMohawk" : "combatMohawkDoggy"; } - return hairType; + return V.position === "missionary" ? "combatMissionary" : "combatDoggy"; } /** @@ -735,13 +793,13 @@ class CombatRenderer { colour: { h: 0, s: 100, l: 30 }, }; if (transformation === "bird" && ["tail", "wings", "malar", "plumage", "pubes"].includes(part)) { - return CombatRenderer.getHairFilter(); + return CombatRenderer.getPartFilter(part); } if (["cat", "wolf"].includes(transformation) && ["ears", "tail", "pubes", "pits"].includes(part)) { - return CombatRenderer.getHairFilter(); + return CombatRenderer.getPartFilter(part); } if (transformation === "fox" && ["ears", "tail", "cheeks", "pubes"].includes(part)) { - return CombatRenderer.getHairFilter(); + return CombatRenderer.getPartFilter(part); } // No filter possible as part(s) cannot be recoloured if ( diff --git a/game/03-JavaScript/05-renderer/21-npc-options.js b/game/03-JavaScript/05-renderer/21-npc-options.js index bd7c6763eb..656a2d1582 100644 --- a/game/03-JavaScript/05-renderer/21-npc-options.js +++ b/game/03-JavaScript/05-renderer/21-npc-options.js @@ -670,6 +670,12 @@ class NpcCombatMapper { blendMode: "multiply", desaturate: true, }; + case "dark red": + return { + blend: "#b50202", + blendMode: "multiply", + desaturate: true, + }; } } return NpcCombatMapper.getNpcSkinFilter(npc); diff --git a/game/03-JavaScript/05-renderer/21-player-options.js b/game/03-JavaScript/05-renderer/21-player-options.js index 0ca2b24601..70879732fc 100644 --- a/game/03-JavaScript/05-renderer/21-player-options.js +++ b/game/03-JavaScript/05-renderer/21-player-options.js @@ -1795,14 +1795,14 @@ class PlayerCombatMapper { if (clothing.combat?.mainColour && !["primary", "secondary"].includes(clothing.combat?.mainColour)) { options.filters[mainFilterKey] = PlayerCombatMapper.genFilterWithHex(clothing.combat.mainColour); } else if (clothing.combat?.mainColour && clothing.combat?.mainColour === "secondary") { - const accColour = clothing.combat?.accColour || clothing.accessory_colour; + const accColour = clothing.accessory_colour === "original" ? 0 : clothing.combat?.accColour || clothing.accessory_colour; const accDebugName = slot + " accessory"; const accCustomFilter = clothing.accessory_colourCustom; options.filters[mainFilterKey] = accColour ? CombatRenderer.lookupColour(setup.colours.clothes_map, accColour, accDebugName, accCustomFilter, clothing.prefilter) : Renderer.emptyLayerFilter(); } else { - const colour = clothing.colour; + const colour = clothing.colour === "original" ? 0 : clothing.colour; const debugName = slot + " clothing"; const customFilter = clothing.colourCustom; options.filters[mainFilterKey] = colour @@ -1813,14 +1813,14 @@ class PlayerCombatMapper { if (clothing.combat?.accColour && !["primary", "secondary"].includes(clothing.combat?.accColour)) { options.filters[accFilterKey] = PlayerCombatMapper.genFilterWithHex(clothing.combat.accColour); } else if (clothing.combat?.accColour && clothing.combat?.accColour === "primary") { - const colour = clothing.colour; + const colour = clothing.colour === "original" ? 0 : clothing.colour; const debugName = slot + " clothing"; const customFilter = clothing.colourCustom; options.filters[accFilterKey] = colour ? CombatRenderer.lookupColour(setup.colours.clothes_map, colour, debugName, customFilter, clothing.prefilter) : Renderer.emptyLayerFilter(); } else { - const accColour = clothing.combat?.accColour || clothing.accessory_colour; + const accColour = clothing.accessory_colour === "original" ? 0 : clothing.combat?.accColour || clothing.accessory_colour; const accDebugName = slot + " accessory"; const accCustomFilter = clothing.accessory_colourCustom; options.filters[accFilterKey] = accColour @@ -1833,7 +1833,7 @@ class PlayerCombatMapper { } else if (clothing.combat?.mainColour && !clothing.combat?.sleeveColour) { options.filters[sleeveFilterKey] = PlayerCombatMapper.genFilterWithHex(clothing.combat.mainColour); } else { - const colour = clothing.colour; + const colour = clothing.colour === "original" ? 0 : clothing.colour; const debugName = slot + " clothing"; const customFilter = clothing.colourCustom; options.filters[sleeveFilterKey] = colour @@ -1846,7 +1846,7 @@ class PlayerCombatMapper { } else if (clothing.combat?.accColour && !clothing.combat?.sleeveAccColour) { options.filters[sleeveAccFilterKey] = PlayerCombatMapper.genFilterWithHex(clothing.combat.accColour); } else { - const accColour = clothing.combat?.accColour || clothing.accessory_colour; + const accColour = clothing.accessory_colour === "original" ? 0 : clothing.combat?.accColour || clothing.accessory_colour; const accDebugName = slot + " accessory"; const accCustomFilter = clothing.accessory_colourCustom; options.filters[sleeveAccFilterKey] = accColour @@ -2521,7 +2521,7 @@ class PlayerCombatMapper { static generateHairFilters(options) { options.filters.hair = CombatRenderer.getHairFilter(); options.filters.fringe = CombatRenderer.getFringeFilter(); - options.hairLength = V.hairlengthstage; + options.hairLength = CombatRenderer.getHairLength(); options.hairType = CombatRenderer.getFringeType(); } } diff --git a/game/04-Variables/colours.js b/game/04-Variables/colours.js index 8a1552fffb..b619a60ba3 100644 --- a/game/04-Variables/colours.js +++ b/game/04-Variables/colours.js @@ -177,7 +177,7 @@ setup.colours = { blend: setup.colours.getSkinRgb(options, tan / 100), blendMode: options.blendMode, desaturate: options.desaturate, - ...options.alpha && { alpha: options.alpha }, + ...(options.alpha && { alpha: options.alpha }), }; }, getSkinRgb(type, tan) { @@ -640,6 +640,24 @@ setup.colours.hairgradients_prototypes = { [0.85, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "linear", + values: [250, 440, 250, 0], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.76, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "linear", + values: [180, 245, 0, 250], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.64, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, }, "low-ombre": { all: { @@ -651,6 +669,24 @@ setup.colours.hairgradients_prototypes = { [0.85, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "linear", + values: [340, 180, 300, 0], + lengthFunctions: [(length, value) => value - length / 1000 / 2, (length, value) => value - length / 1000 / 2], + colors: [ + [0.6, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "linear", + values: [180, 350, 0, 350], + lengthFunctions: [(length, value) => value - length / 1000 / 2, (length, value) => value - length / 1000 / 2], + colors: [ + [0.6, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, }, split: { parted: { @@ -718,6 +754,24 @@ setup.colours.hairgradients_prototypes = { [0.175, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "radial", + values: [15, 183, 50, 150, 103, 350], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.15, "rgba(0, 0, 0, 1)"], + [0.175, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "radial", + values: [125, 103, 50, 150, 103, 350], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.15, "rgba(0, 0, 0, 1)"], + [0.175, "rgba(0, 0, 0, 1)"], + ], + }, }, }, sides: { @@ -731,6 +785,24 @@ setup.colours.hairgradients_prototypes = { [0.85, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "linear", + values: [250, 440, 250, 0], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.76, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "linear", + values: [180, 245, 0, 250], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.64, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, }, "low-ombre": { all: { @@ -742,6 +814,24 @@ setup.colours.hairgradients_prototypes = { [0.85, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "linear", + values: [340, 180, 300, 0], + lengthFunctions: [(length, value) => value - length / 1000 / 2, (length, value) => value - length / 1000 / 2], + colors: [ + [0.6, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "linear", + values: [180, 350, 0, 350], + lengthFunctions: [(length, value) => value - length / 1000 / 2, (length, value) => value - length / 1000 / 2], + colors: [ + [0.6, "rgba(0, 0, 0, 1)"], + [0.85, "rgba(0, 0, 0, 1)"], + ], + }, }, split: { all: { @@ -764,6 +854,24 @@ setup.colours.hairgradients_prototypes = { [0.0, "rgba(0, 0, 0, 1)"], ], }, + combatDoggy: { + gradient: "radial", + values: [15, 183, 50, 150, 103, 350], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.15, "rgba(0, 0, 0, 1)"], + [0.175, "rgba(0, 0, 0, 1)"], + ], + }, + combatMissionary: { + gradient: "radial", + values: [125, 103, 50, 150, 103, 350], + lengthFunctions: [(length, value) => value, (length, value) => value], + colors: [ + [0.15, "rgba(0, 0, 0, 1)"], + [0.175, "rgba(0, 0, 0, 1)"], + ], + }, }, }, }; diff --git a/types/npc.d.ts b/types/npc.d.ts index 7d4c8a67e3..76be4381d1 100644 --- a/types/npc.d.ts +++ b/types/npc.d.ts @@ -150,7 +150,7 @@ declare global { strapon?: { state: "worn"; - color: "black" | "red" | "pink" | "purple" | "fleshy" | "blue" | "green"; + color: "black" | "red" | "pink" | "purple" | "fleshy" | "blue" | "green" | "dark red"; description: string; size: number; }; -- GitLab