您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Generates an estimated compatibility score when comparing character kinks.
// ==UserScript== // @name F-List Kinks Calculator // @namespace flistkinkscalculator // @description Generates an estimated compatibility score when comparing character kinks. // @version 1.1.2 // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAAA4dJREFUWIXFl19oHFUUxn/nbswsbRBDS2jzog9BH2JdhFokVJqA0AcfpI2CoKAvYqlUaqFQUGnBCi2KihYJNCp5FamCCg0V3aBg81LQog/+oVk27UOyNG0C7Y7ZuceHZGbu7E5mdtMUD/twvjsz53733O+cu1dUlf/TugBEBABVPQicCgJbcF+y1obPSSMcfu/6xpjEO43AXvG6u14FZkSkEsYRVUVEUNUh4J2rc4veC0c+fLx5knjetIxJEjlQVbHWsnVLr3/uzOGLwBTwOVCJCAB7gBOjx74YtkEDMYkEbIiJCDZo8PDAdt5+ZaQMjAAk8jRf+Z3bS9c3fHKAxr91bi9dZ6pcTox3tbypirt9J18s4ft+xxN6nsebE7/GAwWT+l4rAWneURgYGOiYQLVaTcQxq8KwNlibgLUBgmBMM4X1mRtntZDQLALhQ+PI+NMLs8Ds+gi45RCWesgkjYC1AUiS+bX5G+uaHJJxNOo1ORkQkk1k7OjeyD/w7mTHODRrQg1kZEBtgIhp6WLxisy6cZiLTA1YGyCS3LtqtRoHFOkYx2Ta0ED4sODU7Hvn/or8QsF0jENrRCLM00BTBj54bSTyX//ox1x8ZF/cM97/6u/IN5EI86oASZaPY83jneDQzWxEcRWsoQEjuThBwMGmnS0I+4B7vn/8zRVnFZKLXUv7n5AjwtYMnDrwROQfG/spF7vmxhHTpgjvlgbCerB5IhS5Mw1MT087eHvkS7tl2Mz87GSyseTh3270OziO3XYj6unp2bDj2DXP8yjc42VnAGC2OsMfl5Jiiv8J68qvaR9FzGqzl5ZKCG3zfX30PbAjuxEBmKUKmybH2Qz0eR43fR8feH5iAmMMjUYjdQKRtSc3xqyegnU23RzMJvDD12d5VsYpeR67tm1jslIBYHR0NDU4rByxzcdsmtXrdS788mc2Ad/3eRA4NDhIwRge6+2lfPnymqsLv6nVarkElpeXW8YSBB7d8RAvHTrOy8Ui1lpUlYXFRRSYm5vLDF4sFiNfNXk5Aeju7qa/v5/h3f+kEpgBFk8fP8wbJ8+gqswvLPDJk89w8Psvsdby1unx3BXm2fDunTy3by+s3I6A+GY0BHwG3A9c/E5kDyBPqZar58/fe2n//keevnXr5ztmsGJTInIiqqy0y+a3YGulkgKMwdB8qZSvsA4tuuimEaiVSkHKN3eFwH8EALqp+4E4kAAAAABJRU5ErkJggg== // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js // @match *://www.f-list.net/c/* // @grant unsafeWindow // @grant GM.getValue // @grant GM.setValue // ==/UserScript== /* globals $ exportFunction */ var FList = unsafeWindow.FList; // Descriptions for your scores. var ratingNames = [ {score: -1.00, confidence: 0.00, desc: "Uncertain"}, {score: -1.00, confidence: 0.20, desc: "Are you fucking kidding me?"}, {score: -0.30, confidence: 0.15, desc: "Hell No!"}, {score: -0.15, confidence: 0.15, desc: "Nope"}, {score: 0.00, confidence: 0.15, desc: "Terrible"}, {score: 0.20, confidence: 0.20, desc: "Poor"}, {score: 0.40, confidence: 0.20, desc: "Average"}, {score: 0.60, confidence: 0.30, desc: "Good"}, {score: 0.75, confidence: 0.40, desc: "Very Good"}, {score: 0.85, confidence: 0.50, desc: "Excellent!"} ]; // The different scoring matrices available for selection. // 1st dimension is your character's kink. 2nd dimension is the other character. var scoringProfiles = [ { name: "Unbiased", matrix: { fave: {fave: 4, yes: 3, maybe: 1, no:-2}, yes: {fave: 3, yes: 3, maybe: 1, no:-1}, maybe: {fave: 1, yes: 1, maybe: 1, no: 0}, no: {fave:-2, yes:-1, maybe: 0, no: 0} } }, { name: "Self-Biased", matrix: { fave: {fave: 4, yes: 3, maybe: 2, no:-4}, yes: {fave: 3, yes: 3, maybe: 1, no:-2}, maybe: {fave: 1, yes: 1, maybe: 1, no: 0}, no: {fave:-1, yes:-1, maybe: 0, no: 0} } } ]; // Giving/Receiving kink pairs var asymmetricalKinks = { 16 : 163, 163 : 16, // Rimming 157 : 137, 137 : 157, // Anal 158 : 141, 141 : 158, // Oral 340 : 229, 229 : 340, // Vaginal 514 : 512, 512 : 514, // Fellatio 515 : 513, 513 : 515, // Cunnilingus 422 : 423, 423 : 422 // Vore }; // Class names for fave, yes, maybe, no var quickCompareClasses = [ 'Character_QuickCompareNo', 'Character_QuickCompareMaybe', 'Character_QuickCompareYes', 'Character_QuickCompareFave' ]; var statusText = $('<span id="KinkCalculatorStatus" style="padding-left:0.5em">'); var scoringProfileSelector = $('<select id="KinkCalculatorProfileSelector">').width('auto'); for (var p in scoringProfiles) { scoringProfileSelector.append($('<option>').val(p).append(scoringProfiles[p].name)); } // Insert our own controls. $('#Character_QuickCompareClear').after(statusText); $('#Character_QuickCompareSelect').width('auto').after(scoringProfileSelector).after(" Scoring profile: "); // Insert our own function into the default Quick Compare function. var oldCompare = FList.Character_quickCompare; var newCompare = function(clear) { console.log("Running a comparison..."); oldCompare(clear); kinkCalculator(clear); }; exportFunction(newCompare, FList, {defineAs: "Character_quickCompare"}); function kinkCalculator(clear) { statusText.empty(); $('.KinkCalculatorBreakdown, .KinkCalculatorAsym').remove(); if (clear) return; statusText.text("Getting character kinks..."); var char1 = { id: $('#Character_QuickCompareSelect').val(), name: $('#Character_QuickCompareSelect').find(":selected").text() }; var char2 = { id: $('#profile-character-id').val(), name: $('#profile-character-name').val() }; var profile = scoringProfileSelector.val(); var dataSource = '/json/getFetishes.php'; $.getJSON(dataSource, {character_id: char1.id, id_only: true}, function (a) { $.getJSON(dataSource, {character_id: char2.id, id_only: true}, function (b) { char1.kinks = a; char2.kinks = b; showScore(char1, char2, profile); }); }); } function calculateKinks(kinks1, kinks2, matrix) { var total = 0; var gross = 0; var positive = 0; var negative = 0; var neutral = 0; var misses = 0; for (var fid1 in kinks1) { var fid2 = asymmetricalKinks[fid1] ? asymmetricalKinks[fid1] : fid1; if (kinks2[fid2]) { var v = matrix[kinks1[fid1]][kinks2[fid2]]; total += v; gross += Math.abs(v); if (v > 0) positive++; else if (v < 0) negative++; else neutral++; var fItem1 = $('#Character_ListedFetish' + fid1); var fItem2 = $('#Character_ListedFetish' + fid2); if (fid1 !== fid2) { // Remap the highlighted kink element fItem2.addClass(fItem1.attr('class')) .attr('title', 'Kinks Calculator re-mapped to this kink using its giving/receiving rules') .append(' <b class="KinkCalculatorAsym">⇋</b>'); if (!fItem1.data('KinkCalculatorValue')) { fItem1.removeClass(quickCompareClasses.join(' ') + ' Character_QuickCompareActive'); } } fItem2.data('KinkCalculatorValue', v) .append(' <b class="KinkCalculatorBreakdown">' + (v < 0 ? '' : '+') + v + '</b>'); } else { misses++; } } var score = total / gross; var certainty = (3*positive + 2*negative + neutral) / (3*positive + 2*negative + neutral + misses); var rating = getRating(score, certainty); return { total: total, gross: gross, positive: positive, negative: negative, neutral: neutral, misses: misses, score: score, rating: rating, certainty: certainty }; } function getRating(score, confidence) { var rating = "?"; for (var r in ratingNames) { if (score >= ratingNames[r].score && confidence >= ratingNames[r].confidence) { rating = ratingNames[r].desc; } } return rating; } function showScore(char1, char2, profile) { statusText.text("Scoring..."); var result = calculateKinks(jsonKinksToMap(char1.kinks), jsonKinksToMap(char2.kinks), scoringProfiles[profile].matrix); var altColorMode = GM.getValue('altColorMode', false); var breakdownDetails = "Compare " + char1.name + " (" + char1.id + ") and " + char2.name+ " (" + char2.id + ")\n\n" + "Hits: " + result.positive + " positive, " + result.negative + " negative, " + result.neutral + " neutral\n" + "Misses: " + result.misses + "\n" + "Points (sum of all hit values): " + result.total + "\n" + "Gross (sum of absolute hit values): " + result.gross + "\n" + "Score (points / gross): " + toPercent(result.score, 1) + "\n" + "Confidence (based on hits / hits + misses): " + toPercent(result.certainty, 1); statusText.empty().append( '<b>Score:</b> ', $('<a href="#">' + toPercent(result.score, 0) + '</a>').click(function(){alert(breakdownDetails); return false;}), ' ', '<b>Confidence:</b> ' + toPercent(result.certainty, 0) + ' ', '<b>Rating:</b> ' + result.rating + ' ', $('<label for="KinkCalculatorAltColorMode"> Color based on points</label>') .prepend($('<input type="checkbox" id="KinkCalculatorAltColorMode"/>') .attr('checked', altColorMode ? 'checked' : null) .change(toggleQuickCompareColors)) ); toggleQuickCompareColors(); } function toggleQuickCompareColors() { var altColorMode = $('#KinkCalculatorAltColorMode').is(':checked'); GM.setValue('altColorMode', altColorMode); var classesJoined = quickCompareClasses.join(' '); $('.Character_QuickCompareActive').each(function(idx, el) { var e = $(el); if (altColorMode) { e.data('OldClasses', e.attr('class')); e.removeClass(classesJoined); var v = parseInt(e.data('KinkCalculatorValue')); var c = v > 3 ? 3 : v > 0 ? 2 : v < 0 ? 0 : 1; e.addClass(quickCompareClasses[c]); } else { e.attr('class', e.data('OldClasses')); } }); } function toPercent(x, dp) { return isNaN(x) ? "?" : (x * 100).toFixed(dp) + "%"; } function jsonKinksToMap(json) { var map = {}; $.each(json.fetishes, function (yif, f) {map[f.fid] = f.choice;}); return map; }