Apply this bugfix and improvement patch with manual file replacement or manual file editing. This cannot be applied by the mod system. If you plan to use mods with this patch, apply this to the unmodded game, and if there is a 'backup' folder then before starting the game either delete the 'backup' folder or apply this to the 'backup' folder as well. The cumulative change log was getting bloated so I started this new one that assumes v4a as the base for line numbers. This patch contains all the bugfix changes for Strive 0.5.25. sexdescriptions.gd overhauled the entire file to improve performance (v5a) most functionality should remain unchanged though some small bugs were fixed including: (v5a) missing oxford comma in lists of 3 or more names wrongly capitalized "Your" [afuck] and [vfuck] tags now produce appropriate descriptions assets.gd fixed RNG indexing bug (v5a) replace line 112 with: return array[randi() % array.size()] origins.gd fixed RNG indexing bug (v5a) replace line 33 with: return globals.randomfromarray(rval) shopinventory.gd fix benign bug related to ayda quest items (v5a) replace lines 36-39 with: i.visible = (categories.everything && i.get_name() != 'Button') || (i.has_meta('category') && categories.get(i.get_meta('category'), false)) globals.gd added convenience function to aid in diagnosing Strive crashes added convenience function to handle error codes when having OS display folders add lines after 1466: #debugging function useful for diagnosing problems when Strive crashes and fails to produce normal error logs #recommended use: spread calls to this function(with unique strings) around code likely to be the source of the crash, run Strive, and view the trace file var firstTrace = true func traceFile(string): string += '\n' var file = File.new() var type if firstTrace: type = File.WRITE firstTrace = false else: type = File.READ_WRITE if file.open("res://DEBUG_TRACE.txt", type) == OK: if !firstTrace: file.seek_end() file.store_string(string) file.close() func shellOpenFolder(path): var retCode = OS.shell_open(ProjectSettings.globalize_path(path)) printErrorCode("OS shell opening " + path, retCode) add line after 1462: globals.traceFile("ERROR: " +str(msg)+ " Error code("+ str(code)+ "): "+ str(errorText[code])) fixed loop that changes the set of data that it iterates upon replace line 1389 with: for i in array.duplicate(): fixed RNG indexing bug (v5a) replace line 1351 with: return array[randi() % array.size()] update function dir_contents for dynamic file path, simplicity, and better error messaging replace lines 1301-1317 with: func dir_contents(target = saveDir): var dir = Directory.new() var array = [] if target.ends_with('/'): target.erase(target.length()-1,1) var retCode = dir.open(target) if retCode == OK: dir.list_dir_begin(true) var file_name = dir.get_next() while file_name != "": if dir.current_is_dir(): array += dir_contents(target + "/" + file_name) else: array.append(target + "/" + file_name) file_name = dir.get_next() return array else: printErrorCode("ERROR: could not access path: "+ str(target), retCode) added save data fixes for "pale_blue" skin and "Armor Breaker" weapon, removed combat group clearing replace lines 1294-1297 with: if person.skin == "pale_blue": person.skin = "pale blue" for item in globals.state.unstackables.values(): if item.enchant == null: item.enchant = '' if item.code == 'weaponshortsword': for e in item.effects: if e.has('type') && e.type == 'incombat': if e.effect == 'damage': if e.effectvalue == 6 && e.descript == "+9 Damage": e.effectvalue = 9 elif e.effect == 'passive': e.type = 'passive' e.effect = 'armorbreaker' e.erase('effectvalue') added function to create new savelist entries for files not currently registered in "progressdata" file, save file record now uses defaultmasternoun instead of "Master" add function after line 1173: func saveListNewEntry(savePath): var saveFile = File.new() saveFile.open(savePath, File.READ) var saveData = parse_json(saveFile.get_as_text()) if !(typeof(saveData) == TYPE_DICTIONARY && saveData.has('slaves') && saveData.has('state') && saveData.has('player') && saveData.has('resources')): return false var date = OS.get_datetime() for i in date: if int(date[i]) < 10: date[i] = '0' + str(date[i]) else: date[i] = str(date[i]) var count = 0 for person in saveData.slaves: if person.away.at != 'hidden': count += 1 savelist[savePath] = { name = saveData.state.defaultmasternoun + " " + saveData.player.name + "\nDay: " + str(saveData.resources.day) + '\nGold: [color=yellow] ' + str(saveData.resources.gold) + '[/color]\nSlaves: ' + str(count), path = savePath, date = date.hour + ":" + date.minute + " " + date.day + '.' + date.month + '.' + date.year, portrait = saveData.player.imageportait } return true improved readability of existing savelist function, save file record now uses defaultmasternoun instead of "Master" replace lines 1171-1172 with: savelist[savename] = { name = globals.state.defaultmasternoun + " " + player.name + "\nDay: " + str(resources.day) + '\nGold: [color=yellow] ' + str(resources.gold) + '[/color]\nSlaves: ' + str(slavecount()), path = savename, date = date.hour + ":" + date.minute + " " + date.day + '.' + date.month + '.' + date.year, portrait = player.imageportait } added dynamic file path and recursive directory creation replace lines 1154-1155 with: if !dir.dir_exists(saveDir): dir.make_dir_recursive(saveDir) fixed beauty traits having stacking effect on babies add lines after 758: if father.beautybase > mother.beautybase: baby.beautybase = father.beautybase + rand_range(-2,5) else: baby.beautybase = mother.beautybase + rand_range(-2,5) remove lines 746-750: if father.beautybase > mother.beautybase: baby.beautybase = father.beautybase + rand_range(-2,5) else: baby.beautybase = mother.beautybase + rand_range(-2,5) baby.cleartraits() add line after 735: baby.cleartraits() fixed RNG indexing bug (v5a) replace line 706 with: father = globals.newslave('randomany', 'random', randomfromarray(gender)) replace line 704 with: father = globals.newslave('randomcommon', 'random', randomfromarray(gender)) added function to manage re-usable ropes (v5a) add lines after 647: # calculates and returns the number of ropes recovered after use, mainly for adding to appropriate inventory. displays infotext if ropes are lost func calcRecoverRope(numPersons, usedFor = 'capture'): var lostRope = 0 if usedFor == 'capture': if variables.consumerope <= 0: return 0 for i in range(numPersons): if rand_range(0, 100) < curRopeBreakChance: lostRope += variables.consumerope curRopeBreakChance = 0 else: curRopeBreakChance += variables.ropewearoutrate numPersons *= variables.consumerope elif usedFor == 'sex': for i in range(numPersons): if rand_range(0, 100) < curRopeBreakChance / 2: lostRope += 1 curRopeBreakChance = 0 else: curRopeBreakChance += variables.ropewearoutrate / 2 if globals.main && lostRope > 0: globals.main.infotext(str(lostRope) + ' rope%s wore out from use' % ('s' if lostRope > 1 else ''),'red') added tab formatting to multi-line variable definition (v5a) add tab to lines 511-529 added variable to progress class for re-usable ropes (v5a) add line after 473 var curRopeBreakChance = 0 added tab formatting to multi-line variable definitions add tab to lines 300-322 fixed loadimage's handling of image files that don't exist replace lines 277-285 with: if path == null || typeof(path) == TYPE_OBJECT: return path if ResourceLoader.exists(path): return load(path) if !File.new().file_exists(path): return null var image = Image.new() var retVal = image.load(path) if retVal != OK: printErrorCode("loadimage("+str(path)+")", retVal) return null added dynamic file paths replace line 243 with: settings.open_encrypted_with_pass(progressFile, File.WRITE, 'tehpass') replace line 240 with: settings.open(settingsFile, File.WRITE) replace line 227 with: var setfolders = {portraits = appDataDir +'portraits/', fullbody = appDataDir + 'bodies/', mods = appDataDir + 'mods/'} setget savefolders replace line 219 with: settings.open_encrypted_with_pass(progressFile, File.READ, 'tehpass') replace line 209 with: settings.open_encrypted_with_pass(progressFile, File.READ, 'tehpass') replace line 195 with: settings.open_encrypted_with_pass(progressFile, File.READ, 'tehpass') replace line 192 with: if settings.file_exists(progressFile) == false: replace line 184 with: settings.open(settingsFile, File.READ) replace lines 180-181 with: if settings.file_exists(settingsFile) == false: settings.open(settingsFile, File.WRITE) added recursive directory creation replace lines 177-178 with: if !dir.dir_exists(i): dir.make_dir_recursive(i) added tab formatting to multi-line variable definitions add tab to lines 131-134 add tab to lines 119-121 add tab to lines 105-115 add tab to lines 98-102 add tab to lines 86-92 add tab to lines 69-83 removed extra person init replace lines 64 with: var player added array of godot image extensions add line after 16: var imageExtensions = ["png","jpg","webp"] added dynamic file paths changed saveDir to slightly improve its flexibility and added comment to clarify its mechanics(v5a) replace lines 14-15 with: var gameDir = "res://" var fileDir = "res://files/" var backupDir = "res://backup/" #for those that want to move appData to program folder, swap next two lines #var appDataDir = "res://appData/" var appDataDir = "user://" var saveDirDefault = appDataDir + "saves/" # saveDir is the current path which is created by modpanel.gd using saveDirDefault and saveID var saveDir = null var settingsFile = appDataDir + "settings.ini" var progressFile = appDataDir + "progressdata" changed game version to trigger save file fixes replace line 7 with: var gameversion = '0.5.26' Mansion.gd changed descriptions of meet and sex interactions in the interaction setup panel to be more accurate added red color to slave names who have not given consent replace lines 3902-3947 with: text += "[center][color=yellow]Meet[/color][/center]\nBuild relationship or train your servant: " for person in sexslaves: text += '[color=aqua]%s[/color]. ' % person.name_short() elif sexmode == 'sex': var consensual = true for person in sexslaves: if !person.consent: consensual = false break if sexslaves.empty(): text += "[center][color=yellow]Sex[/color][/center]\nCurrent participants: " else: if sexslaves.size() == 1: if consensual: text += "[center][color=yellow]Consensual Sex[/color][/center]" else: text += "[center][color=yellow]Rape[/color][/center]" elif sexslaves.size() in [2,3]: if consensual: text += "[center][color=yellow]Consensual Group Sex[/color][/center]" else: text += "[center][color=yellow]Group Rape[/color][/center]" else: text += "[center][color=yellow]Orgy[/color][/center]\n[color=aqua]Aphrodite's Brew[/color] is required to initialize an orgy." if consensual: text += "\nAll participants have given consent." else: text += "\nNot all participants have given consent." text += "\nCurrent participants: " for person in sexslaves: if person.consent: text += '[color=aqua]%s[/color], ' % person.name_short() else: text += '[color=#ff3333]%s[/color], ' % person.name_short() text = text.substr(0, text.length() - 2) + '.' for animal in sexanimals: if sexanimals[animal] != 0: text += "\n" + animal.capitalize() + '(s): ' + str(sexanimals[animal]) # elif sexmode == 'abuse': # text += "[center][color=yellow]Rape[/color][/center]" # text += "\nRequires a target and an optional assistant. Can be initiated with prisoners. \nCurrent target: " # for i in sexslaves: # text += i.dictionary('[color=aqua]$name[/color]') + ". " # text += "\nCurrent assistant: " # for i in sexassist: # text += i.dictionary('[color=aqua]$name[/color]') + ". " # for i in sexanimals: # if sexanimals[i] != 0: # text += "\n" + i.capitalize() + '(s): ' + str(sexanimals[i]) # get_node("sexselect/startbutton").set_disabled(sexslaves.size() == 1 && sexassist.size() <= 1) if sexslaves.empty(): text += '\nSelect slaves to start.' else: text += '\nClick Start to initiate.' text += "\n\nNon-sex Interactions left for today: " + str(globals.state.nonsexactions) text += "\nSex Interactions left for today: " + str(globals.state.sexactions) get_node("sexselect/sextext").set_bbcode(text) var enablebutton = true if sexslaves.size() == 0: enablebutton = false elif sexmode == 'meet': if globals.state.nonsexactions < 1: enablebutton = false elif sexmode == 'sex': if globals.state.sexactions < 1: enablebutton = false elif sexslaves.size() >= 4 && sexmode == 'sex' && globals.itemdict.aphroditebrew.amount < 1: enablebutton = false $sexselect/startbutton.disabled = !enablebutton added tooltips to buttons to indicate the remaining number of daily interactions per slave, added red color to buttons when pressed and slave has not given consent fixed tooltips to include lack of consent(v5a) replace lines 3825-3828 with: newbutton.set('custom_colors/font_color_pressed', Color(1,0.2,0.2)) tooltip = i.dictionary('$name gave you no consent.\n') var numInteractions = i.getRemainingInteractions() if numInteractions > 0: tooltip += i.dictionary('You can interact with $name %s more time%s today.' % [numInteractions, 's' if numInteractions > 1 else '']) else: newbutton.set_disabled(true) tooltip += i.dictionary('You have already interacted with $name too many times today.') newbutton.set_tooltip(tooltip) add line after 3822: var tooltip = '' replace lines 3810-3812 with: var numInteractions = i.getRemainingInteractions() if numInteractions > 0: newbutton.set_tooltip(i.dictionary('You can interact with $name %s more time%s today.' % [numInteractions, 's' if numInteractions > 1 else ''])) else: newbutton.set_disabled(true) newbutton.set_tooltip(i.dictionary('You have already interacted with $name too many times today.')) changed timer start code to match standard usage(v5a) replace line 3525 with: timer.set_autostart(true) fixed slaves already in farm being listed to be added again replace line 3337 with: selectslavelist(false, 'farmassignpanel', self, "person.sleep != 'farm'") changed URLs to use slave id rather than slave list position replace line 3144 with: var tempslave = globals.state.findslave( int(meta.replace('id',''))) replace lines 3140-3141 with: globals.slavetooltip( globals.state.findslave( int(meta.replace('id','')))) replace lines 3127-3129: text += '[url=id' + str(entry.id) + '][color=yellow]' + entry.name + '[/color][/url]' remove line 3120: $MainScreen/mansion/selfinspect/relativespanel/relativestext.set_meta("slaves", slavearray) remove lines 3084-3085: counter = 0 slavearray.clear() remove lines 3074-3075: var counter = 0 var slavearray = [] added tab formatting to multi-line variable definitions add tab to lines 2919-2923 add tab to lines 2818-2824 moved '}' on 2824 to next line fixed RNG indexing bug (v5a) replace lines 2782-2783 with: baby.titssize = globals.randomfromarray(sizes) baby.asssize = globals.randomfromarray(sizes) replace lines 2775-2776 with: baby.titssize = globals.randomfromarray(sizes) baby.asssize = globals.randomfromarray(sizes) replace lines 2768-2769 with: baby.titssize = globals.randomfromarray(sizes) baby.asssize = globals.randomfromarray(sizes) added tab formatting to multi-line variable definitions add tab to lines 2661-2664 add tab to appropriate lines between 2483 and 2568 changed the mansion data panel to increase visual clarity, simplified the code this had the side effect that the text will disappear when the screen or font is resized, but that was remedied by triggering an update renamed "text" iterators to "column" for clarity because the name was already used in that function(v5a) replace lines 2048-2189 with: var stateRangeDict = { health = [[0.4, colordict.low +'Wounded[/color]'], [0.75, colordict.med +'Injured[/color]'], [1.0, colordict.high +'Healthy[/color]']], energy = [[0.2, colordict.low +'Wasted[/color]'], [0.5, colordict.med +'Tired[/color]'], [1.0, colordict.high +'Lively[/color]']], stress = [[50, colordict.high +"Content[/color]"], [80, colordict.med +'Stressed[/color]'], [120, colordict.low +"On verge[/color]"]], } var conditionRanges = [ [20, "[color=#ff4949]in a complete mess"], [40, "[color=#FFA500]very dirty"], [60, "[color=yellow]quite unclean"], [80, "[color=lime]passably clean"], [100, "[color=green]immaculate"], ] # custom case format string handling, formatStr must have the following fields in order: color, count, capacity, pluralStr func fillRoomText(formatStr, count, capacity, pluralStr): var color if count < capacity: color = colordict.high elif count == capacity: color = colordict.med else: color = colordict.low return formatStr % [color, count, capacity, pluralStr if (count != 1) else ''] # if person is not away, creates colored link to open information screen for person, else provides simple colored text func createPersonURL(person): if person == null: return "[color=yellow]Unassigned[/color]" if person.away.duration != 0: return "[color=aqua]" + person.name_short() + "[/color] [color=yellow](away)[/color]" return "[color=aqua][url=id" + person.id + "]" + person.name_short() + "[/url][/color]" # takes an array of paired values(the first being the high end(not included) of the range, and the second is the returned value) and a key # iterates across the array to find the first pair with a range containing the given key, and returns the pair's second value, else the last pair is returned # format: [[60, 'F'], [70, 'D'], [80, 'C'], [90, 'B'], [100, 'A']] # example: findLowestRange(format, 70) -> 'C' func findLowestRange(arrayRanges, key): for pair in arrayRanges: if key < pair[0]: return pair[1] return arrayRanges.back()[1] #convenience function for adding cells to table in RichTextLabel func addTableCell(label, text, align=RichTextLabel.ALIGN_LEFT): label.push_cell() if align != RichTextLabel.ALIGN_LEFT: label.push_align(align) label.append_bbcode(text) if align != RichTextLabel.ALIGN_LEFT: label.pop() label.pop() func build_mansion_info(): var textnode = get_node("MainScreen/mansion/mansioninfo") var text textnode.show() var sleepers = globals.count_sleepers() text = 'You are at your mansion, which is located near [color=aqua]'+ globals.state.location.capitalize()+'[/color].\n\n' text += "Mansion is " + findLowestRange(conditionRanges, globals.state.condition) + "[/color]." text += fillRoomText("\n\nYou have %s%s/%s[/color] bed%s occupied in the communal room.", sleepers.communal, globals.state.mansionupgrades.mansioncommunal, 's') text += fillRoomText("\nYou have %s%s/%s[/color] personal room%s assigned for living.", sleepers.personal, globals.state.mansionupgrades.mansionpersonal, 's') text += fillRoomText("\nYour bed is shared with %s%s/%s[/color] person%s besides you.", sleepers.your_bed, globals.state.mansionupgrades.mansionbed, 's') text += fillRoomText("\n\nYour jail has %s%s/%s[/color] cell%s filled.", sleepers.jail, globals.state.mansionupgrades.jailcapacity, 's') if globals.state.farm >= 3: text += fillRoomText("\nYour farm has %s%s/%s[/color] booth%s holding livestock.", sleepers.farm, variables.resident_farm_limit[globals.state.mansionupgrades.farmcapacity], 's') text += "\n------------------------------------------------------------------------------" textnode.set_bbcode(text) var jobdict = {headgirl = null, jailer = null, farmmanager = null, cooking = null, nurse = null, labassist = null} for i in globals.slaves: if jobdict.has(i.work) && i.away.at != 'hidden': jobdict[i.work] = i textnode.push_table(2) if globals.slaves.size() >= 8: addTableCell(textnode, "Headgirl: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.headgirl)) addTableCell(textnode, "Jailer: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.jailer)) if globals.state.farm >= 3: addTableCell(textnode, "Farm Manager: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.farmmanager)) addTableCell(textnode, "Chef: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.cooking)) addTableCell(textnode, "Nurse: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.nurse)) if globals.state.mansionupgrades.mansionlab > 0: addTableCell(textnode, "Lab Assistant: ", RichTextLabel.ALIGN_RIGHT) addTableCell(textnode, createPersonURL(jobdict.labassist)) textnode.pop() textnode.append_bbcode("\n------------------------------------------------------------------------------") if globals.state.playergroup.empty(): textnode.append_bbcode("\nCombat Group: Nobody is assigned to follow you.") else: textnode.append_bbcode("\n") textnode.push_table(4) for column in ["Health", "Energy", "Stress", "Combat Group"]: addTableCell(textnode, column) for column in ["~~~~~~~~~~~ ", "~~~~~~~~~ ", "~~~~~~~~~~~ ", "~~~~~~~~~~~~~~~~~ "]: addTableCell(textnode, column) for i in globals.state.playergroup.duplicate(): var person = globals.state.findslave(i) if person != null: var temp = [findLowestRange( stateRangeDict.health, float(person.stats.health_cur)/person.stats.health_max), findLowestRange( stateRangeDict.energy, float(person.stats.energy_cur)/person.stats.energy_max), findLowestRange( stateRangeDict.stress, person.stress), createPersonURL(person)] #addTableCell(textnode, +" ", RichTextLabel.ALIGN_RIGHT) for column in temp: addTableCell(textnode, column) else: globals.state.playergroup.erase(i) textnode.pop() added reusable rope, simplified some code (v5a) replace line 2042: globals.itemdict['rope'].amount += globals.state.calcRecoverRope(array.size()) replace lines 2029-2032 with: var array = globals.state.capturedgroup globals.state.capturedgroup = [] var nojailcells = false fixed RNG indexing bug (v5a) replace lines 1961-1962 with: path = musicdict[globals.randomfromarray(['mansion1','mansion2','mansion3','mansion4'])] replace lines 1957-1958 with: path = musicdict[globals.randomfromarray(['combat1', 'combat3'])] remove line 1953: var array = [] use dynamic file paths, use convenience function shellOpenFolder replace lines 1884-1885 with: globals.shellOpenFolder(globals.saveDir) replace line 1865 with: savefilename = globals.saveDir + text changed autosaves to no longer use multithreading changed loading saved progress to null reference to mansion to prevent crashes replace lines 1856-1857 with: globals.main = null use dynamic file paths, simplify code replace lines 1842-1844 with: if dir.file_exists(savefilename): dir.remove(savefilename) use dynamic file paths, use function to create new savelist entries for files not currently registered in "progressdata" file, use recursive directory creation replace lines 1802-1807 with: if !globals.savelist.has(i): if globals.saveListNewEntry(i): node.get_node("date").set_text(globals.savelist[i].get('date')) else: node.get_node("date").set_text(globals.savelist[i].get('date')) node.get_node("name").set_text(i.replace(globals.saveDir,'')) replace lines 1792-1793 with: if !dir.dir_exists(globals.saveDir): dir.make_dir_recursive(globals.saveDir) replace line 1779 with: var savefilename = globals.saveDir + 'autosave' changed transition from mansion to main menu to null reference to mansion to prevent crashes add line after 1752: globals.main = null simplified and improved some code (v5a) replace lines 1502-1533 with: var quests var idx var town for guild in globals.state.repeatables: quests = globals.state.repeatables[guild] idx = 0 while idx < quests.size(): if quests[idx].taken: idx += 1 else: quests.remove(idx) town = guild.replace("slaveguild", "") for ii in range(-1, randi() % 2): globals.repeatables.generatequest(town, 'easy') for ii in range(-1, randi() % 2): globals.repeatables.generatequest(town, 'medium') for ii in range(-1, randi() % 1): globals.repeatables.generatequest(town, 'hard') changed autosaves to no longer use multithreading, simplified code, fixed rare problem with creation of savelist entries for autosaves replace lines 1476-1496 with: var dir = Directory.new() if !dir.dir_exists(globals.saveDir): dir.make_dir_recursive(globals.saveDir) var filearray = globals.dir_contents() var path1 = globals.saveDir + 'autosave1' var path2 = globals.saveDir + 'autosave2' var path3 = globals.saveDir + 'autosave3' if filearray.has(path2): dir.rename(path2, path3) if globals.savelist.has(path2): globals.savelist[path3] = globals.savelist[path2] else: globals.savelistentry(path3) if filearray.has(path1): dir.rename(path1, path2) if globals.savelist.has(path1): globals.savelist[path2] = globals.savelist[path1] else: globals.savelistentry(path2) globals.save_game(path1) remove lines 1458-1459: if thread.is_active(): thread.wait_to_finish() remove line 1453: var thread = Thread.new() fixed RNG indexing bug (v5a) replace lines 1438-1439 with: var truesprite = globals.randomfromarray(alisesprite[state]) var showtext = globals.player.dictionary( globals.randomfromarray(alisetext[state])) added tab formatting to multi-line variable definitions added tab to start of lines 1427-1430 added tab to start of lines 1421-1424 simplified some code replace line 1388 with: elif globals.state.mainquest >= 40 && !globals.state.plotsceneseen.has('hademelissa'): replace line 1382 with: elif globals.state.mainquest >= 36 && !globals.state.plotsceneseen.has('frostfordscene'): replace line 1377 with: elif globals.state.mainquest >= 27 && !globals.state.plotsceneseen.has('slaverguild'): replace line 1372 with: elif globals.state.mainquest >= 24 && !globals.state.plotsceneseen.has('hade2'): replace line 1367 with: elif globals.state.mainquest >= 18 && !globals.state.plotsceneseen.has('hade1'): replace line 1362 with: if globals.state.mainquest >= 16 && !globals.state.plotsceneseen.has('garthorscene'): rounded dailyeventcountdown to simplify number and make the range of times easier to understand, this slightly reduces countdown (v5a) replace line 1353 with: globals.state.dailyeventcountdown = round(rand_range(5,10)) fixed event timers being reduced more than once per day add line after line 1335: globals.state.upcomingevents.sort_custom(self, 'sortEvents') add lines after 1309: static func sortEvents(a, b): return a.duration < b.duration simplified and improved some code (v5a) replace lines 1241-1252 with: for guildQuests in globals.state.repeatables.values(): var idx = 0 while idx < guildQuests.size(): var quest = guildQuests[idx] if quest.taken: quest.time -= 1 if quest.time < 0: text0.bbcode_text += '[color=#ff4949]You have failed to complete your quest at ' + quest.location.capitalize() +'.[/color]\n' guildQuests.remove(idx) else: idx += 1 elif randf() < 0.1: guildQuests.remove(idx) else: idx += 1 increased the rate at which slaves are cycled in and out of slave guilds replace lines 1208-1217 with: for guild in globals.guildslaves: var slaves = globals.guildslaves[guild] count = round(clamp(0.25, 0.75, 0.01 * slaves.size() + rand_range(0.1, 0.4)) * slaves.size()) for i in range(count): slaves.remove(randi() % (slaves.size()*7/4) % slaves.size()) if slaves.size() < 4: get_node("outside").newslaveinguild(2, guild) if slaves.size() < 10 && randf() < 0.85: get_node("outside").newslaveinguild(1, guild) if randf() < 0.5: get_node("outside").newslaveinguild(1, guild) fixed exploit for jobs with per slave xp gain add line after 1186: farmmanager.xp += count * 5 add line after 1155: count += 1 add line after 1153: count = 0 add line after 1149: jailer.xp += count * 5 replace line 1143 with: count += 1 add line after 1140: count = 0 add line after 1136: headgirl.xp += 3 * count replace line 1120 with: count += 1 add line after 1117: count = 0 add drunk effect to exceptions for Dark Elf racial bonus replace line 758 with: if person.race != 'Dark Elf' || (!i.code in ['bandaged','sedated', 'drunk'] && randf() > 0.5): fixed housekeeper specialization doing far too much cleaning replace line 745 with: globals.state.condition = (5.5 + (person.sagi+person.send)*6)/2 commented out unused variable that overlaps with iterator names(v5a) add # to start of line 598: # var person generalized prisonbutton to work for all categories replace lines 584-585 with: func _on_category_pressed(): fixed url links providing direct access to slaves that are in farm(now links to farm) replace lines 580-582 with: if person.sleep == 'farm': _on_farm_pressed() farminspect(person) else: currentslave = globals.slaves.find(person) get_tree().get_current_scene().hide_everything() $MainScreen/slave_tab.slavetabopen() fixed residents count including event hidden slaves replace line 575 with: get_node("charlistcontrol/CharList/res_number").set_bbcode('[center]Residents: ' + str(globals.slavecount()) +'[/center]') changed the mansion slave list to be more efficient and added more categories to the GUI to better control what is visible in the list replace lines 465-573 with: var listinstance = load("res://files/listline.tscn") var awayText = { 'in labor': 'will be resting after labor for ', 'training': 'will be undergoing training for ', 'nurture': 'will be undergoing nurturing for ', 'growing': 'will keep maturing for ', 'lab': 'will be undergoing modification for ', 'rest': 'will be taking a rest for ', 'vacation': 'will be on vacation for ', 'default': 'will be unavailable for ', } func createSlaveListNode(personlist, person, nodeIndex, visible): var node = listinstance.instance() node.set_meta('id', person.id) personlist.add_child(node) personlist.move_child(node, nodeIndex) var nameNode = node.find_node('name') nameNode.connect("mouse_entered", globals, 'slavetooltip', [person]) nameNode.connect("mouse_exited", globals, 'slavetooltiphide') nameNode.connect('pressed', self, 'openslavetab', [person]) updateSlaveListNode(node, person, visible) func updateSlaveListNode(node, person, visible): node.visible = visible #fix permanent details vs updated node.find_node('name').set_text( person.name_long() + ("(+)" if (person.xp >= 100) else "")) node.find_node('health').set_normal_texture( person.health_icon()) node.find_node('healthvalue').set_text( str(round(person.health))) node.find_node('obedience').set_normal_texture( person.obed_icon()) node.find_node('stress').set_normal_texture( person.stress_icon()) if person.imageportait != null: node.find_node('portait').set_texture( globals.loadimage(person.imageportait)) # awayLabel.get_content_height() will not give the correct value until it has rendered a frame, so call_deferred is used to fix the size after that func fixAwayLabel(awayLabel): awayLabel.rect_min_size.y = awayLabel.get_content_height() func rebuild_slave_list(): var personList = get_node("charlistcontrol/CharList/scroll_list/slave_list") var categoryButtons = [personList.get_node("mansionCategory"), personList.get_node("prisonCategory"), personList.get_node("farmCategory"), personList.get_node("awayCategory")] var awayLabel = personList.get_node('awayLabel') var nodeIndex = 0 var isSlaveAway = false for catIdx in range(3): personList.move_child( categoryButtons[catIdx], nodeIndex) nodeIndex += 1 var startIndex = nodeIndex for person in globals.slaves: if person.away.duration != 0: if person.away.at != 'hidden': isSlaveAway = true continue if catIdx == 0: if person.sleep == 'jail' || person.sleep == 'farm': continue elif catIdx == 1: if person.sleep != 'jail': continue elif catIdx == 2: if person.sleep != 'farm': continue if nodeIndex < personList.get_children().size() - (3 - catIdx): if personList.get_children()[nodeIndex].has_meta('id') && personList.get_children()[nodeIndex].get_meta('id') == person.id: updateSlaveListNode(personList.get_children()[nodeIndex], person, categoryButtons[catIdx].pressed) else: #search for correct node var notFound = true for searchIndex in range(nodeIndex, personList.get_children().size()): var searchNode = personList.get_children()[searchIndex] if searchNode.has_meta('id') && searchNode.get_meta('id') == person.id: personList.move_child( searchNode, nodeIndex) updateSlaveListNode(searchNode, person, categoryButtons[catIdx].pressed) notFound = false break if notFound: createSlaveListNode(personList, person, nodeIndex, categoryButtons[catIdx].pressed) else: createSlaveListNode(personList, person, nodeIndex, categoryButtons[catIdx].pressed) nodeIndex += 1 categoryButtons[catIdx].visible = (startIndex != nodeIndex) personList.move_child( categoryButtons[3], nodeIndex) categoryButtons[3].visible = isSlaveAway nodeIndex += 1 personList.move_child( awayLabel, nodeIndex) awayLabel.visible = isSlaveAway && categoryButtons[3].pressed nodeIndex += 1 if isSlaveAway && categoryButtons[3].pressed: var text = '' for person in globals.slaves: if person.away.duration != 0 && person.away.at != 'hidden': text += "%s[color=aqua]%s[/color] %s[color=yellow]%s day%s[/color]." % ['' if text.empty() else '\n', person.name_long(), awayText.get(person.away.at, awayText.default), person.away.duration, 's' if (person.away.duration > 1) else ''] awayLabel.bbcode_text = text call_deferred("fixAwayLabel", awayLabel) for clearIndex in range(nodeIndex, personList.get_children().size()): var clearNode = personList.get_children()[clearIndex] if clearNode.has_meta('id'): clearNode.hide() clearNode.queue_free() fixed RNG indexing bug (v5a) replace line 396 with: var person = globals.newslave( globals.randomfromarray(testslaverace), testslaveage, testslavegender, globals.randomfromarray(testslaveorigin)) replace line 312 with: var person = globals.newslave( globals.randomfromarray(testslaverace), testslaveage, testslavegender, globals.randomfromarray(testslaveorigin)) use dynamic file paths replace line 298 with: globals.save_game(name) replace line 286 with: var name = globals.saveDir + globals.player.name + " - Main Quest Completed" changed URLs to use slave id rather than slave list position replace lines 260-276 with: if meta.find('id') >= 0: globals.slavetooltip( globals.state.findslave( meta.replace('id',''))) func slaveclicked(meta): globals.slavetooltiphide() if meta.find('id') >= 0: globals.openslave( globals.state.findslave( meta.replace('id',''))) added fix to new mansion info vanishing after screen resize add lines after 130: func on_resize_screen(): if get_node("MainScreen/mansion/mansioninfo").visible: call_deferred("build_mansion_info") add line after 77: get_tree().get_root().connect('size_changed',self,'on_resize_screen') Mansion.tscn add slave list categories replace line 9011 with: [connection signal="pressed" from="charlistcontrol/CharList/scroll_list/slave_list/mansionCategory" to="." method="_on_category_pressed"] [connection signal="pressed" from="charlistcontrol/CharList/scroll_list/slave_list/prisonCategory" to="." method="_on_category_pressed"] [connection signal="pressed" from="charlistcontrol/CharList/scroll_list/slave_list/farmCategory" to="." method="_on_category_pressed"] [connection signal="pressed" from="charlistcontrol/CharList/scroll_list/slave_list/awayCategory" to="." method="_on_category_pressed"] fixed obscured text in labels for defeated persons by reducing vertical separation between lines of text in same label add line after 6508: custom_constants/line_spacing = -6 fix size of scroll container for mods replace lines 3205-3206 with: margin_right = -40.0 margin_bottom = 538.0 increased size of mansion info text box replace lines 1312-1313 with: margin_right = 742.0 margin_bottom = -71.0 add slave list categories replace lines 1234-1253 with: [node name="mansionCategory" type="Button" parent="charlistcontrol/CharList/scroll_list/slave_list"] show_behind_parent = true margin_right = 383.0 margin_bottom = 29.0 size_flags_horizontal = 3 size_flags_vertical = 2 theme = ExtResource( 43 ) custom_styles/disabled = SubResource( 6 ) toggle_mode = true pressed = true text = "Mansion" [node name="prisonCategory" type="Button" parent="charlistcontrol/CharList/scroll_list/slave_list"] show_behind_parent = true margin_top = 33.0 margin_right = 383.0 margin_bottom = 62.0 size_flags_horizontal = 3 size_flags_vertical = 2 theme = ExtResource( 43 ) custom_styles/disabled = SubResource( 6 ) toggle_mode = true text = "Jail" [node name="farmCategory" type="Button" parent="charlistcontrol/CharList/scroll_list/slave_list"] show_behind_parent = true margin_top = 66.0 margin_right = 383.0 margin_bottom = 95.0 size_flags_horizontal = 3 size_flags_vertical = 2 theme = ExtResource( 43 ) custom_styles/disabled = SubResource( 6 ) toggle_mode = true text = "Farm" [node name="awayCategory" type="Button" parent="charlistcontrol/CharList/scroll_list/slave_list"] show_behind_parent = true margin_top = 99.0 margin_right = 383.0 margin_bottom = 128.0 size_flags_horizontal = 3 size_flags_vertical = 2 theme = ExtResource( 43 ) custom_styles/disabled = SubResource( 6 ) toggle_mode = true text = "Away" [node name="awayLabel" type="RichTextLabel" parent="charlistcontrol/CharList/scroll_list/slave_list"] margin_top = 132.0 margin_right = 383.0 margin_bottom = 162.0 rect_min_size = Vector2( 290, 30 ) bbcode_enabled = true bbcode_text = "Slave is away for X days." text = "Slave is away for X days." scroll_active = false resized slave list to fill space better replace lines 1138-1140 with: margin_right = 383.0 margin_bottom = 162.0 size_flags_horizontal = 3 replace lines 1130-1132 with: margin_left = -393.0 margin_top = 33.0 margin_right = -10.0 mainmenu.scn updated changelog at TextureFrame/changelog/RichTextLabel(v5a) added green color to version numbers to make them more visually distinct [color=green]...[/color] add following text to bbcode: Changed Melissa's quest after requiring laboratory to require player to use it Changed rope to be reusable with chance of wearing out Umbra Exchange now works for more than 3 items at a time Reworked consent and resistance during interactions Slaves can now participate in 2 interactions per day Reworked dynamic text system for sex to be significantly faster Fixed gallery unlocks for Ayda and Zoe Reworked mod system and updated in-game help for modding Reworked the Constants mod Polished a lot of GUI Long list of bug fixes provided by Ankmairdor increased size of modpanel report popup TextureFrame/modpanel/restartpanel position = (84, 95) size = (800, 500) TextureFrame/modpanel/restartpanel/Control position = (-331, -137) size = (1360, 768) TextureFrame/modpanel/restartpanel/RichTextLabel size = (740, 395) TextureFrame/modpanel/restartpanel/restartbutton position = (450, 440) TextureFrame/modpanel/restartpanel/continuebutton position = (224, 440) fixed person customizaton remaining visible when canceling new game move TextureFrame/newgame/stage8 above TextureFrame/newgame/stage6 in tree fixed positions of mod panel buttons TextureFrame/modpanel/applymods position.y = 630 TextureFrame/modpanel/closemods position.y = 630 TextureFrame/modpanel/disablemods position.y = 630 add GUI for save folder ID create Label TextureFrame/modpanel/LabelSaveFolder below FileDialog in tree position = (57, 590) size = (122, 20) text = "Save Folder ID" tooltip = "This value is added to the save folder's name to create and access a different save folder. This is recommended for identifying and isolating saves that will require specific mods to load correctly (e.g., mods that add new content). This can be useful if the user has multiple instances of Strive with different mods installed." create LineEdit TextureFrame/modpanel/saveFolderID below LabelSaveFolder in tree position = (190, 583) size = (190, 34) Placeholder/text = "< Default >" tooltip = "Current save folder:" connect signal text_changed Path to Node = .. Method in Node = _on_saveFolderID_text_changed updated modding help guide for new features and added clarity to some sections TextureFrame/modpanel/Panel/RichTextLabel changed bbCode_text, which can be found in ModdingGuide.txt mainmenu.gd fixed person customizaton remaining visible when canceling new game remove line 1354: get_node("TextureFrame/newgame/stage6").set_as_toplevel(true) fixed skin colors getting unexpectedly changed, simplified some code replace lines 1021-1026 with: if button.get_name() == 'tits': makeoverPerson.titssize = button.get_item_text(item) elif button.get_name() in ['penis','skin']: makeoverPerson[button.get_name()] = button.get_item_text(item) else: makeoverPerson[button.get_name()] = button.get_item_text(item).replace(" ", "_") changed penis sizes to exclude 'none' as this conflicts with sex assignment (v5a) add line after 934: for i in ['none', 'small', 'average', 'big']: replace line 931 with: for i in ['small', 'average', 'big']: fixed RNG indexing bug (v5a) replace line 654 with: slaveDefaults.age = globals.randomfromarray(ageArray) replace line 650 with: slaveDefaults.race = globals.randomfromarray(globals.allracesarray) replace line 643 with: player.spec = globals.randomfromarray(playerSpecializationArray) replace lines 639-64 with: player.sex = globals.randomfromarray(sexArray) player.age = globals.randomfromarray(ageArray) replace line 631 with: player.race = globals.randomfromarray(globals.allracesarray) use dynamic file paths, create save file entries for unregistered files, cleaned up some code changed text for current load file to include the name of the current save folder(v5a) changed initial load file to be "autosave1" as this might actually exist(v5a) changed "filesname" to be given a file path after saveDir has been created(v5a) replace line 366 with get_node("TextureFrame/SavePanel/saveline").set_text(filesname.replace(globals.appDataDir,'')) #Displays name of selected savefile in textbox replace lines 355-359 with: if !globals.savelist.has(i): if globals.saveListNewEntry(i): node.get_node("date").set_text(globals.savelist[i].get('date')) else: node.get_node("date").set_text(globals.savelist[i].get('date')) node.get_node("name").set_text(i.replace(globals.saveDir,'')) replace lines 347-348 with: for i in globals.savelist.duplicate(): if !savefiles.has(i): replace line 343 with: if filesname == null: filesname = globals.saveDir + 'autosave1' get_node("TextureFrame/SavePanel/saveline").set_text(filesname.replace(globals.appDataDir,'')) #Displays name of selected savefile in textbox replace lines 333-334 with: if !dir.dir_exists(globals.saveDir): dir.make_dir_recursive(globals.saveDir) replace line 325 with: var filesname = null fixed globals.player needing to be created during init to prevent accessing null object move line 272 to before line 268: globals.player = player #Necessary for descriptions to properly identify player character during newgame creation use dynamic file paths replace line 163 with: var showWarning = !File.new().file_exists(globals.settingsFile) remove line 161: var settings = File.new() combat.gd fixed Armor Breaker backfire replace line 722 with: if caster.passives.has("armorbreaker"): options.tscn added file error logging add line after 708: [connection signal="toggled" from="TabContainer/Settings/errorLogging" to="." method="_on_errorLogging_toggled"] fixed being able to cancel the confirmation for fullscreen without triggering appropriate functions add line after 468: popup_exclusive = true fix tooltip for "Skip combat animation" replace line 171 with: hint_tooltip = "Speed up combat by making attack animations nearly instantaneous" added file error logging add lines after 99: [node name="errorLogging" type="CheckBox" parent="TabContainer/Settings"] margin_left = 20.0 margin_top = 205.0 margin_right = 264.0 margin_bottom = 232.0 hint_tooltip = "The game will create a log folder and file in the application data folder (near the saves, portraits, and mods), which will record all text that would be printed to the terminal while the game runs. This is especially useful for sudden crashes or any OS that does not automatically open a terminal when the game starts. This option will only go into effect after the game restarts." size_flags_horizontal = 2 size_flags_vertical = 2 text = "Use file error logging" options.tres.gd added fix to new mansion info vanishing after font resize add lines after 221: if globals.main != null: globals.main.on_resize_screen() added toggleable file error logging add lines after 96: func _on_errorLogging_toggled(value): ProjectSettings.set_setting("logging/file_logging/max_log_files", 1) ProjectSettings.set_setting("logging/file_logging/enable_file_logging", value) ProjectSettings.save() add line after 61: get_node("TabContainer/Settings/errorLogging").set_pressed( ProjectSettings.get_setting("logging/file_logging/enable_file_logging")) constructor.gd improved portrait selection performance for large numbers of images by presorting images, added body images when available replace lines 184-196 with: var portraits_by_race = {} func _fill_portraits_by_race(): for full_race in globals.allracesarray: for raceWord in full_race.split(" "): portraits_by_race[raceWord] = [] var extensions = globals.imageExtensions for path in globals.dir_contents(globals.setfolders.portraits): if !path.get_extension() in extensions: continue for raceWord in portraits_by_race: if path.findn(raceWord) >= 0: portraits_by_race[raceWord].append(path) func randomportrait(person): if portraits_by_race.empty(): _fill_portraits_by_race() var count = 0 var raceWords = person.race.split(" ") for raceWord in raceWords: count += portraits_by_race[raceWord].size() if count == 0: return count = randi() % count for raceWord in raceWords: var newCount = count - portraits_by_race[raceWord].size() if newCount < 0: var path = portraits_by_race[raceWord][count] person.imageportait = path path = path.replace(globals.setfolders.portraits, globals.setfolders.fullbody) if globals.loadimage(path) != null: person.imagefull = path return else: count = newCount variables.gd added new variables and list entries for new variables fixed format of gradepricemod definition to be compatible with mod system add line after 143: ropewearoutchance = {descript = "Percent chance of rope wearing out when used", min = 0.0, max = 100.0}, add line after 110: bonustimeperslavefororgy = {descript = "Bonus number of actions gained for each slave in an sex orgy interaction sequence", min = 1.0, max = 1000.0}, add line after 109: dailyactionsperslave = {descript = "Max number of interactions with a slave in a day", min = 1.0, max = 1000.0}, replace lines 74-77 with: # grade and age mods will be added as bonus to base price which starts at 1 [baseprice*(1+value)] var gradepricemod = { "slave": -0.2, poor = 0.0, commoner = 0.2, rich = 0.5, noble = 1.0 } add line after 22: var dailyactionsperslave = 2.0 add line after 12: var ropewearoutchance = 20.0 add line after 10: var bonustimeperslavefororgy = 10.0 modpanel.gd reorganized the functions in the file, though this made reporting exact line changes impossible fixed being able to duplicate mod installs by not exiting game after applying mods added save folder ID for having separate folders for mod specific saves improved clarity of mod log improved performance of mod system by creating regexs once changed variable regex to better handle multiple line definitions changed definition tag regex to prevent confusion with file tags added support for file tagging and patching extended the backup system to handle patching of any file type added line edit tracking and position correction to help enable multiple mods to change the same definition added inter-mod conflict reporting use dynamic file names fixed improper file paths after manually choosing a path changed the config file location to "res://details.ini" and changed the data stored to handle the mod lists, backup version and files, and save file ID the state of the GUI is no longer stored to the config file after every little button press(only when backup or mods are actually changed, or the mod menu is closed) simplified some code and renamed many variables to be consistent and descriptive fixed issue of improper creation of save directory path when game loads changed creation of saveDir to slightly increase flexibility(v5a) fixed over-agressive reduction of '\n' that resulted in losing existing '\n'(v5a) fixed AddTo tagged single-line variables exactly at end of file(v5a) fixed variable regex confusing single-line array or dictionary defs with multi-line def(v5a) outside.gd added reusable rope, simplified some code (v5a) add line after 2410: playergrouppanel() replace lines 2402-2408 with: var array = globals.state.capturedgroup globals.state.capturedgroup = [] for i in array: gold += i.sellprice()/2 main.popup('You furtively delivered your captives to the local slaver guild. This earned you [color=yellow]' + str(gold) + '[/color] gold. ') globals.state.backpack.stackables.rope = globals.state.backpack.stackables.get('rope', 0) + globals.state.calcRecoverRope(array.size()) add line after 2390: playergrouppanel() add line after 2389: globals.state.backpack.stackables.rope = globals.state.backpack.stackables.get('rope', 0) + globals.state.calcRecoverRope(1) changed URLs to use slave id rather than slave list position, add away check to brothel's worker listing replace lines 2098-2104 with: if person.away.duration == 0 && (person.work == 'whorewimborn' || person.work == 'escortwimborn' || person.work == 'fucktoywimborn'): text = text + person.dictionary('\nYou can see [url=id' + str(person.id) + '][color=yellow]$name[/color][/url] waiting for clients here.') fixed RNG indexing bug (v5a) replace line 2007 with: globals.state.sebastianslave = globals.newslave( globals.state.sebastianorder.race, 'random', 'random', globals.randomfromarray(caste)) fixed slaves bought from sebastian being put in jail even if there was no space (v5a) replace line 1987 with: if globals.count_sleepers().jail < globals.state.mansionupgrades.jailcapacity: person.sleep = 'jail' added tab formatting to multi-line variable definitions add tab before lines 1782-1786 add tabs before lines 1658-1668, split lines to improve readability changed requirement for an obedient slave to be 80 from 90 for consistency replace line 1481 with: reqs = "person.obed >= 80 && person.race == 'Human' && person.beauty >= 40 && person.sex == 'female'" added tab formatting to multi-line variable definitions add tabs before lines 1223-1285 add tab before lines 962-967 changed iterator name for clarity replace lines 905-906 with: for j in temp: ref = ref[j] changed requirement for an obedient slave to be 80 from 90 for consistency replace line 701 with: if selectedslave.fromguild == false && (selectedslave.obed < 80 || selectedslave.effects.has('captured') == true): replace line 695 with: if selectedslave.obed >= 80 && selectedslave.fromguild == false && selectedslave.effects.has('captured') == false: replace line 648 with: elif !location in ['wimborn','gorn','frostford'] || (selectedslave.obed >= 80 && selectedslave.fromguild == false && selectedslave.effects.has('captured') == false): fixed slave pricing inconsistency for negative reputation (v5a) replace line 572 with: price *= (10 - globals.state.reputation[location]) / 20.0 fixed RNG indexing bug (v5a) replace lines 351-352 with: origin = globals.randomfromarray(originpool) race = globals.randomfromarray(globals.allracesarray) replace line 348 with: racearray = [[globals.randomfromarray(globals.allracesarray),1]] fixed numeric labels for buttons added with the addbutton function replace line 101 with: var count = 1 for node in buttoncontainer.get_children(): if node.visible: count += 1 newbutton.get_node("Label").set_text(str(count)) simplified node path used for prior fix replace lines 40-41 with: get_node("playergroupdetails/Panel/TabContainer/Items/_v_scroll").visible = false get_node("playergroupdetails/Panel/TabContainer/Spells/_v_scroll").visible = false dating.gd added drunk effect and text indicator add lines after 1042: elif drunkness > max(0, person.send) + 1 && !person.effects.has('drunk'): person.add_effect(globals.effectdict.drunk) $end/RichTextLabel.bbcode_text += decoder("\n\n[color=yellow][name2] drunkenly sways in place and slightly slurs [his2] words. [/color]") rename drunkness function to not overlap with druckness variable replace line 1037 with: func checkPassOut(): added function to check same-sex interactions similar to sex interactions add lines after 1036: func checkAcceptSexPairing(person): if person.traits.has("Bisexual"): return true if person.sex == globals.player.sex || (person.sex in ['female', 'futanari'] && globals.player.sex in ['female', 'futanari']): return person.traits.has('Homosexual') return true fixed slaves being affected by alcohol despite refusing to drink remove lines 993-995: if person.traits.has("Alcohol Intolerance"): drunkness += 1 add lines after 988: if person.traits.has("Alcohol Intolerance"): drunkness += 2 else: remove line 985: drunkness += 1 changed wine action conditions to allow it to be used on non-obedient slaves so long as their mood bar is at least halfway filled replace line 978 with: if self.mood < 23 && person.obed < 80: fixed fractional or negative learning points from teach action replace line 908 with: person.learningpoints += max(0, round(value)) changed propose action conditions for consent to emphasize obedience rather than mood raised difficulty of getting consent significantly for recently captured slaves, unwanted sex pairings, incest, and Prude trait increased effectiveness of alcohol for acquiring consent slightly inverted value of difficulty variable to match expectations, fixed redundant code fixed text being split between the main panel and the popup by putting it all in the latter remove 2 tabs from line 752: return text replace line 750 with: globals.state.sexactions += 1 showsexswitch(text, mode) text = '' remove lines 743-744: globals.state.sexactions += 1 showsexswitch(text, mode) add line after 741: person.consent = true remove line 739: return text replace lines 728-736 with: var difficulty = 300 - (self.mood + person.obed*3 + person.loyal*2 + person.lust + drunkness*10) if person.effects.has('captured'): difficulty += 100 if !checkAcceptSexPairing(person): difficulty += 60 if globals.state.relativesdata.has(person.id) && (int(globals.state.relativesdata[person.id].father) == int(globals.player.id) || int(globals.state.relativesdata[person.id].mother) == int(globals.player.id)): difficulty += 60 if person.traits.has('Prude'): difficulty +=30 if difficulty >= 0: replace line 725 with: text = '' changed pushdown action conditions to be similar to resisting in sex interaction added consent as an easy pass condition similar to the propose action fixed text being duplicated in the main panel and the popup by putting it all in the latter replace lines 701-715 with: if person.consent: text += "[he2] is briefly overwhelmed, but looks at you eagerly. " self.mood += 6 person.lust += 6 mode = 'sex' else: var difficulty = 400 if person.effects.has('captured'): difficulty += 100 if person.effects.has('drunk'): difficulty /= 2 difficulty -= person.obed*3 + person.loyal*2 + person.lust - person.lewdness/3 if person.traits.has("Likes it rough"): difficulty -= 60 if difficulty > 0 && !person.traits.has('Sex-crazed'): self.mood -= 10 text += "[he2] resists and pushes you back. " mode = 'abuse' else: text += "[he2] closes eyes and silently accepts you. " self.mood += 3 person.lust += 3 mode = 'rapeconsent' showsexswitch(text,mode) return '' use new function to check same-sex interactions replace lines 685-690 with: if checkAcceptSexPairing(person): self.mood += 3 person.lust += 3 else: self.mood += 1 person.lust += 1 use new name for drunkness function replace line 557 with: checkPassOut() inverted disablereqs so that the conditions are true when action is disabled because it makes more sense replace line 483 with: if i.has('disablereqs') && evaluate(i.disablereqs): use new person interaction recording replace line 340 with: person.recordInteraction() changed it so that the drunk effect will cause slaves start meet interactions with drunkness halfway to passing out replace line 327 with: if tempperson.effects.has('drunk'): self.drunkness = max(0, tempperson.send) else: self.drunkness = 0 inverted disablereqs so that the conditions are true when action is disabled because it makes more sense replace line 276 with: disablereqs = 'globals.itemdict.supply.amount < 2', replace line 268 with: disablereqs = 'globals.itemdict.supply.amount < 1', replace line 259 with: disablereqs = 'globals.resources.gold < 5', replace line 327 with: disablereqs = 'globals.resources.gold < 10', replace line 327 with: added disablereqs to pushdown and propose actions for interaction limits, updated descriptions add line after 149: disablereqs = "!person.canInteract()", replace line 147 with: descript = "Ask $name if they would like to be intimate. \n[color=yellow]Requires that the slave can do 1 more interaction.[/color]", add line after 142: disablereqs = "!person.canInteract()", replace line 140: descript = "Force yourself on $name. \n[color=yellow]Requires that you can interact with the slave at least 1 more time today.[/color]", inverted disablereqs so that the conditions are true when action is disabled because it makes more sense replace line 93 with: disablereqs = "person.traits.has('Mute')" replace line 85 with: disablereqs = "person.traits.has('Mute')" date.tscn increased size of text box at end of date to decease need for scrollbar to appear replace lines 485 and 450 with: margin_bottom = 411.0 exploration.gd fixed Zoe's gallery not being unlocked add line after 1785: globals.charactergallery.zoe.unlocked = true simplified some code replace lines 1051-1058 with: for place in ['wimborn','frostford','gorn','amberguard']: if currentzone.tags.has(place): location = place changed it so that uncivilized races do not lose their tamer specialization after being captured remove lines 1044-1047: if globals.races[person.race.replace("Halfkin", "Beastkin")].uncivilized == true: person.add_trait('Uncivilized') if person.spec == 'tamer': person.spec = null added reusable rope and simplified some code added color coding to labels for defeated persons to improve readability replace line 1040 with: if variables.consumerope > 0: replace lines 1019-1031 with: newbutton.get_node("capture").connect("pressed",self,'captureslave', [person]) if globals.state.backpack.stackables.get('rope', 0) < variables.consumerope: newbutton.get_node('capture').set_disabled(true) newbutton.get_node("Label").set_text(defeated.names[i] + ' ' + person.sex+ ' ' + person.race) newbutton.connect("pressed", self, 'defeatedselected', [person]) newbutton.connect("mouse_entered", globals, 'slavetooltip', [person]) newbutton.connect("mouse_exited", globals, 'slavetooltiphide') newbutton.get_node("choice").set_meta('person', person) newbutton.get_node("mindread").connect("pressed",self,'mindreadslave', [person]) if globals.resources.mana < globals.spells.spellcost(globals.spelldict.mindread) || !globals.spelldict.mindread.learned: newbutton.get_node('mindread').set_disabled(true) newbutton.get_node("choice").add_to_group('winoption') newbutton.get_node("choice").connect("item_selected",self, 'defeatedchoice', [person, newbutton.get_node("choice")]) add new line after 1015: var person = defeated.units[i] replace line 1009 with: text += str(globals.state.backpack.stackables.get('rope', 0)) changed it so that uncivilized races do not lose their tamer specialization after being captured, simplified some code replace lines 993-998 with: var person = defeated.units[i] if globals.races[person.race.replace("Halfkin", "Beastkin")].uncivilized && person.spec != 'tamer': person.add_trait('Uncivilized') person.stress += rand_range(20, 50) person.obed += rand_range(10, 20) person.health -= rand_range(40,70) if defeated.names[i] == 'Captured': person.obed += rand_range(10,20) person.loyal += rand_range(5,15) fixed bashing open a chest when less than enough strength having roll based on agility rather than strength replace line 610 with: if 60 - (chest.strength - person.sstr) * 10 >= rand_range(0,100): slightly extended early game enemies lacking armor (v5a) replace line 347 with: if k == 'armor' && rand_range(1, 4) < globals.player.level: fixed RNG indexing bug (v5a) replace line 314 with: race = globals.randomfromarray(globals.allracesarray) newsexsystem.gd removed many excessive whitespace lines throughout the code changed resistance conditions with result being slightly easier and less likely to conflict with getting consent from meet interaction moved calculation of resistwill to a seperate function to be used in more places instead of only using obedience most of the time changed physical resistance to only display text if person is resisting, added handcuff debuff of physical resistance (v5a) replace lines 2169-2197 with: if calcResistWill(member) > 0: var resiststrength = member.person.sstr + 1 var subdue = 0 if member.effects.has('tied'): resiststrength = 0 result.text += '[name1] is powerless to resist, as [his1] limbs are restricted by rope.\n' elif member.effects.has('handcuffed'): resiststrength = ceiling(resiststrength * 2.0 / 3.0) - 1 result.text += '[name1] has some difficulty resisting, as [his1] arms are restricted by handcuffs.\n' for i in member.subduedby: subdue += i.person.sstr + 1 add lines after 2166: func calcResistWill(member): if member.person.traits.has('Sex-crazed'): return 0 var resistwill = 200 if !member.consent: resistwill += 200 if member.person.effects.has('captured'): resistwill += 100 if member.person.effects.has('drunk'): resistwill /= 2 resistwill -= member.person.obed*3 + member.person.loyal*2 + member.lust/10 - member.lewd/3 if member.person.traits.has("Likes it rough"): resistwill -= 60 return resistwill simplified some code, fixed slave lead sex doing nothing(v5a) replace lines 2085-2098 with: if (chosenaction.code in takercategories) == (dom == 'taker'): givers = groupchosen.duplicate() takers = grouptarget.duplicate() else: takers = groupchosen.duplicate() givers = grouptarget.duplicate() replace line 2081 with: if actions.empty(): replace line 2079 with: if value > 0: rebalanced AI weighting of actions to not skip some actions for not having a strapon despite not being needed made fucking more likely later on rather than less likely remove lines 2075-2076: if j.code in ['tribadism','doubledildo','doubledildoass','frottage'] && (chosen.strapon != null || target.strapon != null): value = 0 replace lines 2071-2073 with: value += max(15 - turns, 0) if chosen.lube < 5: value -= (5 - chosen.lube)*2 simplified some code, changed submission to obedience to respond to changes to the person, fixed slave lead sex doing nothing(v5a) replace line 2058 with: if target.person.obed < 80 && j.code in punishcategories && chosenpos == 'giver': replace lines 2019-2033 with: if (j.code in takercategories) == (dom == 'taker'): givers = groupchosen.duplicate() takers = grouptarget.duplicate() else: takers = groupchosen.duplicate() givers = grouptarget.duplicate() var result = checkaction(j) changed mana formula to evenly attribute mana to all slaves(not emphasize those higher on the list) and not have a hard cutoff of 150 mana(especially for large groups) thus the mana gained has increased slightly for small groups and significantly for larger groups(i.e., orgies) remove lines 1899-1900: func mformula(gain, mana): return mana + gain * max(0, mana/(mana-300)+1) replace lines 1885-1889 with: mana = round(mana) manaDict[i.person] = mana totalmana += mana text += "\n" var manaScaling = 1.0 - 0.9 * totalmana / (500.0 + totalmana) totalmana = 0 for person in manaDict: mana = round(manaScaling * manaDict[person]) totalmana += mana person.metrics.manaearn += mana replace line 1877 with: mana *= 1.2 replace line 1875 with: mana += i.sens/500 replace lines 1870-1973 with: var essence = i.person.getessence() if essence != null && i.person.smaf*20 > rand_range(0,100): text += ", Ingredient gained: [color=yellow]" + globals.itemdict[essence].name + "[/color]" globals.itemdict[essence].amount += 1 mana += i.orgasms*3 + rand_range(1,2) add line after 1838: var manaDict = {} fixed RNG indexing bug (v5a) replace line 1831 with: askslaveforaction(globals.randomfromarray(ai)) simplified some code replace lines 1811-1817 with: elif action.scene.code == 'subdue': for taker in action.takers: for giver in action.givers: giver.subduing = null added reusable rope add line after 1810: globals.itemdict['rope'].amount += globals.state.calcRecoverRope(action.takers.size(), 'sex') replace line 1808 with: elif action.scene.code == 'rope': added cancelation of deepthroat if ringgag is removed and taker is resisting add lines after 1807: elif action.scene.code == 'ringgag': var isResist = false for t in action.takers: if !t.effects.has('resist'): continue for a in t.activeactions: if a.scene.code == 'deepthroat': stopongoingaction(a) break simplified some code replace lines 1798-1807 with: if !action.scene.giverpart.empty(): for i in action.givers: i[action.scene.giverpart] = null if !action.scene.takerpart.empty(): for i in action.takers: i[action.scene.takerpart] = null if action.scene.get("takerpart2") != null && !action.scene.get("takerpart2").empty(): for i in action.takers: i[action.scene.takerpart2] = null if action.scene.code == 'strapon' && action.givers[0].penis != null: stopongoingaction(action.givers[0].penis) fixed same sex formula to use 'futanari' instead of 'futa', simplified the code replace lines 1769-1782 with: if givers.empty() || takers.empty(): return false var giverssex = givers[0].sex var takerssex = takers[0].sex if givers.has(actor): return actor.sex == takerssex || (actor.sex in ['female','futanari'] && takerssex in ['female','futanari']) elif takers.has(actor): return actor.sex == giverssex || (actor.sex in ['female','futanari'] && giverssex in ['female','futanari']) return false commented out unused code add # to start of lines 1598-1759: #func orgasm(member): fixed incorrect check for lack of consent replace line 1561 with: if i.effects.has('forced') || i.effects.has('resist'): commented out unused code add # to start of lines 1457-1470: #func triggerorgasm(i): added tab formatting to multi-line variable definitions add tab before lines 1434-1452 fixed error in generating text when action has no takers(strapon), fixed logic(v5a) replace lines 1420-1431 with: if text != null && partner != null && text.find('[name2]') >= 0: if partner.person == globals.player || character.person.traits.has("Monogamous"): text = text.replace('[name2]', character.person.getMasterNoun()) else: text = text.replace('[name2]', partner.name) text = '[color=lime]' + text + '[/color]' else: text = '' return {'text' : text, 'character' : character, 'partner' : partner} replace line 1399 with: if partner != null && (!character.person.traits.has('Homosexual') && !character.person.traits.has("Bisexual")) && character.sex != 'male' && partner.sex != 'male' && partnerside == 'givers': replace line 1363 with: if !scene[partnerside].empty(): partner = scene[partnerside][randi()%scene[partnerside].size()] added check for consent for desires replace line 1332 with: if i.person == globals.player || i.person.unique in ['dog','horse'] || i.effects.has('forced') || i.effects.has('resist'): moved action conflict checks to a separate function, changed conflict checks to be more precise and prevent blatant duplication of actions changed tool actions to always be ongoing actions, since it doesn't make sense to use them for a single turn simplified some code fixed indexing bug related to idx1(v5a) remove lines 1180-1216: if scenescript.code in ['strapon', 'rope', 'subdue']: replace lines 1144-1163 with: var dict = {'scene' : scenescript, 'takers' : takers.duplicate(), 'givers' : givers.duplicate()} if scenescript.code in ['strapon', 'nippleclap', 'clitclap', 'ringgag', 'blindfold', 'nosehook', 'vibrator', 'analvibrator', 'rope', 'milker', 'subdue', 'relaxinginsense']: cont = true var conflicts = getConflictsWithOngoing(scenescript) for c in conflicts: stopongoingaction(c.action) if scenescript.giverpart != '': for i in givers: #print(i.name + " " + str(i[scenescript.giverpart]) + str(scenescript.giverpart)) i[scenescript.giverpart] = dict if scenescript.takerpart != '': for i in takers: i[scenescript.takerpart] = dict if scenescript.get('takerpart2'): for i in takers: add lines after 1067: func getIntersection(array1, array2): var intersection = [] for i in array1: if array2.has(i): intersection.append(i) return intersection func getConflictsWithOngoing(scenescript): var conflicts = [] var set1 var set2 if !scenescript.giverpart.empty(): for i in givers: if i[scenescript.giverpart] != null: conflicts.append({'action': i[scenescript.giverpart], 'givers': [i], 'takers': []}) if !scenescript.takerpart.empty(): for i in takers: if i[scenescript.takerpart] != null: conflicts.append({'action': i[scenescript.takerpart], 'givers': [], 'takers': [i]}) if scenescript.get('takerpart2') != null && !scenescript.takerpart2.empty(): for i in takers: if i[scenescript.takerpart2] != null: conflicts.append({'action': i[scenescript.takerpart2], 'givers': [], 'takers': [i]}) #handle action conflict not covered by part overlaps if scenescript.giverpart.empty() && scenescript.takerpart.empty(): for i in ongoingactions: if scenescript.code == i.scene.code: set1 = getIntersection(i.givers, givers) set2 = getIntersection(i.takers, takers) if !set1.empty() && !set2.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': set2}) if scenescript.code in ['cunnilingus','rimjob']: for i in ongoingactions: if i.scene.category == 'fucking' && i.scene.code != 'strapon': set1 = getIntersection(i.givers, takers) if !set1.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': []}) elif scenescript.code in ['massagefoot','lickfeet']: for i in ongoingactions: if i.scene.category == 'fucking' && i.scene.code != 'strapon': set1 = getIntersection(i.givers, givers) if !set1.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': []}) elif scenescript.code in ['doubledildo', 'doubledildoass', 'tribadism']: for i in ongoingactions: if i.scene.category in ['caress', 'fucking'] && i.scene.code != 'strapon': set1 = getIntersection(i.givers, givers + takers) if !set1.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': []}) elif scenescript.code == 'grovel': for i in ongoingactions: if i.scene.code in ['facesit','afacesit']: set2 = getIntersection(i.takers, takers) if !set2.empty(): conflicts.append({'action': i, 'givers': [], 'takers': set2}) elif i.scene.category == 'fucking' && i.scene.code != 'strapon': set1 = getIntersection(i.givers, takers) if !set1.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': []}) elif scenescript.code in ['facesit','afacesit']: for i in ongoingactions: if i.scene.code == 'grovel': set2 = getIntersection(i.takers, takers) if !set2.empty(): conflicts.append({'action': i, 'givers': [], 'takers': set2}) elif i.scene.category == 'fucking' && i.scene.code != 'strapon': set1 = getIntersection(i.givers, givers + takers) set2 = getIntersection(i.takers, givers) if !set1.empty() || !set2.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': set2}) elif scenescript.code == 'rope': for i in takers: for k in i.activeactions: if k.scene.code == 'subdue': conflicts.append({'action': k, 'givers': [], 'takers': [i]}) elif scenescript.category == 'caress': for i in ongoingactions: if i.scene.code in ['doubledildo', 'doubledildoass', 'tribadism']: set1 = getIntersection(i.givers, givers) set2 = getIntersection(i.takers, givers) if !set1.empty() || !set2.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': set2}) elif scenescript.category == 'fucking' && scenescript.code != 'strapon': for i in ongoingactions: if i.scene.code in ['cunnilingus','rimjob']: set2 = getIntersection(i.takers, givers) if !set2.empty(): conflicts.append({'action': i, 'givers': [], 'takers': set2}) elif i.scene.code in ['massagefoot','lickfeet']: set1 = getIntersection(i.givers, givers) if !set1.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': []}) elif i.scene.code in ['doubledildo', 'doubledildoass', 'tribadism']: set1 = getIntersection(i.givers, givers) set2 = getIntersection(i.takers, givers) if !set1.empty() || !set2.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': set2}) elif i.scene.code == 'grovel': set2 = getIntersection(i.takers, givers) if !set2.empty(): conflicts.append({'action': i, 'givers': [], 'takers': set2}) elif i.scene.code in ['facesit','afacesit']: set1 = getIntersection(i.givers, givers + takers) set2 = getIntersection(i.takers, givers) if !set1.empty() || !set2.empty(): conflicts.append({'action': i, 'givers': set1, 'takers': set2}) #reduce redundancy by not conflicting with same action twice, though it will still list multiple actions with same name var idx1 = conflicts.size() - 1 var idx2 while idx1 > 0: idx2 = idx1 - 1 while idx2 >= 0: if conflicts[idx1].action == conflicts[idx2].action: conflicts[idx2].givers += conflicts[idx1].givers conflicts[idx2].takers += conflicts[idx1].takers conflicts.remove(idx1) break idx2 -= 1 idx1 -= 1 return conflicts updated giver/taker button function to simply take a member reference replace lines 1054-1065 with: func switchsides(member, side): givers.erase(member) takers.erase(member) if member.role == side: member.role = 'none' else: member.role = side if member.role == 'give': givers.append(member) elif member.role == 'take': takers.append(member) simplified some code changed action participation to no longer be limited by obedience alone, updated tooltips changed "doubledildo" actions to no longer hide all actions in the 'carress' and 'fucking' categories changed resisting participants to display most actions as disabled unless an attempt to immobilize them is made changed deepthroat to be more visible but only be enabled when participants are not resisting or have a ringgag remove tab from line 1048: $Panel/bodyimage.visible = true remove line 1045: $Panel/bodyimage.visible = true replace lines 987-1029 with: func checkaction(action): action.givers = givers action.takers = takers if action.requirements() == false || filter.has(action.code): return ['false'] # elif doubledildocheck() && action.category in ['caress','fucking'] && !action.code in ['doubledildo','doubledildoass','tribadism','frottage']: # return ['false'] if action.category in ['SM','tools','humiliation']: for k in givers+takers: if k.limbs == false: return ['false'] var disabled = false var hint_tooltip = '' for k in givers: if k.person == globals.player: continue if action.giverconsent != 'any' && k.effects.has('resist'): disabled = true hint_tooltip = k.person.dictionary("$name refuses to perform this action (high resistance: low obedience, loyalty, or lust)") elif action.giverconsent == 'advanced' && k.lewd < 50: disabled = true hint_tooltip = k.person.dictionary("$name refuses to perform this action (low lewdness)") for k in takers: if k.person == globals.player: continue if action.takerconsent == 'any' && k.effects.has('resist') && action.code != 'subdue': if k.subduedby.empty() && !k.effects.has('tied') || (action.code == 'deepthroat' && k.acc1 == null): hint_tooltip = k.person.dictionary("$name refuses to perform this action (high resistance: low obedience, loyalty, or lust)") disabled = true else: hint_tooltip = k.person.dictionary("$name refuses to perform this action, but is being restrained") elif action.takerconsent != 'any' && k.effects.has('resist'): disabled = true hint_tooltip = k.person.dictionary("$name refuses to perform this action (high resistance: low obedience, loyalty, or lust)") elif action.takerconsent == 'advanced' && k.lewd < 50: disabled = true hint_tooltip = k.person.dictionary("$name refuses to perform this action (low lewdness)") if disabled: return ['disabled',hint_tooltip] else: return ['allowed',hint_tooltip] replace lines 967-985 with: var doubledildo = false var givercheck = false var takercheck = false for scene in ongoingactions: if scene.scene.code in ['doubledildo','doubledildoass','tribadism','frottage']: for i in givers: if scene.givers.has(i) || scene.takers.has(i): givercheck = true for i in takers: if scene.givers.has(i) || scene.takers.has(i): takercheck = true if givercheck && takercheck: doubledildo = true break else: givercheck = false takercheck = false return doubledildo changed conditions for slaves to lead sex to be more clear by effecting the state of the button (previously it changed a textbox that was not visible) fixed odd behavior of giver/taker buttons after activating AI (v5a) replace line 959-960 with: ai.append(i) add lines after 955: for member in participants: member.role = 'none' remove lines 949-954: for i in givers: if i.submission < 90 || i.consent == false: $Control/Panel/RichTextLabel.bbcode_text = i.person.dictionary('$name refuses to participate. ') return elif i.effects.has('tied') || i.subduedby.size() > 0: $Control/Panel/RichTextLabel.bbcode_text = i.person.dictionary("$name is immobile and can't do anything. ") simplified code replace lines 863-866 with: var rval = requests.keys() added tab formatting to multi-line variable definitions add tab before lines 849-858 simplified code replace lines 834-846 with: var cmpCat = categoriesorder.find(first.category) - categoriesorder.find(second.category) if cmpCat == 0: if first.get('order') == null: return false if second.get('order') == null: return true return first.order < second.order return cmpCat < 0 replace lines 811-814 with: get_node("Panel/passbutton").set_disabled( givers.empty() && selectmode != 'ai' ) replace lines 796-805 with: var text = '' if givers.empty(): text += '[...] ' else: for i in givers: text += '[color=yellow]' + i.name + '[/color], ' text += 'will do it ... to ' if takers.empty(): text += "[...]" else: for i in takers: text += '[color=aqua]' + i.name + '[/color], ' remove extra tabbing from lines 789-793 changed conditions for slaves to lead sex to be more clear by effecting the state of the button (previously it changed a textbox that was not visible), simplified code replace lines 773-782 with: if selectmode != 'ai': var noPlayerFound = true for member in givers: if member.person == globals.player: noPlayerFound = false break if !givers.empty() && noPlayerFound: newnode = get_node("Panel/GridContainer2/GridContainer/Button").duplicate() get_node("Panel/GridContainer2/GridContainer").add_child(newnode) newnode.visible = true if givers.size() == 1: newnode.set_text(givers[0].person.dictionary("Let $name Lead")) else: newnode.set_text("Let Actors Lead") newnode.connect("pressed",self,'activateai') for i in givers: if i.effects.has('resist') || i.effects.has('forced'): newnode.hint_tooltip = i.person.dictionary('$name refuses to participate. ') newnode.disabled = true break if i.effects.has('tied') || !i.subduedby.empty(): newnode.hint_tooltip = i.person.dictionary("$name is immobile and can't do anything. ") newnode.disabled = true break else: improved check for ongoing action buttons to appear pressed by including re-arrangements of participants replace line 771 with: if j.scene.code != i.code: continue if j.givers.size() != i.givers.size() || j.takers.size() != i.takers.size(): continue if getIntersection(j.givers, i.givers).size() != j.givers.size(): continue if getIntersection(j.takers, i.takers).size() == j.takers.size(): changed action button tooltip generation to include conflicting actions and resisting but immobilized participants added tooltip for number of rope available (v5a) replace line 764 with: else: var conflicts = getConflictsWithOngoing(i) if !conflicts.empty(): tooltip += '\nConflicts:' for idx in range(conflicts.size()): if idx % 3 == 0: tooltip += '\n ' tooltip += conflicts[idx].action.scene.getname() + ', ' tooltip = tooltip.substr(0, tooltip.length() - 2) newnode.hint_tooltip = tooltip replace line 761 with: var tooltip = i.getname() if result.size() == 2 && !result[1].empty(): tooltip += ' - ' + result[1] if i.code == 'rope': tooltip += '\nFree Ropes left: ' + str(globals.state.getCountStackableItem('rope')) simplified code replace lines 746-750 with: if actionreplacetext.empty(): for i in actionarray: var result = checkaction(i) remove line 740: showactions = false replace line 739 with: elif i.subduing != null && ((takers.size() == 1 && takers[0] != i.subduing) || takers.size() > 1 ): remove lines 737, 734: showactions = false replace line 733 with: elif !i.subduedby.empty(): remove line 731: showactions = false remove line 726: var showactions = true remove line 715: var doubledildo = doubledildocheck() remove line 712: var text = '' updated giver/taker button function to simply take a member reference, changed code to match new GUI structure of giver/taker lists replace lines 695-710 with: newnode = get_node("Panel/givetakepanel/ScrollContainer/VList/ControlLine").duplicate() var giveNode = newnode.get_node("ButtonGiver") var takeNode = newnode.get_node("ButtonReceiver") giveNode.set_pressed(givers.has(i)) takeNode.set_pressed(takers.has(i)) giveNode.text = i.person.name_short() takeNode.text = i.person.name_short() giveNode.connect("pressed",self,'switchsides',[i, 'give']) takeNode.connect("pressed",self,'switchsides',[i, 'take']) newnode.visible = true get_node("Panel/givetakepanel/ScrollContainer/VList").add_child(newnode) replace lines 666-668 with: for i in get_node("Panel/ScrollContainer/VBoxContainer").get_children() + get_node("Panel/GridContainer/GridContainer").get_children() + get_node("Panel/givetakepanel/ScrollContainer/VList").get_children() + $Panel/GridContainer2/GridContainer.get_children(): if !i.get_name() in ['Panel', 'Button', 'ControlLine']: i.hide() simplify some code replace lines 657-658 with: i.set_pressed( i.get_name() == name ) added bonus time to orgies that scales with the number of slaves participating add lines after 642: if actors.size() > 4: turns += variables.bonustimeperslavefororgy * actors.size() simplify some code, moved member initialization into member class, use new person interaction recording replace lines 629-632 with: secondactorcounter[i] = secondactorcounter.get(i, 0) + 1 replace lines 594-623 with: person.sexexp.watchers[i.id] = person.sexexp.watchers.get(i.id, 0) + 1 person.recordInteraction() person.metrics.sex += 1 participants.append( member.new(person, self) ) replace lines 566-583 with: var newmember = member.new(person, self) newmember.number = dummycounter dummycounter += 1 replace lines 552-561 with: person.lewdness = 70 person.mods['hollownipples'] = 'hollownipples' #person.sex = 'male' if type == 'resist': person.obed = 0 person.consent = false else: person.obed = 90 person.consent = true fixed RNG indexing bug (v5a) replace line 551 with: var person = globals.newslave( globals.randomfromarray(globals.allracesarray), 'random', 'random') changed code to match new GUI structure of giver/taker lists replace lines 542-544 with: $Panel/givetakepanel/ScrollContainer/VList.get_child(key).get_node("ButtonGiver").emit_signal("pressed") else: $Panel/givetakepanel/ScrollContainer/VList.get_child(key).get_node("ButtonReceiver").emit_signal("pressed") remove redundant comments remove lines 497-516: # var person = globals.newslave(globals.allracesarray[rand_range(0,globals.allracesarray.size())], 'random', 'random') simplify some code, moved member initialization into member class, prevent animals from having slave body images fixed RNG indexing bug (v5a) replace lines 469-476 with: participants.append( member.new(person, self, true) ) add line after 466: person.imagefull = null remove lines 456-457: var newmember = member.new() newmember.sceneref = self replace line 455 with: var person = globals.newslave( globals.randomfromarray(globals.allracesarray), 'adult', 'male') replace lines 445-452 with: participants.append( member.new(person, self, true) ) add line after 442: person.imagefull = null remove kubes 433-434: var newmember = member.new() newmember.sceneref = self replace line 432 with: var person = globals.newslave( globals.randomfromarray(globals.allracesarray), 'adult', 'male') update reformation via punishment to use resistwill rather than obedience replace lines 419-428 with: if values.get('obed', 0) > 0 && effects.has('resist') && sceneref.calcResistWill(self) < 0 && person != globals.player: var text = '' text += "\n[color=green]Afterward, {^[name2] seems to have:it looks as though [name2] [has2]} {^learned [his2] lesson:reformed [his2] rebellious ways:surrendered} and shows {^complete:total} {^submission:obedience:compliance}" if person.traits.find("Masochist") >= 0: text += ", but there is also {^an unusual:a strange} {^flash:hint:look} of desire in [his2] eyes" text += '. [/color]' #yield(sceneref.get_tree().create_timer(0.1), "timeout") effects.erase('resist') sceneref.get_node("Panel/sceneeffects").bbcode_text += sceneref.decoder(text, scenedict.givers, scenedict.takers) + '\n' simplified and generalized handling of action effects, reduced stress for bad acceptance pain if person is not resisting replace lines 317-417 with: for key in ['lewd', 'lust', 'sens', 'pain', 'obed', 'stress']: values[key] = float(values.get(key, 0)) lastaction = scenedict if scenedict.scene.code in globals.punishcategories: if scenedict.givers.has(self): person.asser += rand_range(1,2) else: person.asser -= rand_range(1,2) if acceptance == 'good': values.sens *= rand_range(1.1,1.4) values.lust *= 2 if lewd < 50 || scenedict.scene.code in ['doublepen','nipplefuck', 'spitroast', 'spitroastass', 'inserttailv', 'inserttaila','doubledildo','doubledildoass','tailjob','footjob','deepthroat']: lewd += rand_range(1,3) for i in scenedict.givers + scenedict.takers: if i != self: globals.addrelations(person, i.person, rand_range(4,8)) elif acceptance == 'average': values.sens *= 1.1 #values.lust *= 1 if lewd < 50 || scenedict.scene.code in ['doublepen','nipplefuck', 'spitroast', 'spitroastass', 'inserttailv', 'inserttaila','doubledildo','doubledildoass','tailjob','footjob','deepthroat']: lewd += rand_range(1,2) for i in scenedict.givers + scenedict.takers: if i != self: globals.addrelations(person, i.person, rand_range(2,4)) if values.pain > 0.0: person.stress += rand_range(0,2) else: values.sens *= 0.6 values.lust *= 0.3 for i in scenedict.givers + scenedict.takers: if i != self: globals.addrelations(person, i.person, -rand_range(3,5)) if values.pain > 0.0: if effects.has('resist'): person.stress += rand_range(5,10) else: person.stress += rand_range(2,4) if values.has('tags'): if values.tags.has('punish'): if (effects.has('resist') || effects.has('forced')) && (!person.traits.has('Masochist') && !person.traits.has('Likes it rough') && !person.traits.has('Sex-crazed') && person.spec != 'nympho'): for i in scenedict.givers: globals.addrelations(person, i.person, -rand_range(5,10)) if values.stress == 0.0: values.stress = rand_range(3,5) if person.effects.has("captured") && rand_range(0,50) <= values.obed + 5: person.effects.captured.duration -= 1 values.lust /= 4 values.sens /= 4 else: if person.asser < 35 && randf() < 0.1: actionshad.addtraits.append('Likes it rough') if !person.traits.has('Masochist') && !person.traits.has('Sex-crazed') && person.spec != 'nympho': if values.stress == 0.0: values.stress = rand_range(2,4) else: values.stress = 0.0 if values.tags.has('pervert') && !person.traits.has('Pervert'): if person.traits.has('Sex-crazed') || person.spec in ['geisha','nympho']: if lust >= 750 && randf() < 0.2: actionshad.addtraits.append("Pervert") elif acceptance == 'good': if lust >= 750 && randf() < 0.2: actionshad.addtraits.append("Pervert") else: values.stress += rand_range(2,4) else: values.sens /= 1.75 values.stress += rand_range(2,4) if values.tags.has('group'): actionshad.group += 1 self.lewd += values.lewd self.lust += values.lust self.sens += values.sens person.obed += values.obed person.stress += values.stress added error proofing for actions with takerpart2 replace line 240 with: if scene.scene.get('takerpart2') && scene.scene.givers.size() == 2 && scene.scene.givers[1] == self: moved member initialization into member class, changed resistance to be based on resistwill rather than simply obedience added handcuff effect (v5a) add lines after 118: func _init(source, fileref, isAnimal = false): sceneref = fileref person = source name = source.name_short() loyalty = source.loyal submission = source.obed lust = source.lust*10 sens = lust/2 sex = source.sex svagina = source.sensvagina smouth = source.sensmouth spenis = source.senspenis sanus = source.sensanal lewd = source.lewdness consent = source.consent if source.traits.has("Sex-crazed"): effects.append("sexcrazed") if isAnimal: limbs = false elif source != globals.player: if !source.consent: consent = false effects.append('forced') if fileref.calcResistWill(self) > 0: effects.append('resist') for i in person.gear.values(): if i == null: continue var tempitem = globals.state.unstackables.get(i) if tempitem != null && tempitem.code == 'acchandcuffs': effects.append('handcuffed') break added tab formatting to multi-line variable definitions add tab before lines 28-45 interactionpanel.tscn changed GUI structure of giver/taker lists to have a single list with 2 buttons per line, all inside a scroll container replace lines 503-528 with: margin_left = 918.0 margin_right = 1330.0 margin_bottom = 397.0 [node name="ScrollContainer" type="ScrollContainer" parent="Panel/givetakepanel"] margin_left = 16.0 margin_top = 39.0 margin_right = 386.0 margin_bottom = 379.0 scroll_horizontal_enabled = false [node name="VList" type="VBoxContainer" parent="Panel/givetakepanel/ScrollContainer"] margin_right = 380.0 margin_bottom = 39.0 rect_min_size = Vector2( 380, 39 ) [node name="ControlLine" type="Control" parent="Panel/givetakepanel/ScrollContainer/VList"] visible = false margin_right = 380.0 margin_bottom = 39.0 rect_min_size = Vector2( 380, 39 ) [node name="ButtonGiver" type="Button" parent="Panel/givetakepanel/ScrollContainer/VList/ControlLine"] margin_right = 180.0 margin_bottom = 39.0 toggle_mode = true text = "Name" [node name="ButtonReceiver" type="Button" parent="Panel/givetakepanel/ScrollContainer/VList/ControlLine"] margin_left = 200.0 margin_right = 380.0 adjust GUI positions and sizes to better fit the scroll container replace lines 367-369 with: margin_left = 469.0 margin_top = 15.0 margin_right = 509.0 replace line 124 with: margin_left = 511.0 imageselect.gd simplify some code, use dynamic file paths, use convenience function shellOpenFolder, fixed improper file paths after manually choosing a path replace lines 228-238 with: func _on_folderdialogue_dir_selected( path ): path = path.replace(OS.get_user_data_dir(), "user:/") if !path.ends_with("/"): path += "/" if get_node("folderdialogue").get_meta("meta") == 'portrait': globals.setfolders.portraits = path portraitspath = path elif get_node("folderdialogue").get_meta("meta") == 'body': globals.setfolders.fullbody = path bodypath = path buildimagelist() _on_selectfolder_pressed() replace lines 222-226 with: get_node("folderdialogue").set_current_path( ProjectSettings.globalize_path(bodypath)) get_node("folderdialogue").popup() replace lines 213-217 with: get_node("folderdialogue").set_current_path( ProjectSettings.globalize_path(portraitspath)) get_node("folderdialogue").popup() replace lines 194-195 with: globals.shellOpenFolder(globals.appDataDir) replace lines 189-190 with: dir.copy(path, path.replace(path.get_base_dir() + "/", portraitspath)) remove line 146: #warning-ignore:unused_argument replace lines 126-131 with: path = path.replace(globals.setfolders.portraits, globals.setfolders.fullbody) if $assignboth.pressed && globals.loadimage(path) != null: person.imagefull = path elif mode == 'body': person.imagefull = path path = path.replace(globals.setfolders.fullbody, globals.setfolders.portraits) if $assignboth.pressed && globals.loadimage(path) != null: person.imageportait = path replace lines 79-91 with: var filepath = newpath.get_base_dir() var dir = Directory.new() if !dir.dir_exists(filepath): dir.make_dir_recursive(filepath replace line 69 with: node.get_node("Label").set_text(i.get_file()) replace lines 53-62 with: if !dir.dir_exists(currentpath): dir.make_dir_recursive(currentpath) if !dir.dir_exists(thumbnailpath + type): dir.make_dir_recursive(thumbnailpath + type) var extensions = globals.imageExtensions for i in globals.dir_contents(currentpath): if filecheck.file_exists(i) && i.get_extension() in extensions: var node = get_node("ScrollContainer/GridContainer/Button").duplicate() var iconpath = i.get_basename().replace(currentpath, thumbnailpath + type + '/') + ".png" remove lines 42-43: #warning-ignore:unused_variable var array = [] remove line 14: #warning-ignore:return_value_discarded replace line 11 with: var thumbnailpath = globals.appDataDir + "thumbnails/" remove lines 7-8: #warning-ignore:unused_class_variable var bodybuilt = false person.gd simplified some code to provide minor increase in speed replace line 692 with: string = string.replace('$race', race.to_lower()) replace lines 686-689 with: string = string.replace('$master', getMasterNoun()) if sex == 'male': string = string.replace('$penis', globals.fastif(penis == 'none', 'strapon', 'his cock')) string = string.replace('$child', 'boy') string = string.replace('$child', 'son') string = string.replace('$sibling', 'brother') string = string.replace('$sir', 'Sir') else: string = string.replace('$penis', globals.fastif(penis == 'none', 'strapon', 'her cock')) string = string.replace('$child', 'girl') string = string.replace('$child', 'daughter') string = string.replace('$sibling', 'sister') string = string.replace('$sir', "Ma'am") remove lines 678-679: string = string.replace('$penis', globals.fastif(penis == 'none', 'strapon', '$his cock')) string = string.replace('$child', globals.fastif(sex == 'male', 'boy', 'girl')) replace lines 655-667 with: string = string.replace('$sex', sex) if sex == 'male': string = string.replace('$penis', 'strapon' if (penis == 'none') else 'his cock') string = string.replace('$child', 'boy') string = string.replace('$He', 'He') string = string.replace('$he', 'he') string = string.replace('$His', 'His') string = string.replace('$his', 'his') string = string.replace('$him', 'him') string = string.replace('$son', 'son') string = string.replace('$sibling', 'brother') string = string.replace('$parent', 'father') string = string.replace('$sir', 'Sir') else: string = string.replace('$penis', 'strapon' if (penis == 'none') else 'her cock') string = string.replace('$child', 'girl') string = string.replace('$He', 'She') string = string.replace('$he', 'she') string = string.replace('$His', 'Her') string = string.replace('$his', 'her') string = string.replace('$him', 'her') string = string.replace('$son', 'daughter') string = string.replace('$sibling', 'sister') string = string.replace('$parent', 'mother') string = string.replace('$sir', "Ma'am") string = string.replace('$race', race.to_lower()) changed lastinteractionday to support multiple actions per slave per day add lines after 279: #interactions functions added to manage transistion from storing day of last interaction to storing a dictionary func recordInteraction(): if typeof(lastinteractionday) != TYPE_DICTIONARY: lastinteractionday = {'day' : globals.resources.day, 'count' : float(lastinteractionday == globals.resources.day)} elif lastinteractionday.day == globals.resources.day: lastinteractionday.count += 1 else: lastinteractionday = {'day' : globals.resources.day, 'count' : 1} func getRemainingInteractions(): if typeof(lastinteractionday) == TYPE_DICTIONARY: return variables.dailyactionsperslave - float(lastinteractionday.day == globals.resources.day) * lastinteractionday.count else: return variables.dailyactionsperslave - float(lastinteractionday == globals.resources.day) func canInteract(): return getRemainingInteractions() > 0 replace line 109 with: var lastinteractionday = {'day' : 0, 'count' : 0} events.gd fixed zoe's naked gallery never unlocking add line after 3124: globals.charactergallery.zoe.nakedunlocked = true add line after 3113 globals.charactergallery.zoe.nakedunlocked = true constantsmoddata.gd changed access to mod folder path to prevent problems related to static long-term copies replace line 571 with: var result = file.open(globals.modfolder + "Constants/storedvariables", File.WRITE) add line after 155: var modfolder = globals.modfolder + "Constants/" remove line 20: var modfolder = globals.modfolder + 'Constants/' clarified documentation replace line 17 with: # arrays and dictionaries must contain only a single non-container type and must not be empty (dictionary keys are not included in this count) constmodinstal.gd changed mod version to trigger update in the mod folder replace line 6 with: var modversion = '0.4.1' 299wearstrapon.gd simplified code replace lines 34-37 with: return "[name1] put[s/1] on [a /1]strap-on dildo[/s1]." added missing standard action variables add lines after 12: const givertags = [] const takertags = [] 310deepthroat.gd changed deepthroat action to be visible even without ringgag and reduced stress replace line 60 with: var effects = {pain = 3, tags = ['punish','pervert'], obed = rand_range(10,15)} comment out lines 40, 43, 44: # for i in takers: # if i.mouth != null && givers.size() > 1: # valid = false # if i.acc1 == null: # valid = false 409rope.gd fixed rope action condition to require enough rope for all takers replace line 27 with: if globals.state.getCountStackableItem('rope') < takers.size(): 420calminginsense.gd used Rendrassa's Arousing Incense text with some minor fixes, since it makes more sense than the current action changed effects to be more suitable(more lust, less sens) changed action to allow ongoing status and not directly give orgasms replace lines 66-76 with: text = "[name2] lie[s/2] unconscious, {^trembling:twitching} {^slightly:weakly} as [his2] {^nose[/s2]:nostrils} [is2] {^flooded:consumed:invaded} with the {^scent:incense:smell}." #elif member.consent == false: #TBD elif member.sens < 100: text = "[name2] {^show:give}[s/2] little {^response:reaction} to the {^incense:aroma:perfume}." elif member.sens < 400: text = "[name2] {^begin:start}[s/2] to {^respond:react} as the {^scent:smell:fragrance} is breathed in." elif member.sens < 800: text = "[name2] {^shiver[s/2]:shudder[s/2]:relax[es/2]} in {^pleasure:arousal:extacy} as the incense {^fills:invades:spreads through} [his2] sinuses." else: text = "[names2] bod[y/ies2] {^tremble:quiver}[s/2] {^as the incense permeates [his2] senses:in response to [names2] inhaling}{^ as [he2] rapidly near[s/2] orgasm: as [he2] approach[es/2] orgasm: as [he2] edge[s/2] toward orgasm:}." replace lines 53-59 with: temparray += ["[name1] {^take:place:shove:stick:hold}[s/1] [names2] face[/s2] {^close to:near} the incense"] temparray += ["[name1] activate[s/1] the incense under [names2] nose[/s2]"] text += temparray[randi()%temparray.size()] temparray.clear() temparray += [", {^whispering to:teasing} [him2] about the effects."] temparray += [", {^moving:waving} it beneath [his2] nose[/s2]."] temparray += [", so that [he2] {^sniff:inhale}[s/2] the {^aroma:scent:odor:perfume.}"] replace line 41 with: var effects = {lust = 75, sens = 20} replace line 30 with: var effects = {lust = 25, sens = 10} add lines after 19: func getongoingname(givers, takers): return "[name1] use[s/1] arousing incense on [name2]." func getongoingdescription(givers, takers): return "" replace line 18 with: return "Arousing Incense" replace lines 14-15 with: const givertags = ['noorgasm'] const takertags = ['noorgasm'] replace line 8 with: const canlast = true residentslinescript.gd changed slavelist to use slave id rather than slave list position replace lines 42, 35, 28, and 20 with: var pos = getPos() replace line 7 with: get_tree().get_current_scene().currentslave = getPos() add lines after 3: func getPos(): var pos = 0 var id = get_meta('id') for person in globals.slaves: if id == person.id: return pos pos += 1 return -1 ending.gd fixed reference to node that may not exist when returning to main menu replace line 52 with: if globals.main == null || globals.main.get_node("screenchange").visible: ending.tscn fixed backgrounds not being visible during ending scene move lines 70-78 to after line 58: [node name="TextureRect" type="TextureRect" parent="."] material = SubResource( 1 ) margin_left = -1.0 margin_top = -1.0 margin_right = 1378.0 margin_bottom = 774.0 texture = ExtResource( 4 ) expand = true slave_tab.gd added tab formatting to multi-line variable definitions add tab to appropriate lines between 758-767 add tab to appropriate lines between 649-654 add tabs to appropriate lines between 613-645 add tab to appropriate lines between 604-609 add tab to appropriate lines between 595-600 changed URLs to use slave id rather than slave list position replace line 541 with: var tempslave = globals.state.findslave( int(meta.replace('id',''))) replace lines 537-538 with: globals.slavetooltip( globals.state.findslave( int(meta.replace('id','')))) replace lines 524-526 with: text += '[url=id' + str(entry.id) + '][color=yellow]' + entry.name + '[/color][/url]' remove line 518: $stats/customization/relativespanel/relativestext.set_meta("slaves", slavearray) remove lines 482-483: counter = 0 slavearray.clear() remove lines 470-471: var counter = 0 var slavearray = [] added tab formatting to multi-line variable definitions add tab to appropriate lines between 427-430 removed unused variable remove lines 91-92: #warning-ignore:unused_variable var file = File.new() fixed reference to node that may not exist during loading saved progress replace line 40 with: if (globals.main != null && globals.main.get_node("screenchange").visible) || $stats/customization/nicknamepanel.is_visible(): removed unnecessary warning blocks remove line 36, 34, 30, 27, 25, 23: #warning-ignore:return_value_discarded effects.gd added tab formatting to multi-line variable definitions add tab to appropriate lines between 10-42 added drunk effect for interactions, not fully integrated into the game add line after 15: drunk = {code = 'drunk', duration = 1}, removed useless warning block remove line 8: #warning-ignore:unused_class_variable jobs&specs.gd fixed cook getting xp for slaves that are away replace line 623 with: person.xp += globals.slavecount() * 2 fixed exploit for jobs with per slave xp gain replace lines 605-614 with: var count = 0 if globals.player.health < globals.player.stats.health_max: globals.player.health += person.wit/15+person.smaf*3 person.xp += 1 count += 1 for i in globals.slaves: if i.away.duration == 0 && i.health < i.stats.health_max: count += 1 if globals.itemdict.supply.amount > 0: i.health += person.wit/25+person.smaf*3 else: i.health += person.wit/35+person.smaf*3 person.xp += count * 8 fixed RNG indexing bug (v5a) replace line 535 with: var request = globals.randomfromarray(array) replace line 499 with: item = globals.randomfromarray(itemarray replace line 474 with: var item = globals.itemdict[globals.randomfromarray(gearitemslist)] replace line 444 with: array.remove(randi() % array.size()) replace line 426 with: var item = globals.itemdict[globals.randomfromarray(weakitemslist)] added tab formatting to a lot of multi-line variable definitions, increased readability of some definitions add tab to appropriate lines between 5-383 split parts of each entry in leveluprequests to seperate lines split the beginning and ends of entries in jobdict to seperate lines laboratory.gd added tab formatting to multi-line variable definitions, improved readability fixed definitions between lines 121-241 prevent slaves in the farm from being assigned to be the lab assistant, fixed bug(v5a) replace line 41 with: get_tree().get_current_scene().selectslavelist(false,'_on_lab_pressed',self,globals.jobs.jobdict.labassist.reqs+" && globals.currentslave.sleep != 'farm'") items.gd added tab formatting to a lot of multi-line variable definitions add tabs to appropriate lines between 13-1217, too many to bother listing fixed Armor Breaker effect data for damage and armor piercing replace line 1030 with: effect = [{type = 'incombat', effect = 'damage', effectvalue = 9, descript = "+9 Damage"}, {type = 'passive', effect = 'armorbreaker', descript = "Bypass 8 Armor"}], increased rope cost now that it is reusable replace line 46 with: cost = 50, spells.gd fixed RNG indexing bug (v5a) replace line 518 with: person.ears = globals.randomfromarray(globals.allears) replace line 515 with: person.wings = globals.randomfromarray(globals.allwings) replace line 509 with: person.tail = globals.randomfromarray(globals.alltails) replace line 478 with: person.skincov = globals.randomfromarray(globals.skincovarray) replace line 471 with: person.balls = globals.randomfromarray(globals.genitaliaarray) replace line 462 with: person.penistype = globals.randomfromarray(globals.penistypearray) replace line 456 with: person.penis = globals.randomfromarray(globals.genitaliaarray) replace line 446 with: person.asssize = globals.randomfromarray(globals.sizearray) replace line 440 with: person.titssize = globals.randomfromarray(globals.sizearray) replace line 437 with: person.height = globals.randomfromarray(globals.heightarray) replace line 434 with: line = globals.randomfromarray(array) changed mindread text to clarify captured effect duration replace line 256 with: text = text + "\n$name doesn't accept $his new life in your domain. (Rebelling: " + str(person.effects.captured.duration) + ")" added tab formatting to multi-line variable definitions added tabs to lines 15-238 description.gd added tab formatting to multi-line variable definitions, improved readability, also fixed '}' at end of dictionaries that were not compatible with mod system add tabs to lines 392-546 fix '}' on lines 388 and 379 add tab to lines 339-346 add tab to lines 326-331 traits.gd added tab formatting to multi-line variable definition throughout entire file replaced each pair of leading spaces with a tab races.gd added tab formatting to multi-line variable definition throughout entire file, improved readability of stats dictionaries names.gd added tab formatting to multi-line variable definition throughout entire file gallery.gd added tab formatting to multi-line variable definitions throughout entire file, also fixed '}' at end of nakedsprites that was not compatible with mod system abilities.gd added tab formatting to multi-line variable definitions throughout entire file upgradespanel.gd added tab formatting to multi-line variable definitions add tab to start of lines 7-11 tutorial.gd added tab formatting to multi-line variable definitions throughout entire file mansionupgrades.gd added tab formatting to multi-line variable definitions throughout entire file explorationregions.gd added tab formatting to multi-line variable definitions throughout entire file newsexdictionary.gd added tab formatting to multi-line variable definitions throughout entire file, improved readability enchantments.gd added tab formatting to multi-line variable definitions add tab to lines 4-22 dicitonary.gd added tab formatting to multi-line variable definitions throughout entire file, also fixed '}' at end of loredict that was not compatible with mod system dailyevents.gd fixed RNG indexing bug (v5a) replace line 617 with: slave2 = globals.newslave( globals.getracebygroup(globals.state.location), 'random', 'random', globals.randomfromarray(origins)) replace line 558 with: slave2 = globals.newslave( globals.getracebygroup(globals.state.location), globals.randomfromarray(age), 'random', globals.randomfromarray(origins)) replace line 299 with: slave2 = globals.newslave( globals.getracebygroup(globals.state.location), globals.randomfromarray(age), 'random', globals.randomfromarray(origins)) replace line 131 with: return globals.randomfromarray(slavearray) replace line 89 with: rval = globals.randomfromarray(eventarray) added tab formatting to multi-line variable definitions add tab to lines 46-75 add tab to lines 13-42 combatdata.gd added tab formatting to multi-line variable definitions throughout entire file, improved readability