• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[TFS 1.5] Sex System 2.0 🍑

Sarah Wesker

ƐƖєgαηт Sуηтαx ❤
Staff member
TFS Developer
Support Team
Joined
Mar 16, 2017
Messages
1,408
Solutions
154
Reaction score
1,958
Location
London
GitHub
MillhioreBT
Twitch
millhiorebt
Sex System 2.0 🍑

I already released a sex system some time ago, but it was using commands and it was a bit ugly.
Here I bring you version 2, which uses modal windows, fully compatible with TFS-master.
In addition to using modalwindows, sex will have benefits for both men and women.
Here is a list of the benefits:
Mothers can now claim child support.
Women gain regeneration, skills, stats, speed, ect... depending on the type of sex.
The same for men, they gain special attributes.
If someone bothers you a lot, you can add them to the blacklist.
If you really like someone, you can add them to the whitelist.

Everything is configurable, although if you want to add different things you might need a bit of knowledge as I didn't make the script very newbie friendly.
Below is a sample video and the code:
View attachment eeeret.mp4


Talkaction: !sex
data/scripts/sex_system2.lua
Lua:
--[[
    This is the virtual sex system created by 𝓜𝓲𝓵𝓵𝓱𝓲𝓸𝓻𝓮 𝓑𝓣
    For TFS-master 1.5 server

    The virtual sex system is a system that allows players to have sexual relations with other players, this system is fully configurable and can be modified to suit the user.

    -- WARNING --
    > If you are a programmer and you want to modify the system, you can do it, surely you know what you are doing.
    > If you're not a programmer, don't modify anything you don't understand, as it may cause system errors.
]]--

local talkActionWords = "!sex"
local sexSystem = {
    types = {"oral", "anal", "vaginal"},
    intensities = {"mild", "normal", "strong", "extreme", "painful"},
    conditionTimes = { -- Depending on the intensity the benefits will last longer active.
        mild = 60000, -- 1 Minute
        normal = 120000, -- 2 Minutes
        strong = 180000, -- 3 Minutes
        extreme = 240000, -- 4 Minutes
        painful = 300000 -- 5 Minutes
    },

    childrenId = 6579,
    childrenMaintance = 10, -- 10% gold from father to mother.
    childrenChance = 50, -- 50% chance of getting pregnant if the condom fails.

    whiteListStorageBase = 670000,
    whiteListStorageMax = 100,
    blackListStorageBase = 670100,
    blackListStorageMax = 100
}

local sexPoses = {
    [PLAYERSEX_FEMALE] = {
        oral = {"Dressed and seated", "The 69, but on the side", "On knees", "Above his mouth"},
        anal = {"The doggy", "Side", "Face down", "Riding"},
        vaginal = {"The back lying", "Romantic landscape", "The amazon", "The missionary"}
    },

    [PLAYERSEX_MALE] = {
        oral = {"The lazy", "The supported", "The king of the world", "The automatic"},
        anal = {"At the border", "Standing", "The immersion", "The little spoon"},
        vaginal = {"The rabbit", "Legs to shoulder", "The cowgirl", "Forklift truck"}
    }
}

local sexCondons = {
    { type = "Free", security = 0.25, price = 0 },
    { type = "Latex", security = 0.99, price = 100 },
    { type = "Polyurethane", security = 0.75, price = 50 },
    { type = "Animal skin", security = 0.50, price = 10 }
}

local boostTypes = {
    regeneration = CONDITION_REGENERATION,
    skills = CONDITION_ATTRIBUTES,
    stats = CONDITION_ATTRIBUTES,
    speed = CONDITION_HASTE,
    critical = CONDITION_ATTRIBUTES,
    slowness = CONDITION_PARALYZE
}

local boostValues = {
    regeneration = {"HEALTHGAIN", "MANAGAIN"},
    skills = {"SKILL_AXE", "SKILL_CLUB", "SKILL_SWORD"},
    stats = {"STAT_MAGICPOINTS"},
    speed = {"SPEED"},
    critical = {"SPECIALSKILL_CRITICALHITCHANCE", "SPECIALSKILL_CRITICALHITAMOUNT"},
    slowness = {"SPEED"}
}

