From 12d7649b77b7f718259aa8a6b21ea5bd19f36884 Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Sat, 25 May 2024 19:57:34 +0200 Subject: [PATCH 01/15] Typo reverse cowgirl dialog Fixed a typo in the position file for reverse_cowgirl, making no dialog load, ever. --- .../reverse_cowgirl/reverse_cowgirl.config | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/positions/twoactors/reverse_cowgirl/reverse_cowgirl.config b/positions/twoactors/reverse_cowgirl/reverse_cowgirl.config index 84c3ed72..1fd78a49 100644 --- a/positions/twoactors/reverse_cowgirl/reverse_cowgirl.config +++ b/positions/twoactors/reverse_cowgirl/reverse_cowgirl.config @@ -15,15 +15,15 @@ "interactionType": ["direct",null,null,null], "dialog" : { - "default" : "/dialog/sexbound//positions/revers_cowgirl/defaultcowgirlfull.config", - "apex" : "/dialog/sexbound//positions/revers_cowgirl/apexcowgirl.config", - "avian" : "/dialog/sexbound//positions/revers_cowgirl/aviancowgirl.config", - "fenerox" : "/dialog/sexbound//positions/revers_cowgirl/feneroxcowgirl.config", - "floran" : "/dialog/sexbound//positions/revers_cowgirl/florancowgirl.config", - "glitch" : "/dialog/sexbound//positions/revers_cowgirl/glitchcowgirl.config", - "human" : "/dialog/sexbound//positions/revers_cowgirl/humancowgirl.config", - "hylotl" : "/dialog/sexbound//positions/revers_cowgirl/hylotlcowgirl.config", - "novakid" : "/dialog/sexbound//positions/revers_cowgirl/novakidcowgirl.config" + "default" : "/dialog/sexbound//positions/reversecowgirl/defaultcowgirlfull.config", + "apex" : "/dialog/sexbound//positions/reversecowgirl/apexcowgirl.config", + "avian" : "/dialog/sexbound//positions/reversecowgirl/aviancowgirl.config", + "fenerox" : "/dialog/sexbound//positions/reversecowgirl/feneroxcowgirl.config", + "floran" : "/dialog/sexbound//positions/reversecowgirl/florancowgirl.config", + "glitch" : "/dialog/sexbound//positions/reversecowgirl/glitchcowgirl.config", + "human" : "/dialog/sexbound//positions/reversecowgirl/humancowgirl.config", + "hylotl" : "/dialog/sexbound//positions/reversecowgirl/hylotlcowgirl.config", + "novakid" : "/dialog/sexbound//positions/reversecowgirl/novakidcowgirl.config" }, "containerOverlay" : [ From 22906f4232d159e7dd207cb0cae5f6aaec8b10c6 Mon Sep 17 00:00:00 2001 From: hyperjuni <93186376+hyperjuni@users.noreply.github.com> Date: Mon, 27 May 2024 20:33:11 +0300 Subject: [PATCH 02/15] Tail zLevel 23 to 15 Modified the zLevel of actor2's tail to be under actor1's groin --- artwork/humanoid/twoactors-centered.animation | 2 +- artwork/humanoid/twoactors.animation | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/artwork/humanoid/twoactors-centered.animation b/artwork/humanoid/twoactors-centered.animation index 8ebaaf3c..9e7b1db1 100644 --- a/artwork/humanoid/twoactors-centered.animation +++ b/artwork/humanoid/twoactors-centered.animation @@ -862,7 +862,7 @@ "anchorPart": "actor2-body", "centered": true, "image": ".", - "zLevel": 23 + "zLevel": 15 }, "partStates": { "actors": { diff --git a/artwork/humanoid/twoactors.animation b/artwork/humanoid/twoactors.animation index a17dc874..0de94599 100644 --- a/artwork/humanoid/twoactors.animation +++ b/artwork/humanoid/twoactors.animation @@ -1187,7 +1187,7 @@ "anchorPart": "actor2-body", "centered": false, "image": ".", - "zLevel": 23 + "zLevel": 15 }, "partStates": { "actors": { From 420186e23b8ec72e7955254e3560fd46411b6dfa Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Wed, 29 May 2024 05:18:17 +0200 Subject: [PATCH 03/15] Fixed position inconsistency on forced actor uninit When using `uninitActors()` to flush a sexnode, the positions were never reset, as this bypasses the normal "remove actor" pipeline. This results in any attempt to add another actor afterwards, when it was flushed in non-idle state, to break. This is because the position filtering on the first added actor is done in null state, not actually switching to idle, never updating the current position pointer, which then points to an nil entry in the position list. This is now fixed. Also added more failsafety to the system and reduced old commented out code, as well as reduced log spam on unnecessary duplicate "uninitActors" calls. --- scripts/sexbound/lib/sexbound.lua | 25 +++++---------------- scripts/sexbound/lib/sexbound/positions.lua | 2 +- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/scripts/sexbound/lib/sexbound.lua b/scripts/sexbound/lib/sexbound.lua index d3594d3f..53a91a59 100644 --- a/scripts/sexbound/lib/sexbound.lua +++ b/scripts/sexbound/lib/sexbound.lua @@ -394,6 +394,8 @@ end --- Uninitializes each instance of Actor in the actors table. function Sexbound:uninitActors() + if not self._actors[1] then return true end -- No need to uninit anything if noone was here to begin with. Reduces log spam. + self:getLog():info("Uniniting Actors.") Sexbound.Messenger.get("main"):broadcast(self, "Sexbound:PrepareRemoveActor", {}, true) @@ -408,6 +410,10 @@ function Sexbound:uninitActors() self._actors = {} self._actorsOrdered = {} + + self._positions:filterPositions() --Reset positions as this bypasses normal "actor leaves" check and we need to be in idle for when someone else joins + self._currentOrderId = 1 + self._currentOrderIndex = "" return true end @@ -619,25 +625,6 @@ end function Sexbound:helper_reassignAllRoles() if #self._actors == 0 then return end local sexConfig = self:getConfig().sex or {} - - --[[if sexConfig.allowSwitchRoles and self:getActorCount() == 2 then - -- Switch actors when actor 1 is female and actor 2 is male when actor 1 is not wearing a strapon. - if not self._actors[1]:getStatus():hasStatus("equipped_strapon") and self._actors[1]:getGender() == "female" and - (self._actors[2]:getGender() == "male" or self._actors[2]:getSubGender() == "futanari") then - self:switchActorRoles() - - -- An actor wearing a strapon should be switched to be actor 1. - elseif self._actors[2]:getStatus():hasStatus("equipped_strapon") then - self:switchActorRoles() - end - end - - -- Check if any actor needs to have its role forced - self:forEachActor(function(index, actor) - if actor:getForceRole() > 0 and actor:getForceRole() ~= index then - self:helper_forceActorRole(actor, index) - end - end)]] local curIndices, curPerms = self._positions:getCurrentPosition():getAvailableRoles() if curPerms[self._currentOrderId] then diff --git a/scripts/sexbound/lib/sexbound/positions.lua b/scripts/sexbound/lib/sexbound/positions.lua index bb5f5e26..0d1bbde8 100644 --- a/scripts/sexbound/lib/sexbound/positions.lua +++ b/scripts/sexbound/lib/sexbound/positions.lua @@ -321,7 +321,7 @@ end --- Returns a reference to the Current Position. function Sexbound.Positions:getCurrentPosition() if self._index == -1 then return self._idlePosition end - return self._availablePositions[self._index] + return self._availablePositions[self._index] or self._idlePosition end function Sexbound.Positions:getParent() From 05062fbe070cde15e0d36ec1355edf7b33e64940 Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Wed, 29 May 2024 05:39:57 +0200 Subject: [PATCH 04/15] Tentacleplant layer fix Fixed that the front arm layer of the tentacleplant v2 needs to be above actor 2's by new layer numbers. --- .../sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object b/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object index 579b8d7a..123978e1 100644 --- a/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object +++ b/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object @@ -248,7 +248,7 @@ }, "actor1-arm-front": { "properties": { - "zLevel": 43 + "zLevel": 61 } }, "actor2-groin" : { From 5f34db626c96713770f47587e99d9c3180e60670 Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Wed, 29 May 2024 21:27:05 +0200 Subject: [PATCH 05/15] Position specific tail layer move Making the change from 22906f4 affect only the butterfly position. --- artwork/humanoid/twoactors-centered.animation | 18 +++++++++++++----- artwork/humanoid/twoactors.animation | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/artwork/humanoid/twoactors-centered.animation b/artwork/humanoid/twoactors-centered.animation index 9e7b1db1..22c9d814 100644 --- a/artwork/humanoid/twoactors-centered.animation +++ b/artwork/humanoid/twoactors-centered.animation @@ -862,7 +862,7 @@ "anchorPart": "actor2-body", "centered": true, "image": ".", - "zLevel": 15 + "zLevel": 23 }, "partStates": { "actors": { @@ -900,10 +900,18 @@ "position4_2-climax": {}, "position4_2-postclimax": {}, "position4_2-reset": {}, - "position5": {}, - "position5-climax": {}, - "position5-postclimax": {}, - "position5-reset": {}, + "position5": { + "properties": {"zLevel": 15} + }, + "position5-climax": { + "properties": {"zLevel": 15} + }, + "position5-postclimax": { + "properties": {"zLevel": 15} + }, + "position5-reset": { + "properties": {"zLevel": 15} + }, "position6": {}, "position6-climax": {}, "position6-postclimax": {}, diff --git a/artwork/humanoid/twoactors.animation b/artwork/humanoid/twoactors.animation index 0de94599..24c603bc 100644 --- a/artwork/humanoid/twoactors.animation +++ b/artwork/humanoid/twoactors.animation @@ -1187,7 +1187,7 @@ "anchorPart": "actor2-body", "centered": false, "image": ".", - "zLevel": 15 + "zLevel": 32 }, "partStates": { "actors": { @@ -1225,10 +1225,18 @@ "position4_2-climax": {}, "position4_2-postclimax": {}, "position4_2-reset": {}, - "position5": {}, - "position5-climax": {}, - "position5-postclimax": {}, - "position5-reset": {}, + "position5": { + "properties": {"zLevel": 15} + }, + "position5-climax": { + "properties": {"zLevel": 15} + }, + "position5-postclimax": { + "properties": {"zLevel": 15} + }, + "position5-reset": { + "properties": {"zLevel": 15} + }, "position6": {}, "position6-climax": {}, "position6-postclimax": {}, From d315cc9575b9fefcd163fd9ef907aff977a5e4bb Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Thu, 30 May 2024 05:50:43 +0200 Subject: [PATCH 06/15] Infertility control Added ability to remove infertility on player entity for 2000 Sexbux to customizer UI. This allows for infertility to be added throughout gameplay without being a permanent punishment. It also makes Neki players less confused, as they can see their infertility state now. Made infertility cancel and disable ovulation. --- dialog/sexbound/en/notifications.config | 4 ++ .../sexbound/customizer/customizer.config | 37 +++++++++++++++++++ .../sexbound/customizer/customizergeneral.lua | 30 +++++++++++++-- scripts/sexbound/override/player.lua | 34 ++++++++++++++--- scripts/sexbound/override/player/pregnant.lua | 2 +- 5 files changed, 98 insertions(+), 9 deletions(-) diff --git a/dialog/sexbound/en/notifications.config b/dialog/sexbound/en/notifications.config index 11e99f05..3cd4e749 100644 --- a/dialog/sexbound/en/notifications.config +++ b/dialog/sexbound/en/notifications.config @@ -66,6 +66,10 @@ "sterilize": { "true": "You have been successfully sterilized. Fertilization is now impossible.", "false": "Your sterilization has been successfully reverted. Be mindful about fertilization." + }, + "infertility": { + "true": "You have become infertile. I hope you weren't banking on kids.", + "false": "You are no longer infertile. Careful now, unless you want to be a parent." } } } \ No newline at end of file diff --git a/interface/sexbound/customizer/customizer.config b/interface/sexbound/customizer/customizer.config index 597a740d..312cc822 100644 --- a/interface/sexbound/customizer/customizer.config +++ b/interface/sexbound/customizer/customizer.config @@ -153,6 +153,43 @@ "hAnchor" : "left", "width" : 50, "value" : "^shadow;1000", + "color" : [255, 255, 255] + }, + "infertileLabel" : { + "fontSize": 10, + "type": "label", + "position": [20, 148], + "hAnchor": "left", + "vAnchor": "top", + "width": 210, + "value": "^shadow;You currently ^orange;are not^reset;^shadow; infertile.", + "wrapWidth": 210, + "color": [255, 255, 255] + }, + "infertileConfirm": { + "type": "button", + "base": "/interface/sexbound/customizer/smallbutton.png", + "hover": "/interface/sexbound/customizer/smallbutton-hover.png", + "pressed": "/interface/sexbound/customizer/smallbutton-hover.png", + "disabledImage": "/interface/sexbound/customizer/smallbutton-disabled.png", + "pressedOffset": [1,-1], + "caption": "Change", + "fontColor": [255, 255, 255], + "textAlign": "center", + "position": [240, 137] + }, + "infertilePriceIcon": { + "type" : "image", + "file" : "/interface/sexbound/customizer/icons/sexbux.png", + "position" : [300, 139] + }, + "infertilePrice": { + "type" : "label", + "fontSize" : 8, + "position" : [310, 139], + "hAnchor" : "left", + "width" : 50, + "value" : "^shadow;2000", "color" : [255, 255, 255] } } diff --git a/interface/sexbound/customizer/customizergeneral.lua b/interface/sexbound/customizer/customizergeneral.lua index d50d1538..e3e62b41 100644 --- a/interface/sexbound/customizer/customizergeneral.lua +++ b/interface/sexbound/customizer/customizergeneral.lua @@ -11,7 +11,8 @@ function Customizer.General:new(parent) currentSubGender = "None", currentSubGenderIndex = 1, subGenderList = {}, - sterilized = false + sterilized = false, + infertile = false }, Customizer.General_mt) end @@ -32,6 +33,11 @@ function Customizer.General:init() self.sterilized = self._parent.config.sterilized if self.sterilized == nil then self.sterilized = false end if self.sterilized then widget.setText("generalTab.sterilizeLabel", "^shadow;You currently ^orange;are^reset;^shadow; sterilized.") end + + self.infertile = self._parent.config.infertile + if self.infertile == nil then self.infertile = false end + if self.infertile then widget.setText("generalTab.infertileLabel", "^shadow;You currently ^orange;are^reset;^shadow; infertile.") + else widget.setButtonEnabled("generalTab.infertileConfirm", false) end end function Customizer.General:handleMessage(message) @@ -126,8 +132,8 @@ end function Customizer.General:sterilize() local res = player.consumeCurrency("sexbux", 1000) if not res then - widget.playSound("/sfx/interface/clickon_error.ogg") - return false + widget.playSound("/sfx/interface/clickon_error.ogg") + return false end if self.sterilized then @@ -138,4 +144,22 @@ function Customizer.General:sterilize() world.sendEntityMessage(player.id(), "Sexbound:Status:AddStatus", "sterilized") end self.sterilized = not self.sterilized +end + +function Customizer.General:makeFertile() + if not self.infertile then + widget.playSound("/sfx/interface/clickon_error.ogg") + return false + end + + local res = player.consumeCurrency("sexbux", 2000) + if not res then + widget.playSound("/sfx/interface/clickon_error.ogg") + return false + end + + widget.setText("generalTab.infertileLabel", "^shadow;You currently ^orange;are not^reset;^shadow; infertile.") + world.sendEntityMessage(player.id(), "Sexbound:Status:RemoveStatus", "infertile") + + self.infertile = false end \ No newline at end of file diff --git a/scripts/sexbound/override/player.lua b/scripts/sexbound/override/player.lua index 5deb6c78..0be0bfa9 100644 --- a/scripts/sexbound/override/player.lua +++ b/scripts/sexbound/override/player.lua @@ -72,7 +72,8 @@ function Sexbound.Player.new() _startItemsList = {"sexbound1-codex", "sexboundcustomizer"}, _loungeId = nil, _states = {"defaultState", "havingSexState"}, - _isSterilized = false + _isSterilized = false, + _isInfertile = false }, Sexbound.Player_mt) self:init(self, "player") @@ -123,6 +124,7 @@ function Sexbound.Player.new() self._stateMachine = self:helper_loadStateMachine() self._isSterilized = self._status:hasStatus("sterilized") + self._isInfertile = self._status:hasStatus("infertile") self._hasInited = true self:updateTraitEffects() @@ -136,6 +138,8 @@ function Sexbound.Player.new() -- Setup event listeners to notify player about changes to the sterilization status self._status:addEventListener("add", function(data) self:notifySterilizationChange(data) end) self._status:addEventListener("remove", function(data) self:notifySterilizationChange(data) end) + self._status:addEventListener("add", function(data) self:notifyInfertileChange(data) end) + self._status:addEventListener("remove", function(data) self:notifyInfertileChange(data) end) return self end @@ -213,10 +217,6 @@ function Sexbound.Player:showCustomizerUI(args) if "function" ~= type(player.interact) then return end xpcall(function() local _loadedConfig = root.assetJson("/interface/sexbound/customizer/customizer.config") - --_loadedConfig.config.statistics = self:getStatistics():getStatistics() - --_loadedConfig.config.currentGender = self._subGender._currentGender - --_loadedConfig.config.subGenders = self._subGender:getAllSubGenders() - --_loadedConfig.config.sterilized = self._status:hasStatus("sterilized") player.interact("ScriptPane", _loadedConfig, player.id()) end, function(err) sb.logError("Unable to load Sexbound Customizer UI config.") @@ -230,6 +230,7 @@ function Sexbound.Player:handleGetCutomizerData(args) _loadedConfig.currentGender = self._subGender._currentGender _loadedConfig.subGenders = self._subGender:getAllSubGenders() _loadedConfig.sterilized = self._status:hasStatus("sterilized") + _loadedConfig.infertile = self._status:hasStatus("infertile") return _loadedConfig end @@ -307,6 +308,29 @@ function Sexbound.Player:notifySterilizationChange(data) if sterilized then status.removeEphemeralEffect("sexbound_custom_ovulating") end -- Remove ovulation cycle effect when sterilized end +function Sexbound.Player:notifyInfertileChange(data) + if data.name ~= "infertile" then return end + + local notifications = self:getNotifications() or {} + notifications = notifications.events or {} + notifications = notifications.infertility or {} + + local infertile = data.query == "add" + infertile = tostring(infertile) + + local text = notifications[infertile] + if text then + world.sendEntityMessage(entity.id(), "queueRadioMessage", { + messageId = "sexbound_infertility", + unique = false, + text = text + }) + end + + self._isInfertile = infertile + if infertile then status.removeEphemeralEffect("sexbound_custom_ovulating") end -- Remove ovulation cycle effect when infertile +end + function Sexbound.Player:handleSyncStorage(newData) newData = self:fixPregnancyData(newData) storage = util.mergeTable(storage, newData or {}) diff --git a/scripts/sexbound/override/player/pregnant.lua b/scripts/sexbound/override/player/pregnant.lua index 772ca7f4..4d9b6977 100644 --- a/scripts/sexbound/override/player/pregnant.lua +++ b/scripts/sexbound/override/player/pregnant.lua @@ -80,7 +80,7 @@ function Sexbound.Player.Pregnant:updatePeriodCycle(dt) if not enabled then return end -- Player cannot ovulate in the first place - ignore - if not self:canOvulate() or self:getParent()._isSterilized then return end + if not self:canOvulate() or self:getParent()._isSterilized or self:getParent()._isInfertile then return end local ovulating = status.statusProperty("sexbound_custom_ovulating", false) -- Currently active ovulation effect - ignore From 3b5009acb2d50eb1d4d06c1ad8da62694cbfefae Mon Sep 17 00:00:00 2001 From: hyperjuni <93186376+hyperjuni@users.noreply.github.com> Date: Sat, 1 Jun 2024 22:37:54 +0300 Subject: [PATCH 07/15] Layering and typo fixes --- artwork/humanoid/twoactors-centered.animation | 16 ++++++++++++---- artwork/humanoid/twoactors.animation | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/artwork/humanoid/twoactors-centered.animation b/artwork/humanoid/twoactors-centered.animation index 22c9d814..16bf73a1 100644 --- a/artwork/humanoid/twoactors-centered.animation +++ b/artwork/humanoid/twoactors-centered.animation @@ -868,10 +868,18 @@ "actors": { "idle_laying": {}, "idle": {}, - "position1": {}, - "position1-climax": {}, - "position1-postclimax": {}, - "position1-reset": {}, + "position1": { + "properties": {"zLevel": 15} + }, + "position1-climax": { + "properties": {"zLevel": 15} + }, + "position1-postclimax": { + "properties": {"zLevel": 15} + }, + "position1-reset": { + "properties": {"zLevel": 15} + }, "position2": {}, "position2-climax": {}, "position2-postclimax": {}, diff --git a/artwork/humanoid/twoactors.animation b/artwork/humanoid/twoactors.animation index 24c603bc..d880046a 100644 --- a/artwork/humanoid/twoactors.animation +++ b/artwork/humanoid/twoactors.animation @@ -1187,16 +1187,24 @@ "anchorPart": "actor2-body", "centered": false, "image": ".", - "zLevel": 32 + "zLevel": 23 }, "partStates": { "actors": { "idle_laying": {}, "idle": {}, - "position1": {}, - "position1-climax": {}, - "position1-postclimax": {}, - "position1-reset": {}, + "position1": { + "properties": {"zLevel": 15} + }, + "position1-climax": { + "properties": {"zLevel": 15} + }, + "position1-postclimax": { + "properties": {"zLevel": 15} + }, + "position1-reset": { + "properties": {"zLevel": 15} + }, "position2": {}, "position2-climax": {}, "position2-postclimax": {}, From 70c886261aa9db361cb2e1936800e6643026a9f7 Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Sun, 2 Jun 2024 01:00:46 +0200 Subject: [PATCH 08/15] Makes sex music stop forcibly on uninit Caches which entities IDs have started and stopped sex music, and forcibly sends the stop music command on uninit to all still registered listeners. Should fix the issue, where destroyed (smashed) or timeouted nodes (where the player unlounges, losing the status effect, triggering the stop message chain BECAUSE the node doesn't exist anymore) kept the music playing forever, until you lounge and unlounge in another node. Fixes #113 --- scripts/sexbound/lib/sexbound.lua | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/sexbound/lib/sexbound.lua b/scripts/sexbound/lib/sexbound.lua index 53a91a59..b7a5990a 100644 --- a/scripts/sexbound/lib/sexbound.lua +++ b/scripts/sexbound/lib/sexbound.lua @@ -43,7 +43,8 @@ function Sexbound.new(maxActors) _timers = {}, _globalActorId = 0, _uiSyncTokens = {positions=0}, - _containsPlayer = false + _containsPlayer = false, + _sexMusicListeners = {} }, Sexbound_mt) -- Store UUID of the entity running this instance of Sexbound. @@ -270,6 +271,8 @@ function Sexbound:uninit() animator.setAnimationState("props", "none", true) animator.setAnimationState("actors", "none", true) + + self:forceStopSexMusic() --Stop sex music for all still listening actors return result1 and result2 end @@ -1169,12 +1172,32 @@ function Sexbound:startSexMusicForEntity(entityId) local songList = self._config.sex.sexMusicPool or {} local song = util.randomChoice(songList) world.sendEntityMessage(entityId, "playAltMusic", {song}, 1) + + -- Cache who is currently listening, but only once, so we can forcibly end the music on uninit + for _,id in ipairs(self._sexMusicListeners) do + if id == entityId then return end + end + + table.insert(self._sexMusicListeners, entityId) end function Sexbound:stopSexMusicForEntity(entityId) if self._config.noMusic then return end world.sendEntityMessage(entityId, "playAltMusic", {""}, 1) --Try to play a different, non existent song to reset progress of current song world.sendEntityMessage(entityId, "stopAltMusic", 1) + + -- Remove from cache + for i,id in ipairs(self._sexMusicListeners) do + if id == entityId then table.remove(self._sexMusicListeners, i) return end + end +end + +function Sexbound:forceStopSexMusic() + if self._config.noMusic then return end + for _,id in ipairs(self._sexMusicListeners) do + world.sendEntityMessage(id, "playAltMusic", {""}, 1) --Try to play a different, non existent song to reset progress of current song + world.sendEntityMessage(id, "stopAltMusic", 1) + end end function Sexbound:checkNodeCompatibility(args) From 315569325198c79ee01742a6c5d38ee78b5e6346 Mon Sep 17 00:00:00 2001 From: hyperjuni <93186376+hyperjuni@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:44:36 +0300 Subject: [PATCH 09/15] Update sexbound_tentacleplant_v2.object --- .../sexbound_tentacleplant_v2.object | 643 ++++++++++-------- 1 file changed, 344 insertions(+), 299 deletions(-) diff --git a/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object b/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object index 123978e1..6fa00aa6 100644 --- a/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object +++ b/objects/sextoylegendary/sexbound_tentacleplant_v2/sexbound_tentacleplant_v2.object @@ -1,303 +1,348 @@ { - "objectName" : "sexbound_tentacleplant_v2", - "shortdescription" : "Juvenile Tentacle Plant", - "description" : "Plant it down for it to mature with it's true colors.", - "shortdescriptionNew" : "Rooted Tentacle Plant", - "descriptionNew" : "A docile tentacle plant that knows when to heck.", - "colonyTags" : ["sexbound", "sex", "jungle", "odd"], - "category" : "other", - "rarity" : "Legendary", - "race" : "generic", - "printable" : false, - "price" : 25000, - "level" : 0, - - "itemTags" : ["sexbound", "sex"], - - "apexDescription" : "Nothing weird about this plant.", - "avianDescription" : "Maybe I should become what happens next.", - "floranDescription" : "Floran happy to see nature have some fun with us.", - "glitchDescription" : "Annoyed. This plant occasionally tangles with my circuitry.", - "humanDescription" : "I already know where this is going...", - "hylotlDescription" : "It feels like an octopus, yet so much more gentle.", - "novakidDescription" : "That ain't no buckin' bruncko!", - - "retainObjectParametersInItem" : true, - "cleanObject" : true, - "hueshiftValue" : 0, - - "sitStatusEffects" : ["sexbound_sex"], - - "interactive" : true, - - "objectType" : "loungeable", - "sitFlipDirection" : false, - "sitPosition" : [0, 20], - "sitOrientation" : "lay", - "sitAngle" : 0, - - "smashable" : false, - - "inventoryIcon" : "tentacleplanticon.png", - "orientations" : [ - { - "dualImage" : "tentacleplant.png:idle.1", + "objectName" : "sexbound_tentacleplant_v2", + "shortdescription" : "Juvenile Tentacle Plant", + "description" : "Plant it down for it to mature with it's true colors.", + "shortdescriptionNew" : "Rooted Tentacle Plant", + "descriptionNew" : "A docile tentacle plant that knows when to heck.", + "colonyTags" : ["sexbound", "sex", "jungle", "odd"], + "category" : "other", + "rarity" : "Legendary", + "race" : "generic", + "printable" : false, + "price" : 25000, + "level" : 0, - "imagePosition" : [-24, 0], - "frames" : 1, - "animationCycle" : 1, + "itemTags" : ["sexbound", "sex"], - "spaceScan" : 0.1, - "anchors" : [ "bottom" ] - } - ], - - "scripts" : ["/objects/sextoylegendary/sexbound_tentacleplant_v2/tentacleplant.lua"], - - "animation" : "/artwork/humanoid/twoactors.animation", - - "sexboundConfig" : { - "requiredVersion" : ">=3.x.x", - - "modName" : "Tentacle Plant", - - "position" : { - "sex" : [ "butterfly", "cowgirl_tentacleplant", "fellatio" ] - }, - "idlePosition": "idle_toy", - - "sex" : { - "allowSwitchRoles" : false - }, - - "sitPositions" : [ [0, 20], [0, 20] ] - }, - - "animationCustom" : { - "animatedParts" : { - "stateTypes" : { - "actors" : { - "states" : { - "idle" : { - "frames" : 2 - } - } - } - }, - - "parts" : { - "actor1-climax-male-spawn" : { - "partStates" : { - "actors" : { - "position4-climax" : { - "properties" : { - "offset" : [2.750, 1.250] - } - }, - "position5-climax" : { - "properties" : { - "offset" : [3.125, 1.250] - } - }, - "position5-postclimax" : { - "properties" : { - "offset" : [3.125, 1.250] - } - }, - "position6-climax" : { - "properties" : { - "offset" : [3.50, 1.250] - } - } - } - } - }, - - "actor1" : { - "partStates" : { - "actors" : { - "position4" : { - "properties" : { - "offset" : [-3.375, 0] - } - }, - "position4-climax" : { - "properties" : { - "offset" : [-3.375, 0] - } - }, - "position4-postclimax" : { - "properties" : { - "offset" : [-3.375, 0] - } - }, - "position4-reset" : { - "properties" : { - "offset" : [-3.375, 0] - } - }, - "position5" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position5-climax" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position5-postclimax" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position5-reset" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position6" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position6-climax" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position6-postclimax" : { - "properties" : { - "offset" : [-2.25, 0] - } - }, - "position6-reset" : { - "properties" : { - "offset" : [-2.25, 0] - } - } - } - } - }, - - "actor2" : { - "partStates" : { - "actors" : { - "position4" : { - "properties" : { - "offset" : [-3.5, 0.5] - } - }, - "position4-climax" : { - "properties" : { - "offset" : [-3.5, 0.5] - } - }, - "position4-postclimax" : { - "properties" : { - "offset" : [-3.5, 0.5] - } - }, - "position4-reset" : { - "properties" : { - "offset" : [-3.5, 0.5] - } - }, - "position5" : { - "frameProperties" : { - "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0], [-2.75, 0], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0], [-2.75, 0]] - } - }, - "position5-climax" : { - "frameProperties" : { - "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0.125], [-2.75, 0]] - } - }, - "position5-postclimax" : { - "frameProperties" : { - "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0], [-2.75, 0], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0], [-2.75, 0]] - } - }, - "position5-reset" : { - "frameProperties" : { - "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0.125], [-2.75, 0]] - } - }, - "position6" : { - "properties" : { - "offset" : [-1.625, 0] - } - }, - "position6-climax" : { - "properties" : { - "offset" : [-1.625, 0] - } - }, - "position6-postclimax" : { - "properties" : { - "offset" : [-1.625, 0] - } - }, - "position6-reset" : { - "properties" : { - "offset" : [-1.625, 0] - } - } - } - } - }, - "actor1-arm-front": { - "properties": { - "zLevel": 61 - } + "apexDescription" : "Nothing weird about this plant.", + "avianDescription" : "Maybe I should become what happens next.", + "floranDescription" : "Floran happy to see nature have some fun with us.", + "glitchDescription" : "Annoyed. This plant occasionally tangles with my circuitry.", + "humanDescription" : "I already know where this is going...", + "hylotlDescription" : "It feels like an octopus, yet so much more gentle.", + "novakidDescription" : "That ain't no buckin' bruncko!", + + "retainObjectParametersInItem" : true, + "cleanObject" : true, + "hueshiftValue" : 0, + + "sitStatusEffects" : ["sexbound_sex"], + + "interactive" : true, + + "objectType" : "loungeable", + "sitFlipDirection" : false, + "sitPosition" : [0, 20], + "sitOrientation" : "lay", + "sitAngle" : 0, + + "smashable" : false, + + "inventoryIcon" : "tentacleplanticon.png", + "orientations" : [ + { + "dualImage" : "tentacleplant.png:idle.1", + + "imagePosition" : [-24, 0], + "frames" : 1, + "animationCycle" : 1, + + "spaceScan" : 0.1, + "anchors" : [ "bottom" ] + } + ], + + "scripts" : ["/objects/sextoylegendary/sexbound_tentacleplant_v2/tentacleplant.lua"], + + "animation" : "/artwork/humanoid/twoactors.animation", + + "sexboundConfig" : { + "requiredVersion" : ">=3.x.x", + "modName" : "Tentacle Plant", + "position" : { + "sex" : [ "butterfly", "cowgirl_tentacleplant", "fellatio" ] + }, + "idlePosition": "idle_toy", + "sex" : { + "allowSwitchRoles" : false }, - "actor2-groin" : { - "partStates" : { - "actors" : { - "position4" : { - "frameProperties" : { - "zLevel" : [42,42,42,42,42] - } - }, - "position4-climax" : { - "frameProperties" : { - "zLevel" : [42] - } - }, - "position4-postclimax" : { - "frameProperties" : { - "zLevel" : [42,42,42,42,42,42,42,42,42,42,42,42,42] - } - }, - "position4-reset" : { - "frameProperties" : { - "zLevel" : [42] - } - }, - "position5" : { - "frameProperties" : { - "zLevel" : [42,42,42,42,42] - } - }, - "position5-climax" : { - "frameProperties" : { - "zLevel" : [42] - } - }, - "position5-postclimax" : { - "frameProperties" : { - "zLevel" : [42,42,42,42,42,42,42,42,42,42,42,42,42] - } - }, - "position5-reset" : { - "frameProperties" : { - "zLevel" : [42] - } - } - } - } - } - } - } - } -} \ No newline at end of file + "sitPositions" : [ [0, 20], [0, 20] ] + }, + + "animationCustom" : { + "animatedParts" : { + "stateTypes" : { + "actors" : { + "states" : { + "idle" : { + "frames" : 2 + } + } + } + }, + + "parts" : { + "actor1-climax-male-spawn" : { + "partStates" : { + "actors" : { + "position4-climax" : { + "properties" : { + "offset" : [2.750, 1.250] + } + }, + "position5-climax" : { + "properties" : { + "offset" : [3.125, 1.250] + } + }, + "position5-postclimax" : { + "properties" : { + "offset" : [3.125, 1.250] + } + }, + "position6-climax" : { + "properties" : { + "offset" : [3.50, 1.250] + } + } + } + } + }, + + "actor1" : { + "partStates" : { + "actors" : { + "position4" : { + "properties" : { + "offset" : [-3.375, 0] + } + }, + "position4-climax" : { + "properties" : { + "offset" : [-3.375, 0] + } + }, + "position4-postclimax" : { + "properties" : { + "offset" : [-3.375, 0] + } + }, + "position4-reset" : { + "properties" : { + "offset" : [-3.375, 0] + } + }, + "position5" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position5-climax" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position5-postclimax" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position5-reset" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position6" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position6-climax" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position6-postclimax" : { + "properties" : { + "offset" : [-2.25, 0] + } + }, + "position6-reset" : { + "properties" : { + "offset" : [-2.25, 0] + } + } + } + } + }, + + "actor2" : { + "partStates" : { + "actors" : { + "position4" : { + "properties" : { + "offset" : [-3.5, 0.5] + } + }, + "position4-climax" : { + "properties" : { + "offset" : [-3.5, 0.5] + } + }, + "position4-postclimax" : { + "properties" : { + "offset" : [-3.5, 0.5] + } + }, + "position4-reset" : { + "properties" : { + "offset" : [-3.5, 0.5] + } + }, + "position5" : { + "frameProperties" : { + "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0], [-2.75, 0], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0], [-2.75, 0]] + } + }, + "position5-climax" : { + "frameProperties" : { + "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0.125], [-2.75, 0]] + } + }, + "position5-postclimax" : { + "frameProperties" : { + "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0], [-2.75, 0], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0.125], [-2.75, 0], [-2.75, 0]] + } + }, + "position5-reset" : { + "frameProperties" : { + "offset" : [[-2.625, -0.125], [-2.625, -0.125], [-2.75, 0], [-2.75, 0.125], [-2.75, 0]] + } + }, + "position6" : { + "properties" : { + "offset" : [-1.625, 0] + } + }, + "position6-climax" : { + "properties" : { + "offset" : [-1.625, 0] + } + }, + "position6-postclimax" : { + "properties" : { + "offset" : [-1.625, 0] + } + }, + "position6-reset" : { + "properties" : { + "offset" : [-1.625, 0] + } + } + } + } + }, + + "actor1-arm-front": { + "properties": { + "zLevel": 61 + } + }, + + "actor2-tail": { + "partStates" : { + "actors" : { + "position4" : { + "frameProperties" : { + "zLevel" : [14,14,14,14,14] + } + }, + "position4-climax" : { + "frameProperties" : { + "zLevel" : [14] + } + }, + "position4-postclimax" : { + "frameProperties" : { + "zLevel" : [14,14,14,14,14,14,14,14,14,14,14,14,14] + } + }, + "position4-reset" : { + "frameProperties" : { + "zLevel" : [14] + } + }, + "position5" : { + "frameProperties" : { + "zLevel" : [14,14,14,14,14] + } + }, + "position5-climax" : { + "frameProperties" : { + "zLevel" : [14] + } + }, + "position5-postclimax" : { + "frameProperties" : { + "zLevel" : [14,14,14,14,14,14,14,14,14,14,14,14,14] + } + }, + "position5-reset" : { + "frameProperties" : { + "zLevel" : [14] + } + } + } + } + }, + + "actor2-groin" : { + "partStates" : { + "actors" : { + "position4" : { + "frameProperties" : { + "zLevel" : [42,42,42,42,42] + } + }, + "position4-climax" : { + "frameProperties" : { + "zLevel" : [42] + } + }, + "position4-postclimax" : { + "frameProperties" : { + "zLevel" : [42,42,42,42,42,42,42,42,42,42,42,42,42] + } + }, + "position4-reset" : { + "frameProperties" : { + "zLevel" : [42] + } + }, + "position5" : { + "frameProperties" : { + "zLevel" : [42,42,42,42,42] + } + }, + "position5-climax" : { + "frameProperties" : { + "zLevel" : [42] + } + }, + "position5-postclimax" : { + "frameProperties" : { + "zLevel" : [42,42,42,42,42,42,42,42,42,42,42,42,42] + } + }, + "position5-reset" : { + "frameProperties" : { + "zLevel" : [42] + } + } + } + } + } + } + } + } +} From 9d921717953baa06de9db637966f650db5833882 Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:07:34 +0200 Subject: [PATCH 10/15] Fixed Customizer UI Literally just forgot to *actually* define the callback function. This shit's been giving me way too many sleepless nights. --- interface/sexbound/customizer/customizer.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/sexbound/customizer/customizer.config b/interface/sexbound/customizer/customizer.config index 312cc822..3f4d34b3 100644 --- a/interface/sexbound/customizer/customizer.config +++ b/interface/sexbound/customizer/customizer.config @@ -394,7 +394,8 @@ "subGenderSelector.up", "subGenderSelector.down", "subGenderConfirm", - "sterilizeConfirm" + "sterilizeConfirm", + "infertileConfirm" ], "scripts" : [ From 7db4b2aa9a2a747825dcd84d9182e4451ce9f34e Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:43:03 +0200 Subject: [PATCH 11/15] Pregnancy fixes Fixed non-refactored references on pregnancy stage check. Fixed missing entity message encoding for generated baby colors from actor to entity. Thanks again, Chucklefish. --- scripts/sexbound/plugins/pregnant/baby.lua | 20 +++++++++++++++---- .../sexbound/plugins/pregnant/pregnant.lua | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/sexbound/plugins/pregnant/baby.lua b/scripts/sexbound/plugins/pregnant/baby.lua index 2e15bf6b..a2e7fa03 100644 --- a/scripts/sexbound/plugins/pregnant/baby.lua +++ b/scripts/sexbound/plugins/pregnant/baby.lua @@ -151,7 +151,7 @@ function Baby:create(mother, father) color[i] = Sexbound.Util.rgbaToHex6(r) end - return color + return Sexbound.Util.messageEncode(color) --Encode color data cause Starbound sucks ass with Lua <-> JSON conversion end baby.bodyColor = generateColor(motherBodyIndex, fatherBodyIndex, bodyColorPool, bodyAllowBlending, bodyColorLambda, "bodyColor") @@ -399,18 +399,30 @@ function Baby:_convertBabyConfigToSpawnableNPC(babyConfig, babyName) local altColorAsFacialMaskSubColor = not not speciesConfig.altColorAsFacialMaskSubColor if babyConfig.bodyColor then + local c = babyConfig.bodyColor + local ci, cv = next(c) + local cl = string.len(tostring(ci)) + if cl == 7 or cl == 9 then c = Sexbound.Util.decodeMessage(c) end -- If color code is 7 (RGB+X) or 9 (RGBA+X) long, decode bodyColorPalette = "?replace" - for k,v in pairs(babyConfig.bodyColor) do bodyColorPalette = bodyColorPalette..";"..k.."="..v end + for k,v in pairs(c) do bodyColorPalette = bodyColorPalette..";"..k.."="..v end end if babyConfig.hairColor then + local c = babyConfig.hairColor + local ci, cv = next(c) + local cl = string.len(tostring(ci)) + if cl == 7 or cl == 9 then c = Sexbound.Util.decodeMessage(c) end -- If color code is 7 (RGB+X) or 9 (RGBA+X) long, decode hairColorPalette = "?replace" - for k,v in pairs(babyConfig.hairColor) do hairColorPalette = hairColorPalette..";"..k.."="..v end + for k,v in pairs(c) do hairColorPalette = hairColorPalette..";"..k.."="..v end end if babyConfig.undyColor then + local c = babyConfig.undyColor + local ci, cv = next(c) + local cl = string.len(tostring(ci)) + if cl == 7 or cl == 9 then c = Sexbound.Util.decodeMessage(c) end -- If color code is 7 (RGB+X) or 9 (RGBA+X) long, decode undyColorPalette = "?replace" - for k,v in pairs(babyConfig.undyColor) do undyColorPalette = undyColorPalette..";"..k.."="..v end + for k,v in pairs(c) do undyColorPalette = undyColorPalette..";"..k.."="..v end end -- Build directives like Starbound does diff --git a/scripts/sexbound/plugins/pregnant/pregnant.lua b/scripts/sexbound/plugins/pregnant/pregnant.lua index 8243800b..6e8aa9c5 100644 --- a/scripts/sexbound/plugins/pregnant/pregnant.lua +++ b/scripts/sexbound/plugins/pregnant/pregnant.lua @@ -750,7 +750,7 @@ function Sexbound.Actor.Pregnant:getPregnancyStage() elseif isPlayer == true then tempResult = 1 - (b.birthWorldTime / b.fullBirthWorldTime) -- Player world time calculation: reverse percentage of countdown else - tempResult = (world.day() + world.timeOfDay() - baby.fullBirthWorldTime) / (baby.birthWorldTime - baby.fullBirthWorldTime) -- Non-Player world time calculation: percentage diff time sinc start to total timespan + tempResult = (world.day() + world.timeOfDay() - b.fullBirthWorldTime) / (b.birthWorldTime - b.fullBirthWorldTime) -- Non-Player world time calculation: percentage diff time sinc start to total timespan end if tempResult > result then result = tempResult end end From e3152198935cc8b8e9e5d1e4df5046e105d4472f Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:17:52 +0200 Subject: [PATCH 12/15] Bump version 1.2_r1 Updated version number to 1.2 Revision 1. Added changelog for revision 1. --- _metadata | 2 +- sexbound-changelog.json | 9 ++++++++- sexbound.config | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/_metadata b/_metadata index 5f4a96d9..1152fa89 100644 --- a/_metadata +++ b/_metadata @@ -1,6 +1,6 @@ { "name": "lox_sexbound", - "version": "1.2_h1", + "version": "1.2_r1", "author": "Erina Sugino", "description": "^orange;Sexbound Reborn^reset; is the continuation of the old Sexbound API by Locuturus and Co., enabling sexual interactions in Starbound. It was forked from SxB version 5.12.1.\n\n\n\n^red;»^reset; CREDITS:\n\n^orange;Erina Sugino - Developer & Creator^reset;\n^blue;https://ko-fi.com/erinasugino^reset;\n\n^orange;* Locuturus and Co. - Original Author^reset;\n^blue;https://www.loverslab.com/files/file/4337-sexbound/^reset;\n\n^orange;* Sir Bumpleton - Lustbound Creator^reset;\n^blue;https://www.loverslab.com/files/file/14030-lustbound/^reset;\n\n^orange;* Red3dred - Contributor^reset;\n^blue;https://www.patreon.com/user?u=27199103^reset;\n\n^orange;* Rylasasin - Contributor^reset;\n^blue;^reset;\n\n^orange;XspeedPL - Contributor^reset;\n^blue;^reset;\n\n^orange;Hyperjuni - Contributor^reset;\n^blue;^reset;\n\n\n\n^red;IF YOU HAVE THIS MOD FROM STEAM, YOU HAVE AN UNAUTHORIZED STOLEN COPY.^reset;", "friendlyName": "Sexbound Reborn", diff --git a/sexbound-changelog.json b/sexbound-changelog.json index 16ab5516..e33ff05a 100644 --- a/sexbound-changelog.json +++ b/sexbound-changelog.json @@ -1,4 +1,11 @@ { + "1.2_r1": [ + "» 1.2 Revision 1 «", + "- Added option to make an infertile character fertile again to customizer", + "- Fixed pregnancies for e.g. Sergal", + "- Fixed sex music not stopping when node gets destroyed", + "- More layer fixes, e.g. for the tentacleplant" + ], "1.2" : [ "» BIG TALK «", "- Completely reworked Sextalk. It's better, trust me", @@ -24,7 +31,7 @@ ], "1.1" : [ "» FOUNDING FAMILY «", - "- Integrated Lustbound Base", + "- Integrated \"Lustbound Base\"", "- Reworked pregnancies - cross-species, multi-babies, incest, color genetics, true kids, ...", "- Reworked NPC behavior - smarter arousal, special dialog for families, no perma-stuck pathfinding", "- Optional cumflation logic", diff --git a/sexbound.config b/sexbound.config index 487b3df3..9b82b351 100644 --- a/sexbound.config +++ b/sexbound.config @@ -1,5 +1,5 @@ { - "version": "1.2_h1", + "version": "1.2_r1", /** * A link to a reliable download / update web page for Sexbound. From 1b783f237ebf97ef1922e9e0425bfee75773363b Mon Sep 17 00:00:00 2001 From: ErinaSugino <121646352+ErinaSugino@users.noreply.github.com> Date: Fri, 28 Jun 2024 07:08:13 +0200 Subject: [PATCH 13/15] Configurable arousal/heat Made all arousal/heat effects configurable per species and by user preference. Species file parameter "sxbHeatDuration" determines how long the heat effect should last for the given species. "effectConfig" on the corresponding statuseffect file has settings per species (plus default) on what the effects do. "arousal" section in main config file allows users to tweak effects globally. Made arousal/heat effect scripts efficiently modularized. --- scripts/sexbound/override/common.lua | 1 + scripts/sexbound/override/player/arousal.lua | 2 +- .../sexbound/plugins/pregnant/pregnant.lua | 15 +++- sexbound.config | 44 ++++++++++ .../sexbound_arousal/sexbound_arousal.lua | 84 +++++++++++++++++++ .../sexbound_arousal_debuff1.animation} | 0 .../sexbound_arousal_debuff1.lua | 10 +++ .../sexbound_arousal_debuff1.statuseffect | 10 +++ .../sexbound_arousal_debuff2.animation} | 0 .../sexbound_arousal_debuff2.lua | 21 +++++ .../sexbound_arousal_debuff2.statuseffect | 10 +++ .../sexbound_arousal_heat.animation | 5 ++ .../sexbound_arousal_heat.lua | 21 +++++ .../sexbound_arousal_heat.statuseffect | 2 +- .../sexbound_arousal_heat_weak.animation | 5 ++ .../sexbound_arousal_heat_weak.lua | 21 +++++ .../sexbound_arousal_heat_weak.statuseffect | 2 +- .../sexbound_arousal_debuff1.statuseffect | 9 -- .../sexbound_arousal_debuff2.lua | 31 ------- .../sexbound_arousal_debuff2.statuseffect | 9 -- .../sexbound_arousal_heat.lua | 50 ----------- .../sexbound_arousal_heat_weak.lua | 40 --------- 22 files changed, 249 insertions(+), 143 deletions(-) create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal.lua rename stats/effects/{sexbound_arousal_heat/sexbound_arousal_heat.animation => sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.animation} (100%) rename stats/effects/{ => sexbound_arousal}/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua (65%) create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect rename stats/effects/{sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation => sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.animation} (100%) create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.animation create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.lua rename stats/effects/{ => sexbound_arousal}/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect (74%) create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation create mode 100644 stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua rename stats/effects/{ => sexbound_arousal}/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect (75%) delete mode 100644 stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect delete mode 100644 stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua delete mode 100644 stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect delete mode 100644 stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.lua delete mode 100644 stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua diff --git a/scripts/sexbound/override/common.lua b/scripts/sexbound/override/common.lua index 5033c9b1..d9e8f914 100644 --- a/scripts/sexbound/override/common.lua +++ b/scripts/sexbound/override/common.lua @@ -115,6 +115,7 @@ function Sexbound.Common:fetchCoreIdentity() self._speciesType = speciesConfig.sxbSpeciesType or nil self._usesHeat = (speciesConfig.sxbUseHeat or false) and (self._config.sex.enableHeatMechanic or false) + self._heatDuration = speciesConfig.sxbHeatDuration or 1800 end function Sexbound.Common:buildBodyTraits(gender) diff --git a/scripts/sexbound/override/player/arousal.lua b/scripts/sexbound/override/player/arousal.lua index 41ee8c32..97328696 100644 --- a/scripts/sexbound/override/player/arousal.lua +++ b/scripts/sexbound/override/player/arousal.lua @@ -42,7 +42,7 @@ function Sexbound.Player.Arousal:update(dt) storage.sexbound.heatCycle = heatTimer if heatTimer <= 0 then -- Trigger heat - status.addEphemeralEffect("sexbound_arousal_heat") + status.addEphemeralEffect("sexbound_arousal_heat", self._parent._heatDuration or 1800) end end else diff --git a/scripts/sexbound/plugins/pregnant/pregnant.lua b/scripts/sexbound/plugins/pregnant/pregnant.lua index 6e8aa9c5..15fd2278 100644 --- a/scripts/sexbound/plugins/pregnant/pregnant.lua +++ b/scripts/sexbound/plugins/pregnant/pregnant.lua @@ -395,7 +395,18 @@ function Sexbound.Actor.Pregnant:thisActorHasEnoughFertility(otherActor) local sxbFertility = self:getParent():getIdentity().sxbFertility local fertility = sxbFertility or self:getFertility() - if status:hasOneOf({"sexbound_aroused_strong", "sexbound_aroused_heat"}) then fertility = fertility * 1.1 end -- Strong arousal buff and in heat buff give 10% higher base fertility + -- Apply fertility buff from current arousal/heat level based on config + local arousalBuff = 1 + if ((self._config.arousal.heat or {}).fertilityBonus or 0) > 0 and status:hasStatus("sexbound_aroused_heat") then -- Heat max priority + arousalBuff = self._config.arousal.heat.fertilityBonus + elseif ((self._config.arousal.heatWeak or {}).fertilityBonus or 0) > 0 and status:hasStatus("sexbound_aroused_heat_weak") then -- Heat weak should be exclusive to heat. Heat > arousal + arousalBuff = self._config.arousal.heatWeak.fertilityBonus + elseif ((self._config.arousal.strong or {}).fertilityBonus or 0) > 0 and status:hasStatus("sexbound_aroused_strong") then -- Strong arousal > arousal + arousalBuff = self._config.arousal.strong.fertilityBonus + elseif ((self._config.arousal.weak or {}).fertilityBonus or 0) > 0 and status:hasStatus("sexbound_aroused") then + arousalBuff = self._config.arousal.weak.fertilityBonus + end + fertility = fertility * arousalBuff local bonusCount = self:getCurrentInseminations(otherActor) local bonusMax = self:getConfig().fertilityBonusMax or 0.6 @@ -671,6 +682,8 @@ function Sexbound.Actor.Pregnant:fetchRemoteConfig(mainConfig) self._config.immersionLevel = mainConfig.immersionLevel or 1 self._config.subGenderList = mainConfig.sex.subGenderList or {} self._config.subGenderChance = mainConfig.sex.subGenderChance or 0.01 + + self._config.arousal = mainConfig.arousal or {} end function Sexbound.Actor.Pregnant:handleInsemination(otherActor) diff --git a/sexbound.config b/sexbound.config index 9b82b351..cd9da428 100644 --- a/sexbound.config +++ b/sexbound.config @@ -538,5 +538,49 @@ }, "allowIncest": true + }, + + /** + * Behaviour of the arousal/heat effects + * "playMoans" defines if the given effect should play occasional moans + * "moanChance" defines the chance a moan is played every interval of "moanFrequency" as percent (0-1) + * "moanFrequency" defines the time between two possible moans in seconds. 0 = off. + * "speedDebuff" defines a multiplier to how strong a potential speed debuff applied by the effect can be. 0 = off. + * "strengthDebuff" defines a multiplier to how strong a potential strength debuff applied by the effect can be. 0 = off. + * "fertilityBonus" defines a bonus given to the player's fertility with this effect, as percent multiplier. 0 = none + */ + "arousal": { + "weak": { + "playMoans": false, + "moanChance": 0.1, + "moanFrequency": 10, + "speedDebuff": 1, + "strengthDebuff": 1, + "fertilityBonus": 0 + }, + "strong": { + "playMoans": false, + "moanChance": 0.1, + "moanFrequency": 10, + "speedDebuff": 1, + "strengthDebuff": 1, + "fertilityBonus": 1.1 + }, + "heat": { + "playMoans": true, + "moanChance": 0.1, + "moanFrequency": 10, + "speedDebuff": 1, + "strengthDebuff": 1, + "fertilityBonus": 1.1 + }, + "heatWeak": { + "playMoans": true, + "moanChance": 0.1, + "moanFrequency": 10, + "speedDebuff": 1, + "strengthDebuff": 1, + "fertilityBonus": 0 + } } } diff --git a/stats/effects/sexbound_arousal/sexbound_arousal.lua b/stats/effects/sexbound_arousal/sexbound_arousal.lua new file mode 100644 index 00000000..3c64f5f6 --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal.lua @@ -0,0 +1,84 @@ +function fetchConfig() + self._config = {} + r,err = pcall(function() + local config = root.assetJson("/sexbound.config") + if config and config.arousal then config = config.arousal + self._config = config[self._effect] or {playMoans = false, moanChance = 0.1, moanFrequency = 10, speedDebuff = 1, strengthDebuff = 1, fertilityBonus = 0} + else sb.logError("Arousal effect could not fetch arousal config!") return end + end) + if not r then + sb.logError("Arousal effect could not fetch main config!") + return + end +end + +function fetchSpeciesConfig() + local species = world.entitySpecies(entity.id()) + self._speciesConfig = (species and config.getParameter(species)) or config.getParameter("default", {}) +end + +function setupEffects() + if (self._speciesConfig.strength or 0) ~= 0 and (self._config.strengthDebuff or 0) ~= 0 then + effect.addStatModifierGroup({ + { stat = "powerMultiplier", effectiveMultiplier = self._speciesConfig.strength * self._config.strengthDebuff } + }) + end +end + +function setupTimers() + self._timers = {} + self._doSpeed = 0 + + if (self._speciesConfig.speed or 0) ~= 0 and (self._speciesConfig.speedInterval or 0) > 0 and (self._speciesConfig.speedChance or 0) > 0 and (self._config.speedDebuff or 0) ~= 0 then + table.insert(self._timers, {action = "speed", value = self._speciesConfig.speed * self._config.speedDebuff, chance = self._speciesConfig.speedChance, timeMax = self._speciesConfig.speedInterval, timeCur = self._speciesConfig.speedDebuff}) + end + + if self._config.playMoans and (self._config.moanChance or 0) > 0 and (self._config.moanFrequency or 0) > 0 then + self._fetchMoans = true + table.insert(self._timers, {action = "moan", chance = self._config.moanChance, timeMax = self._config.moanFrequency, timeCur = self._config.moanFrequency}) + end +end + +function update(dt) { + promises:update() + + if not self._fetchMoans then + promises:add(world.sendEntityMessage(entity.id(), "Sexbound:Arousal:GetMoans"), function(moans) + self._moanSfx = moans or {} + if self._moanSfx.soundEffects then animator.setSoundPool("moan", self._moanSfx.soundEffects) end + end) + self._fetchMoans = false + end + + for _,t in ipairs(self._timers) do + t.timeCur = t.timeCur - dt + if t.timeCur <= 0 then + t.timeCur = t.timeMax + handleTimer(t) + end + end + + if self._doSpeed and self._doSpeed ~= 0 then + mcontroller.controlModifiers({ + speedModifier = self._doSpeed + }) + end +} + +function handleTimer(timer) + local a = timer.action + local v = timer.value + local c = timer.chance + local r = math.random() + + if a == "speed" then + if r <= c then self_doSpeed = v else self._doSpeed = 0 end + elseif a == "moan" then + if r <= c then + local pitch = self._moanSfx.pitch or 1.0 + if type(pitch) == "table" then pitch = pitch[1] + (math.random() * (pitch[2] - pitch[1])) end + animator.setSoundPitch("moan", pitch) + animator.playSound("moan") + end + end +end \ No newline at end of file diff --git a/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.animation b/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.animation similarity index 100% rename from stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.animation rename to stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.animation diff --git a/stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua b/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua similarity index 65% rename from stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua rename to stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua index 97a6d876..eeddde80 100644 --- a/stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua +++ b/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.lua @@ -1,7 +1,17 @@ +require "/scripts/messageutil.lua" +require "/stats/effects/sexbound_arousal/sexbound_arousal.lua" + function init() + self._effect = "heat" + local entityId = entity.id() status.setStatusProperty("sexbound_aroused", true) world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused") + + fetchConfig() + fetchSpeciesConfig() + setupEffects() + setupTimers() end function uninit() diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect b/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect new file mode 100644 index 00000000..48b202fb --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect @@ -0,0 +1,10 @@ +{ + "name" : "sexbound_arousal_debuff1", + "blockingStat" : "sexboundImmunity", + "defaultDuration" : 999, + "effectConfig" : {"default": {"strength": 0, "speed": 0, "speedInterval": 0, "speedChance": 0}}, + "icon" : "/interface/statuses/arousal1.png", + "label" : "Horny", + "scripts" : [ "sexbound_arousal_debuff1.lua" ], + "animationConfig" : "sexbound_arousal_debuff1.animation" +} diff --git a/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation b/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.animation similarity index 100% rename from stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation rename to stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.animation diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua b/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua new file mode 100644 index 00000000..4377515f --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua @@ -0,0 +1,21 @@ +require "/scripts/messageutil.lua" +require "/stats/effects/sexbound_arousal/sexbound_arousal.lua" + +function init() + self._effect = "heat" + + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_strong", true) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_strong") + + fetchConfig() + fetchSpeciesConfig() + setupEffects() + setupTimers() +end + +function uninit() + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_strong", false) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_strong") +end diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect b/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect new file mode 100644 index 00000000..b4c02861 --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect @@ -0,0 +1,10 @@ +{ + "name" : "sexbound_arousal_debuff2", + "blockingStat" : "sexboundImmunity", + "defaultDuration" : 999, + "effectConfig" : {"default": {"strength": 0.9, "speed": 0.9, "speedInterval": 10, "speedChance": 0.1}}, + "icon" : "/interface/statuses/arousal2.png", + "label" : "Pent up", + "scripts" : [ "sexbound_arousal_debuff2.lua" ], + "animationConfig" : "sexbound_arousal_debuff2.animation" +} diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.animation b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.animation new file mode 100644 index 00000000..ff10b1fb --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.animation @@ -0,0 +1,5 @@ +{ + "sounds" : { + "moan": [] + } +} diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.lua b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.lua new file mode 100644 index 00000000..43333cc3 --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.lua @@ -0,0 +1,21 @@ +require "/scripts/messageutil.lua" +require "/stats/effects/sexbound_arousal/sexbound_arousal.lua" + +function init() + self._effect = "heat" + + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_heat", true) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_heat") + + fetchConfig() + fetchSpeciesConfig() + setupEffects() + setupTimers() +end + +function uninit() + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_heat", false) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_heat") +end diff --git a/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect similarity index 74% rename from stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect rename to stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect index 2fdcb360..f5f1f040 100644 --- a/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat/sexbound_arousal_heat.statuseffect @@ -2,7 +2,7 @@ "name" : "sexbound_arousal_heat", "blockingStat" : "sexboundImmunity", "defaultDuration" : 1800, - "effectConfig" : {}, + "effectConfig" : {"default": {"strength": 0.9, "speed": 0.9, "speedInterval": 10, "speedChance": 0.15}}, "icon" : "/interface/statuses/heat.png", "label" : "In Heat", "scripts" : [ "sexbound_arousal_heat.lua" ], diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation new file mode 100644 index 00000000..ff10b1fb --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.animation @@ -0,0 +1,5 @@ +{ + "sounds" : { + "moan": [] + } +} diff --git a/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua new file mode 100644 index 00000000..687e01c1 --- /dev/null +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua @@ -0,0 +1,21 @@ +require "/scripts/messageutil.lua" +require "/stats/effects/sexbound_arousal/sexbound_arousal.lua" + +function init() + self._effect = "heatWeak" + + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_heat_weak", true) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_heat_weak") + + fetchConfig() + fetchSpeciesConfig() + setupEffects() + setupTimers() +end + +function uninit() + local entityId = entity.id() + status.setStatusProperty("sexbound_aroused_heat_weak", false) + world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_heat_weak") +end diff --git a/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect similarity index 75% rename from stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect rename to stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect index ac2c3a7a..fa5e7c59 100644 --- a/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect +++ b/stats/effects/sexbound_arousal/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.statuseffect @@ -2,7 +2,7 @@ "name" : "sexbound_arousal_heat_weak", "blockingStat" : "sexboundImmunity", "defaultDuration" : 900, - "effectConfig" : {}, + "effectConfig" : {"default": {"strength": 0.95, "speed": 0.95, "speedInterval": 10, "speedChance": 0.1}}, "icon" : "/interface/statuses/heat_weak.png", "label" : "In Heat (Medicated)", "scripts" : [ "sexbound_arousal_heat_weak.lua" ], diff --git a/stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect b/stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect deleted file mode 100644 index fbbe8f33..00000000 --- a/stats/effects/sexbound_arousal_debuff1/sexbound_arousal_debuff1.statuseffect +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name" : "sexbound_arousal_debuff1", - "blockingStat" : "sexboundImmunity", - "defaultDuration" : 999, - "effectConfig" : {}, - "icon" : "/interface/statuses/arousal1.png", - "label" : "Horny", - "scripts" : [ "sexbound_arousal_debuff1.lua" ] -} diff --git a/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua b/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua deleted file mode 100644 index 7c2ec890..00000000 --- a/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.lua +++ /dev/null @@ -1,31 +0,0 @@ -function init() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_strong", true) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_strong") - - effect.addStatModifierGroup({ - { stat = "powerMultiplier", effectiveMultiplier = 0.9 } - }) - - self._effectTimer = 0 - self._doEffect = false -end - -function update(dt) - self._effectTimer = self._effectTimer - dt - if self._effectTimer <= 0 then - if math.random() <= 0.1 then self._doEffect = true else self._doEffect = false end - self._effectTimer = 10 - end - if self._doEffect then - mcontroller.controlModifiers({ - speedModifier = 0.9 - }) - end -end - -function uninit() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_strong", false) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_strong") -end diff --git a/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect b/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect deleted file mode 100644 index a83ae60c..00000000 --- a/stats/effects/sexbound_arousal_debuff2/sexbound_arousal_debuff2.statuseffect +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name" : "sexbound_arousal_debuff2", - "blockingStat" : "sexboundImmunity", - "defaultDuration" : 999, - "effectConfig" : {}, - "icon" : "/interface/statuses/arousal2.png", - "label" : "Pent up", - "scripts" : [ "sexbound_arousal_debuff2.lua" ] -} diff --git a/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.lua b/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.lua deleted file mode 100644 index a7825856..00000000 --- a/stats/effects/sexbound_arousal_heat/sexbound_arousal_heat.lua +++ /dev/null @@ -1,50 +0,0 @@ -require "/scripts/messageutil.lua" - -function init() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_heat", true) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_heat") - - effect.addStatModifierGroup({ - { stat = "powerMultiplier", effectiveMultiplier = 0.9 } - }) - - self._effectTimer = 0 - self._doEffect = false - self._moanSfx = {} - self._triedToFetch = false -end - -function update(dt) - promises:update() - if not self._triedToFetch then - promises:add(world.sendEntityMessage(entity.id(), "Sexbound:Arousal:GetMoans"), function(moans) - self._moanSfx = moans or {} - if self._moanSfx.soundEffects then animator.setSoundPool("moan", self._moanSfx.soundEffects) end - end) - self._triedToFetch = true - end - self._effectTimer = self._effectTimer - dt - if self._effectTimer <= 0 then - local rng = math.random() - if rng <= 0.1 then - self._doEffect = true - local pitch = self._moanSfx.pitch or 1.0 - if type(pitch) == "table" then pitch = pitch[1] + (math.random() * (pitch[2] - pitch[1])) end - animator.setSoundPitch("moan", pitch) - animator.playSound("moan") - else self._doEffect = false end - self._effectTimer = 10 - end - if self._doEffect then - mcontroller.controlModifiers({ - speedModifier = 0.9 - }) - end -end - -function uninit() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_heat", false) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_heat") -end diff --git a/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua b/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua deleted file mode 100644 index 94ab43de..00000000 --- a/stats/effects/sexbound_arousal_heat_weak/sexbound_arousal_heat_weak.lua +++ /dev/null @@ -1,40 +0,0 @@ -require "/scripts/messageutil.lua" - -function init() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_heat_weak", true) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:AddStatus", "sexbound_aroused_heat_weak") - - effect.addStatModifierGroup({ - { stat = "powerMultiplier", effectiveMultiplier = 0.95 } - }) - - self._effectTimer = 0 - self._moanSfx = {} - self._triedToFetch = false -end - -function update(dt) - promises:update() - promises:add(world.sendEntityMessage(entity.id(), "Sexbound:Arousal:GetMoans"), function(moans) - self._moanSfx = moans or {} - if self._moanSfx.soundEffects then animator.setSoundPool("moan", self._moanSfx.soundEffects) end - end) - self._effectTimer = self._effectTimer - dt - if self._effectTimer <= 0 then - local rng = math.random() - if rng <= 0.05 then - local pitch = self._moanSfx.pitch or 1.0 - if type(pitch) == "table" then pitch = pitch[1] + (math.random() * (pitch[2] - pitch[1])) end - animator.setSoundPitch("moan", pitch) - animator.playSound("moan") - end - self._effectTimer = 10 - end -end - -function uninit() - local entityId = entity.id() - status.setStatusProperty("sexbound_aroused_heat_weak", false) - world.sendEntityMessage(entityId, "Sexbound:Pregnant:RemoveStatus", "sexbound_aroused_heat_weak") -end From 559e875cbf9327a5d2a5d2ec3ed4869e6028fbb6 Mon Sep 17 00:00:00 2001 From: FeatherAntennae Date: Fri, 28 Jun 2024 15:01:23 -0400 Subject: [PATCH 14/15] Spawn semen when dripping. --- scripts/sexbound/plugins/climax/climax.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/sexbound/plugins/climax/climax.lua b/scripts/sexbound/plugins/climax/climax.lua index bd01fb70..d012a226 100644 --- a/scripts/sexbound/plugins/climax/climax.lua +++ b/scripts/sexbound/plugins/climax/climax.lua @@ -508,7 +508,16 @@ function Sexbound.Actor.Climax:inflationDrip(dt) local oldAmount = self._inflation if oldAmount > 0 then local actor = self._parent - self._inflation = math.max(0, self._inflation - util.randomInRange(self:getDripRate()) * dt) + + local dripQuantity = math.min(util.randomInRange(self:getDripRate()) * dt, self._inflation) + self._inflation = self._inflation - dripQuantity + + if self._config.enableSpawnLiquids then + local spawnPosition = actor:getClimaxSpawnPosition() or {0.0, 0.0} + local semenId = 157 + world.spawnLiquid(spawnPosition, semenId, dripQuantity) + end + self:getLog():debug("Actor "..actor:getActorNumber().." dripping. New amount: "..self._inflation) if oldAmount >= self:getInflationThreshold() and self._inflation < self:getInflationThreshold() then actor:resetParts(actor:getAnimationState(), actor:getSpecies(), actor:getGender(), actor:resetDirectives(actor:getActorNumber())) From 17d4348fc56885c24dfb47f5e7802b7dded61885 Mon Sep 17 00:00:00 2001 From: FeatherAntennae Date: Sat, 29 Jun 2024 20:31:26 -0400 Subject: [PATCH 15/15] Added system to keep track of fluids inflating an actor. --- .../sexbound/climax/dripping.projectile | 19 +++ scripts/sexbound/plugins/climax/climax.lua | 98 +++++++++---- scripts/sexbound/plugins/climax/inflation.lua | 137 ++++++++++++++++++ 3 files changed, 228 insertions(+), 26 deletions(-) create mode 100644 projectiles/sexbound/climax/dripping.projectile create mode 100644 scripts/sexbound/plugins/climax/inflation.lua diff --git a/projectiles/sexbound/climax/dripping.projectile b/projectiles/sexbound/climax/dripping.projectile new file mode 100644 index 00000000..d7b04f3f --- /dev/null +++ b/projectiles/sexbound/climax/dripping.projectile @@ -0,0 +1,19 @@ +{ + "projectileName": "dripping", + "image": "climax.png", + "physics": "climax", + "animationCycle": 1, + "frameNumber": 1, + "speed": 5, + "damagePoly": [ + [-0.25, -0.25], + [0.25, -0.25], + [0.25, 0.25], + [-0.25, 0.25] + ], + "damageKind": "hidden", + "onlyHitTerrain": true, + "hydrophobic": true, + + "scripts": ["/projectiles/sexbound/climax/climaxprojectile.lua"] +} diff --git a/scripts/sexbound/plugins/climax/climax.lua b/scripts/sexbound/plugins/climax/climax.lua index d012a226..02baab6e 100644 --- a/scripts/sexbound/plugins/climax/climax.lua +++ b/scripts/sexbound/plugins/climax/climax.lua @@ -5,6 +5,7 @@ if not SXB_RUN_TESTS then require("/scripts/sexbound/lib/sexbound/actor/plugin.lua") require("/scripts/sexbound/plugins/climax/scriptedclimax/scenario1.lua") + require("/scripts/sexbound/plugins/climax/inflation.lua") end Sexbound.Actor.Climax = Sexbound.Actor.Plugin:new() @@ -23,7 +24,7 @@ function Sexbound.Actor.Climax:new(parent, config) _scenarios = {}, _soundEffects = {}, _timer = {}, - _inflation = 0, + _inflation = {}, _dripTimer = 0 }, Sexbound.Actor.Climax_mt) @@ -48,6 +49,10 @@ function Sexbound.Actor.Climax:new(parent, config) _self:loadSoundEffects() end + if _self._config.enableInflation then + _self._inflation = Inflation:new(_self, _self._config) + end + -- Load scripted climax scenarios table.insert(_self._scenarios, Sexbound.ScriptedClimax.Scenario1:new(_self)) @@ -404,7 +409,11 @@ function Sexbound.Actor.Climax:shoot(...) world.sendEntityMessage(_actor:getEntityId(), "Sexbound:Climax:Feed") end if (interaction == "direct" or interaction == "toy_dick") and self._config.enableInflation then - Sexbound.Messenger.get('main'):send(self, _actor, "Sexbound:Climax:Inflate", self:getInflationRate()) + local genital = self._parent:getGenitalType() + local liquid = self._config.projectileLiquid[self._parent:getSpecies()] or self._config.projectileLiquid["default"] + liquid = liquid[genital] + Sexbound.Messenger.get('main'):send(self, _actor, "Sexbound:Climax:Inflate", self._inflation:generateLoad(liquid, self:getInflationRate())) + end end end @@ -488,15 +497,21 @@ function Sexbound.Actor.Climax:spawnItem(...) end --- Increases level of inflation -function Sexbound.Actor.Climax:inflate(amount) - amount = amount or 0.1 - local oldAmount = self._inflation - local actor = self._parent - self._inflation = self._inflation + amount - self:getLog():debug("Actor "..actor:getActorNumber().." gotten inflation request. New value: "..self._inflation) - if oldAmount < self:getInflationThreshold() and self._inflation >= self:getInflationThreshold() then +function Sexbound.Actor.Climax:inflate(inflationLoad) + -- Regenerate received inflation load (fills in potential invalid values) + inflationLoad = self._inflation:generateLoad(inflationLoad.liquid, inflationLoad.quantity) + + -- Add new inflation load + local oldAmount = self._inflation:getTotalInflation() + self._inflation:addLoad(inflationLoad) + local newAmount = self._inflation:getTotalInflation() + + -- Handle case where total inflation passed the treshold + if oldAmount < self:getInflationThreshold() and newAmount >= self:getInflationThreshold() then + local actor = self._parent actor:resetParts(actor:getAnimationState(), actor:getSpecies(), actor:getGender(), actor:resetDirectives(actor:getActorNumber())) - self:getLog():debug("Actor "..actor:getActorNumber().." passing threshold - reseting actor to show inflated belly sprite.") + self:getLog():debug("Actor "..actor:getActorNumber().." ("..actor:getName()..") passed inflation threshold; ".. + "reseting actor to show inflated belly sprite.") end end @@ -505,31 +520,62 @@ function Sexbound.Actor.Climax:inflationDrip(dt) self._dripTimer = math.max(0, self._dripTimer - dt) if self._dripTimer == 0 then - local oldAmount = self._inflation + local oldAmount = self._inflation:getTotalInflation() if oldAmount > 0 then local actor = self._parent + self:getLog():debug("Actor "..actor:getActorNumber().." ("..actor:getName()..") dripping.") - local dripQuantity = math.min(util.randomInRange(self:getDripRate()) * dt, self._inflation) - self._inflation = self._inflation - dripQuantity + -- Remove drip quantity from inflation + local dripQuantity = math.min(util.randomInRange(self:getDripRate()) * dt, self._inflation:getTotalInflation()) + local removedLiquids = self._inflation:removeQuantity(dripQuantity) + local newAmount = self._inflation:getTotalInflation() - if self._config.enableSpawnLiquids then + -- Spawn particles if enabled + if self._config.enableClimaxParticles then + animator.burstParticleEmitter("insemination-drip" .. actor:getActorNumber()) + end + + -- Spawn liquid projectiles if enabled + if self._config.enableSpawnLiquids and removedLiquids then + local projectileName = "dripping" local spawnPosition = actor:getClimaxSpawnPosition() or {0.0, 0.0} - local semenId = 157 - world.spawnLiquid(spawnPosition, semenId, dripQuantity) + local sourceEntityId = actor:getEntityId() + local spawnDirection = {0, -1} + local trackSourceEntity = false + local handler = {} + + -- Generate liquid spawn action for projectile + local actionOnReap = {} + local actionCount = 0 + for liquid, quantity in pairs(removedLiquids) do + self:getLog():debug("Actor "..actor:getActorNumber().." dripping. Spawning "..quantity.." "..liquid..".") + if liquid then + actionCount = actionCount + 1 + actionOnReap[actionCount] = { + action = "liquid", + liquid = liquid, + quantity = quantity + } + end + end + + -- Spawn projectile if at least one action was generated + if actionCount > 0 then + self:getLog():debug("Actor "..actor:getActorNumber().." dripping. Creating projectile.") + handler.actionOnReap = actionOnReap + world.spawnProjectile(projectileName, spawnPosition, sourceEntityId, spawnDirection, trackSourceEntity, handler) + end end - self:getLog():debug("Actor "..actor:getActorNumber().." dripping. New amount: "..self._inflation) - if oldAmount >= self:getInflationThreshold() and self._inflation < self:getInflationThreshold() then + -- Handle case where total inflation lowered under the treshold + if oldAmount >= self:getInflationThreshold() and newAmount < self:getInflationThreshold() then actor:resetParts(actor:getAnimationState(), actor:getSpecies(), actor:getGender(), actor:resetDirectives(actor:getActorNumber())) - self:getLog():debug("Actor "..actor:getActorNumber().." passing threshold - reseting actor to hide inflated belly sprite.") - end - - if self._config.enableClimaxParticles then - animator.burstParticleEmitter("insemination-drip" .. actor:getActorNumber()) + self:getLog():debug("Actor "..actor:getActorNumber().." ("..actor:getName()..") passed inflation threshold; ".. + "reseting actor to hide inflated belly sprite.") end end - self._dripTimer = math.min(2, 1 / self._inflation ^ self:getDripSpeed()) + self._dripTimer = math.min(2, 1 / self._inflation:getTotalInflation() ^ self:getDripSpeed()) end end @@ -651,7 +697,7 @@ end --- Returns the current inflation level offsetted by pregnancy function Sexbound.Actor.Climax:getAdjustedInflation() - local val = self._inflation + local val = self._inflation:getTotalInflation() if self:getParent():isVisiblyPregnant() then val = val + self:getInflationThreshold() end @@ -665,7 +711,7 @@ end --- Returns whether or not this actor is currently inflated function Sexbound.Actor.Climax:isInflated() - return self._inflation >= self:getInflationThreshold() + return self._inflation:getTotalInflation() >= self:getInflationThreshold() end --- Returns a sound effect by specifed name or the table of sound effects. diff --git a/scripts/sexbound/plugins/climax/inflation.lua b/scripts/sexbound/plugins/climax/inflation.lua new file mode 100644 index 00000000..de369223 --- /dev/null +++ b/scripts/sexbound/plugins/climax/inflation.lua @@ -0,0 +1,137 @@ +Inflation = {} +Inflation_mt = { + __index = Inflation +} + +function Inflation:new(parent, config) + local _self = setmetatable({ + _logPrefix = "INFL", + _parent = parent, + _config = config, + _actor = {}, + _log = {}, + _defaultLiquidId = config.projectileLiquid["default"]["male"] or "semen", + _loads = {}, + _loadCount = 0, + _totalInflation = 0 + }, Inflation_mt) + + _self._log = Sexbound.Log:new(_self._logPrefix, _self._parent:getRoot():getConfig()) + _self._actor = _self._parent._parent + _self:getLog():info("Inited Inflation Tracker for ".._self:getActorLogPrefix()) + + return _self +end + +function Inflation:addLoad(inflationLoad) + inflationLoad = { + liquid = inflationLoad.liquid or self._defaultLiquidId, + quantity = inflationLoad.quantity or 0 + } + if inflationLoad.quantity <= 0 then return end + + self:getLog():debug(self:getActorLogPrefix().." received inflation load. ".. + "Liquid: ["..inflationLoad.liquid.."] ".. + "Quantity: ["..inflationLoad.quantity.."]") + + -- Add to the last load if it has the same liquid id + if self._loadCount > 0 and self._loads[self._loadCount].liquid == inflationLoad.liquid then + local currentQuantity = self._loads[self._loadCount].quantity + local mergedQuantity = currentQuantity + inflationLoad.quantity + self._loads[self._loadCount].quantity = mergedQuantity + + self:getLog():debug(self:getActorLogPrefix().." same liquid as previous; merging loads. ".. + "Previous load quantity: ["..currentQuantity.."] ".. + "Merged quantity: ["..mergedQuantity.."]") + + else + self:getLog():debug(self:getActorLogPrefix().." liquid is different from previous load; adding new load.") + + self._loadCount = self._loadCount + 1 + self._loads[self._loadCount] = inflationLoad + end + + self._totalInflation = self._totalInflation + inflationLoad.quantity + + self:getLog():debug(self:getActorLogPrefix().." has ["..self:getloadCount().."] inflation loads. ".. + "Total inflation: [".. self:getTotalInflation().."]") +end + +function Inflation:removeQuantity(quantity) + if self._totalInflation <= 0 or self._loadCount <= 0 then return end + + local toRemove = quantity or 0 + local removedLiquids = {} + while toRemove > 0 and self._loadCount > 0 do + local lastLoadQuantity = self._loads[self._loadCount].quantity + self:getLog():debug(self:getActorLogPrefix().." removing ["..toRemove.."] to total inflation.") + self:getLog():debug(self:getActorLogPrefix().." load #"..self._loadCount.." contributes ["..lastLoadQuantity.."] to total inflation.") + + if toRemove < lastLoadQuantity then + self:getLog():debug(self:getActorLogPrefix().." removing ["..toRemove.."] from load #"..self._loadCount) + + -- Add to removed liquid quantity + local currentlyRemoved = removedLiquids[self._loads[self._loadCount].liquid] or 0 + removedLiquids[self._loads[self._loadCount].liquid] = currentlyRemoved + toRemove + + -- Remove entire quantity from previous load + self._loads[self._loadCount].quantity = lastLoadQuantity - toRemove + self._totalInflation = self._totalInflation - toRemove + toRemove = 0 + + self:getLog():debug(self:getActorLogPrefix().." load #"..self._loadCount.." contributes ["..self._loads[self._loadCount].quantity.."] to total inflation.") + else + self:getLog():debug(self:getActorLogPrefix().." removing load #"..self._loadCount) + + -- Add to removed liquid quantity + local currentlyRemoved = removedLiquids[self._loads[self._loadCount].liquid] or 0 + removedLiquids[self._loads[self._loadCount].liquid] = currentlyRemoved + self._loads[self._loadCount].quantity + + -- Remove previous load + self._loads[self._loadCount] = nil + self._loadCount = math.max(0, self._loadCount - 1) + self._totalInflation = math.max(0, self._totalInflation - lastLoadQuantity) + + -- Update quantity to remove + toRemove = toRemove - lastLoadQuantity + end + end + + self:getLog():debug(self:getActorLogPrefix().." has ["..self:getloadCount().."] inflation loads. ".. + "Total inflation: [".. self:getTotalInflation().."]") + + return removedLiquids +end + +function Inflation:clear() + for i=0, self._loadCount do self._loads[i] = nil end + self._loadCount = 0 + self._totalInflation = 0 +end + +function Inflation:getTotalInflation() + return self._totalInflation +end + +function Inflation:getloadCount() + return self._loadCount +end + +function Inflation:generateLoad(liquid, quantity) + return { + liquid = liquid or self._defaultLiquidId, + quantity = quantity or 0.1 + } +end + +function Inflation:getLog() + return self._log +end + +function Inflation:getActor() + return self._actor +end + +function Inflation:getActorLogPrefix() + return "Actor "..self:getActor():getActorNumber().." ("..self:getActor():getName()..")" +end \ No newline at end of file