local defaultValues = {
    regeneration = {
        HEALTHTICKS = 1000,
        MANATICKS = 1000
    }
}

local sexBoosts = {
    [PLAYERSEX_FEMALE] = {
        oral = {regeneration = 100, skills = 10, stats = 10, speed = 10},
        anal = {regeneration = 100, skills = 30, stats = 10 , slowness = 10},
        vaginal = {regeneration = 200, skills = 20, stats = 20, speed = 20}
    },

    [PLAYERSEX_MALE] = {
        oral = {regeneration = 100, skills = 10, stats = 10, speed = 10},
        anal = {regeneration = 100, skills = 30, stats = 10 , critical = 10},
        vaginal = {regeneration = 200, skills = 20, stats = 20, speed = 20}
    }
}

function sexSystem.applyBoosts(player, request)
    local sexBoost = sexBoosts[player:getSex()][request.type]
    if not sexBoost then
        return
    end

    for type, value in pairs(sexBoost) do
        local condition = Condition(boostTypes[type])
        condition:setParameter(CONDITION_PARAM_TICKS, sexSystem.conditionTimes[request.intensity])
        condition:setParameter(CONDITION_PARAM_SUBID, request.playerId)
        for _, boost in pairs(boostValues[type] or {}) do
            condition:setParameter(_G[string.format("CONDITION_PARAM_%s", boost)], value)
        end

        for boostType, boostValue in pairs(defaultValues[type] or {}) do
            condition:setParameter(_G[string.format("CONDITION_PARAM_%s", boostType)], boostValue)
        end

        player:addCondition(condition)
    end

    addEvent(function (playerId)
        local player = Player(playerId)
        if player then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You have received a boost for %s seconds.", math.floor(sexSystem.conditionTimes[request.intensity] / 1000)))
        end
    end, 3000, player:getId())
end

local function getIndexByList(player, listName, targetId)
    targetId = targetId or -1
    for key = sexSystem[string.format("%sStorageBase", listName)], sexSystem[string.format("%sStorageMax", listName)] - 1 do
        if player.storage[key] == targetId then
            return key
        end
    end
end

local function setIndexByList(player, listName, targetId, value)
    local key = getIndexByList(player, listName, targetId)
    if key then
        player.storage[key] = value or -1
        return true
    end
end

local function hasWhiteList(player, targetId) return getIndexByList(player, "whiteList", targetId) end
local function addWhiteList(player, targetId) return setIndexByList(player, "whiteList", -1, targetId) end
local function removeWhiteList(player, targetId) return setIndexByList(player, "whiteList", targetId) end

local function hasBlackList(player, targetId) return getIndexByList(player, "blackList", targetId) end
local function addWhiteList(player, targetId) return setIndexByList(player, "blackList", -1, targetId) end
local function removeWhiteList(player, targetId) return setIndexByList(player, "blackList", targetId) end

local function createChildren(player, target)
    local children = Game.createItem(sexSystem.childrenId, 1)
    if children then
        children:setCustomAttribute("fatherGuid", player:getGuid())
        children:setCustomAttribute("motherGuid", target:getGuid())
        children:setAttribute(ITEM_ATTRIBUTE_NAME, string.format("%s and %s's child", player:getName(), target:getName()))
        children:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Use the child to claim your maintenance.")
        local returnValue = target:addItemEx(children)
        if returnValue ~= RETURNVALUE_NOERROR then
            target:sendCancelMessage(returnValue)
            children:moveTo(target:getPosition())
        end
    end
    return true
end

local requests = {}

local function resetRequest(playerId, targetId)
    requests[playerId] = nil
    if targetId then
        requests[targetId] = nil
    end
end

local talkAction = TalkAction(talkActionWords)

function talkAction.onSay(player, words, param)
    local pos = player:getPosition()
    pos:getNextPosition(player:getDirection())
    local tile = Tile(pos)
    if not tile then
        player:sendCancelMessage("There is nothing in front of you.")
        return false
    end

    local target = tile:getTopVisibleCreature(player)
    if not target then
        player:sendCancelMessage("There is no one in front of you.")
        return false
    end

    local targetId = target:getId()
    if hasBlackList(player, targetId) and not hasWhiteList(player, targetId) then
        player:sendCancelMessage("You have this player in your blacklist.")
        return false
    end

    local request = requests[player:getId()]
    if request then
        if type(request) == "table" then
            sexSystem.handleRequest(player, target, request)
            return false
        end

        player:sendCancelMessage("You already have a request.")
        return false
    end

    local targetRequest = requests[target:getId()]
    if targetRequest then
        player:sendCancelMessage("The player already has a pending request.")
        return false
    end

    requests[player:getId()] = target:getId()
    sexSystem.sendSexTypesWindow(player, target)
    return false
end

talkAction:register()

function sexSystem.handleRequest(player, target, request)
    if player:getId() == request.playerId then
        player:sendCancelMessage("You already have a pending request.")
        return false
    end

    local target = Player(request.playerId)
    if not target then
        player:sendCancelMessage("The requesting player is not online.")
        return false
    elseif target:getId() ~= request.playerId then
        player:sendCancelMessage("The player you are looking at was not the one who requested the sex.")
        return false
    end

    return sexSystem.sendSexHandleRequestWindow(player, target, request)
end

function sexSystem.sendSexHandleRequestWindow(player, target, request)
    local modalWindow = ModalWindow{
        title = "Sex System - Request",
        message = string.format("The player %s wants to have sex with you, do you accept?", target:getName())
    }

    modalWindow:addChoice("No")
    modalWindow:addChoice("Yes")
    if not hasBlackList(player, request.playerId) then
        modalWindow:addChoice("Add to blacklist")
    end

    if not hasWhiteList(player, request.playerId) then
        modalWindow:addChoice("Add to whitelist")
    end

    modalWindow:addButton("Select", function (player, button, choice)
        local target = Player(request.playerId)
        if not target then
            player:sendCancelMessage("The requesting player is not online.")
            resetRequest(player:getId(), request.playerId)
            return false
        end

        if not choice or not table.contains({"Yes", "Add to whitelist"}, choice.text) then
            if choice.text == "Add to blacklist" then
                if not hasBlackList(player, request.playerId) then
                    addBlackList(player, request.playerId)
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have added %s to your blacklist.", target:getName()))
                end
            end

            player:sendCancelMessage("You have declined the request.")
            resetRequest(player:getId(), request.playerId)
            return false
        end

        if choice.text == "Add to whitelist" then
            if not hasWhiteList(player, request.playerId) then
                addWhiteList(player, request.playerId)
                player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have added %s to your whitelist.", target:getName()))
            end
        end

        sexSystem.startSex(target, player, request)
        resetRequest(player:getId(), request.playerId)
    end)
    modalWindow:addButton("Close")

    modalWindow:setDefaultEnterButton("Select")
    modalWindow:setDefaultEscapeButton("Close")
    return modalWindow:sendToPlayer(player)
end

function sexSystem.startSex(player, target, request)
    local con = request.con
    if player:getTotalMoney() < con.price then
        resetRequest(request.playerId, request.targetId)
        return false
    end

    player:removeTotalMoney(con.price)
    player:getPosition():sendMagicEffect(CONST_ME_HEARTS)
    target:getPosition():sendMagicEffect(CONST_ME_HEARTS)
    player:sendTextMessage(MESSAGE_LOOT, string.format("You have had {33780|%s} {33952|%s} sex {3035|%s} with {33780|%s}.", request.intensity, request.type, request.pose, target:getName()))
    target:sendTextMessage(MESSAGE_LOOT, string.format("You have had {33780|%s} {33952|%s} sex {3035|%s} with {33780|%s}.", request.intensity, request.type, request.pose, player:getName()))

    local childrenGenerated = nil
    if math.random() > con.security then
        if math.random(1, 100) <= sexSystem.childrenChance then
            local playerSex = player:getSex()
            if playerSex ~= target:getSex() then
                if playerSex == PLAYERSEX_MALE then
                    childrenGenerated = createChildren(player, target)
                else
                    childrenGenerated = createChildren(target, player)
                end
            end
        end
    end

    if not childrenGenerated then
        local fluid = Game.createItem(2016, FLUID_MILK, player:getPosition())
        if fluid then
            fluid:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, string.format("%s Fluids", player:getName()))
            fluid:decay(0)
        end

        local fluid = Game.createItem(2016, FLUID_MILK, target:getPosition())
        if fluid then
            fluid:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, string.format("%s Fluids", target:getName()))
            fluid:decay(0)
        end
    end

    sexSystem.applyBoosts(player, request)
    sexSystem.applyBoosts(target, request)
    resetRequest(request.playerId, request.targetId)
end

function sexSystem.sendSexTypesWindow(player, target)
    local modalWindow = ModalWindow{
        title = "Sex System - Type",
        message = string.format("Select the type of sex you want to have with %s", target:getName())
    }

    local playerSex = player:getSex()
    local targetSex = target:getSex()

    for _, sexType in pairs(sexSystem.types) do
        repeat
            if sexType == "vaginal" and playerSex == PLAYERSEX_MALE and targetSex == PLAYERSEX_MALE then
                break
            end

            modalWindow:addChoice(string.format("%s sex", sexType))
        until true
    end

    modalWindow:addButton("Accept", function (player, button, choice)
        local playerId = player:getId()
        if not choice or choice.text == "" then
            resetRequest(playerId)
            return false
        end

        local targetId = requests[playerId]
        if not targetId then
            resetRequest(playerId)
            return false
        end

        local target = Player(targetId)
        if not target then
            resetRequest(playerId)
            return false
        end

        sexSystem.sendSexIntensitiesWindow(player, target, sexSystem.types[choice.id])
        return true
    end)
    modalWindow:addButton("Cancel", function (player, button, choice)
        resetRequest(player:getId())
    end)

    modalWindow:setDefaultEnterButton("Accept")
    modalWindow:setDefaultEscapeButton("Cancel")
    return modalWindow:sendToPlayer(player)
end

function sexSystem.sendSexIntensitiesWindow(player, target, type)
    local modalWindow = ModalWindow{
        title = "Sex System - Intensity",
        message = string.format("Select the intensity with which you want to have %s sex with %s.", type, target:getName())
    }

    for _, intensity in pairs(sexSystem.intensities) do
        modalWindow:addChoice(intensity)
    end

    modalWindow:addButton("Accept", function (player, button, choice)
        local playerId = player:getId()
        if not choice or choice.text == "" then
            resetRequest(playerId)
            return false
        end

        local targetId = requests[playerId]
        if not targetId then
            resetRequest(playerId)
            return false
        end

        local target = Player(targetId)
        if not target then
            resetRequest(playerId)
            return false
        end

        sexSystem.sendSexPosesWindow(player, target, type, sexSystem.intensities[choice.id])
        return true
    end)
    modalWindow:addButton("Cancel", function (player, button, choice)
        resetRequest(player:getId())
    end)

    modalWindow:setDefaultEnterButton("Accept")
    modalWindow:setDefaultEscapeButton("Cancel")
    return modalWindow:sendToPlayer(player)
end

function sexSystem.sendSexPosesWindow(player, target, type, intensity)
    local modalWindow = ModalWindow{
        title = "Sex System - Poses",
        message = string.format("Select the position with which you want to have %s %s sex with %s.", intensity, type, target:getName())
    }

    local playerId = player:getId()
    local poses = sexPoses[player:getSex()]
    if not poses then
        resetRequest(playerId)
        return false
    end

    local poses = poses[type]
    if not poses then
        resetRequest(playerId)
        return false
    end

    for _, pose in pairs(poses) do
        modalWindow:addChoice(pose)
    end

    modalWindow:addButton("Accept", function (player, button, choice)
        local playerId = player:getId()
        if not choice or choice.text == "" then
            resetRequest(playerId)
            return false
        end

        local targetId = requests[playerId]
        if not targetId then
            resetRequest(playerId)
            return false
        end

        local target = Player(targetId)
        if not target then
            resetRequest(playerId)
            return false
        end

        sexSystem.sendSexCondonsWindow(player, target, type, intensity, poses[choice.id])
        return true
    end)
    modalWindow:addButton("Cancel", function (player, button, choice)
        resetRequest(player:getId())
    end)

    modalWindow:setDefaultEnterButton("Accept")
    modalWindow:setDefaultEscapeButton("Cancel")
    return modalWindow:sendToPlayer(player)
end

function sexSystem.sendSexCondonsWindow(player, target, type, intensity, pose)
    local modalWindow = ModalWindow{
        title = "Sex System - Condons",
        message = "Select the condom you want to use:"
    }

    for _, con in pairs(sexCondons) do
        modalWindow:addChoice(string.format("%s (%d%%) - %d gold", con.type, con.security * 100, con.price))
    end

    modalWindow:addButton("Accept", function (player, button, choice)
        local playerId = player:getId()
        if not choice or choice.text == "" then
            resetRequest(playerId)
            return false
        end

        local targetId = requests[playerId]
        if not targetId then
            resetRequest(playerId)
            return false
        end

        local target = Player(targetId)
        if not target then
            resetRequest(playerId)
            return false
        end

        local con = sexCondons[choice.id]
        if not con then
            resetRequest(playerId)
            return false
        end

        if player:getTotalMoney() < con.price then
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You don't have enough money.")
            resetRequest(playerId)
            return false
        end

        sexSystem.createRequest(player, target, type, intensity, pose, con)
        return true
    end)
    modalWindow:addButton("Cancel", function (player, button, choice)
        resetRequest(player:getId())
    end)

    modalWindow:setDefaultEnterButton("Accept")
    modalWindow:setDefaultEscapeButton("Cancel")
    return modalWindow:sendToPlayer(player)
end

function sexSystem.createRequest(player, target, type, intensity, pose, con)
    local request = {
        playerId = player:getId(),
        targetId = target:getId(),
        type = type,
        intensity = intensity,
        pose = pose,
        con = con
    }

    requests[player:getId()] = request
    requests[target:getId()] = request

    player:sendTextMessage(MESSAGE_LOOT, string.format("You have requested to have {33780|%s} {33952|%s} sex {3035|%s} with {33780|%s}.\nWait for it to respond to your request.", intensity, type, pose, target:getName()))
    target:sendTextMessage(MESSAGE_LOOT, string.format("{3038|%s} has asked you to have {33780|%s} {33952|%s} sex {3035|%s}.\nTo handle the request use the command {3035|%s}.", player:getName(), intensity, type, pose, talkActionWords))
    return true
end

local action = Action()

function action.onUse(player, item, fromPos, target, toPos, isHotkey)
    local fatherGuid = item:getCustomAttribute("fatherGuid")
    local motherGuid = item:getCustomAttribute("motherGuid")
    if not fatherGuid or not motherGuid then
        return false
    end

    local mother = Player(motherGuid)
    if not mother or motherGuid ~= player:getGuid() then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You are not the mother of this child.")
        return true
    end

    local father = Player(fatherGuid)
    if not father then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "The father of this child is offline.")
        return true
    end

    local maintance = math.ceil(father:getTotalMoney() * (sexSystem.childrenMaintance / 100))
    if player:getTotalMoney() < maintance then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "The father of this child doesn't have enough money to pay the maintance.")
        return true
    end

    father:removeTotalMoney(maintance)
    player:setBankBalance(player:getBankBalance() + maintance)
    player:sendTextMessage(MESSAGE_INFO_DESCR, string.format("You have received %d gold from the father of this child.", maintance))
    child:getPosition():sendMagicEffect(CONST_ME_GIFT_WRAPS)
    child:remove()
    return true
end

action:id(sexSystem.childrenId)
action:register()
 
noice GIF
 
When I went searching for itemId 6579, the line number brought up a dead minotaur, and I laughed for a solid 2 minutes thinking that is what you used for the children.
ahahahahaha, it definitely made me laugh even hahaha
 
Back
Top