Character comparison start

This commit is contained in:
Mr. Stallion 2019-06-28 20:37:41 -05:00
parent f7d0dc3754
commit 5856057c81
9 changed files with 751 additions and 155 deletions

View File

@ -20,7 +20,7 @@
<script lang="ts">
import {Component, Hook, Prop} from '@f-list/vue-ts';
import Vue from 'vue';
import {EventBus} from './event-bus';
import {EventBus} from '../chat/event-bus';
// import core from './core';
@Component

View File

@ -4,7 +4,7 @@ import {InlineImage} from '../interfaces';
import { analyzeUrlTag, CoreBBCodeParser } from './core';
import {InlineDisplayMode} from './interfaces';
import {BBCodeCustomTag, BBCodeSimpleTag, BBCodeTextTag} from './parser';
import UrlTagView from '../chat/UrlTagView.vue';
import UrlTagView from './UrlTagView.vue';
interface StandardParserSettings {

View File

@ -7,7 +7,7 @@ import ChannelView from './ChannelTagView.vue';
import {characterImage} from './common';
import core from './core';
import {Character} from './interfaces';
import UrlView from './UrlTagView.vue';
import UrlView from '../bbcode/UrlTagView.vue';
import UserView from './user_view';
export const BBCodeView: Component = {

View File

@ -1,121 +0,0 @@
/*
export enum TagId {
Age = 1,
Orientation = 2,
Gender = 3,
Build = 13,
FurryPreference = 49,
BdsmRole = 15,
Position = 41,
BodyType = 51,
ApparentAge = 64,
RelationshipStatus = 42,
Species = 9,
LanguagePreference = 49
}
export enum Orientation {
Straight = 4,
Gay = 5,
Bisexual = 6,
Asexual = 7,
Unsure = 8,
BiMalePreference = 89,
BiFemalePreference = 90,
Pansexual = 127,
BiCurious = 128
}
orientationCompatibilityMap[Orientation.Straight] = [
[Orientation.Straight, 1],
[Orientation.Gay, -1],
[Orientation.Bisexual, 1],
[Orientation.Asexual, 0],
[Orientation.Unsure, 0],
[Orientation.BiMalePreference, (c: CharacterInfo) => (isMale(c) ? 1 : 0.5)],
[Orientation.BiFemalePreference, (c: CharacterInfo) => (isFemale(c) ? 1 : 0.5)],
[Orientation.Pansexual, 1],
[Orientation.BiCurious, 0]
];
orientationCompatibilityMap[Orientation.Gay] = [
[Orientation.Straight, -1],
[Orientation.Gay, 1],
[Orientation.Bisexual, 1],
[Orientation.Asexual, 0],
[Orientation.Unsure, 0],
[Orientation.BiMalePreference, (c: CharacterInfo) => (isMale(c) ? 1 : 0.5)],
[Orientation.BiFemalePreference, (c: CharacterInfo) => (isFemale(c) ? 1 : 0.5)],
[Orientation.Pansexual, 1],
[Orientation.BiCurious, (c: CharacterInfo, t) => isSameGender(c, t) ? 0.5 : 1]
];
orientationCompatibilityMap[Orientation.Bisexual] = [
[Orientation.Straight, 1],
[Orientation.Gay, 1],
[Orientation.Bisexual, 1],
[Orientation.Asexual, 0],
[Orientation.Unsure, 0],
[Orientation.BiMalePreference, (c: CharacterInfo) => (isMale(c) ? 1 : 0.5)],
[Orientation.BiFemalePreference, (c: CharacterInfo) => (isFemale(c) ? 1 : 0.5)],
[Orientation.Pansexual, 1],
[Orientation.BiCurious, 0]
];
orientationCompatibilityMap[Orientation.Asexual] = [];
orientationCompatibilityMap[Orientation.Unsure] = [];
orientationCompatibilityMap[Orientation.BiMalePreference] = [
[Orientation.Straight, -1],
[Orientation.Gay, 1],
[Orientation.Bisexual, 1],
[Orientation.Asexual, 0],
[Orientation.Unsure, 0],
[Orientation.BiMalePreference, (c: CharacterInfo) => (isMale(c) ? 1 : 0.5)],
[Orientation.BiFemalePreference, (c: CharacterInfo) => (isFemale(c) ? 1 : 0.5)],
[Orientation.Pansexual, 1],
[Orientation.BiCurious, 0]
];
*/
export class Matcher {
static readonly TAGID_AGE = 1;
static readonly TAGID_ORIENTATION = 2;
static readonly TAGID_GENDER = 3;
static readonly TAGID_FURRY_PREFERENCE = 49;
static readonly TAGID_BUILD = 13;
static readonly TAGID_BDSM_ROLE = 15;
static readonly TAGID_POSITION = 41;
static readonly TAGID_BODY_TYPE = 51;
static readonly TAGID_APPARENT_AGE = 64;
static readonly TAGID_RELATIONSHIP = 42;
static readonly TAGID_SPECIES = 9;
static readonly TAGID_LANGUAGE_PREFERENCE = 49;
}

View File

@ -5,7 +5,7 @@
<div class="alert alert-danger" v-show="error">{{error}}</div>
</div>
<div class="col-md-4 col-lg-3 col-xl-2" v-if="!loading && character">
<sidebar :character="character" :selfCharacter="selfCharacter" @memo="memo" @bookmarked="bookmarked" :oldApi="oldApi"></sidebar>
<sidebar :character="character" :characterMatch="characterMatch" @memo="memo" @bookmarked="bookmarked" :oldApi="oldApi"></sidebar>
</div>
<div class="col-md-8 col-lg-9 col-xl-10 profile-body" v-if="!loading && character">
<div id="characterView">
@ -40,7 +40,7 @@
<character-kinks :character="character" :oldApi="oldApi" ref="tab0"></character-kinks>
</div>
<div role="tabpanel" class="tab-pane" :class="{active: tab === '1'}" id="infotags">
<character-infotags :character="character" ref="tab1"></character-infotags>
<character-infotags :character="character" ref="tab1" :characterMatch="characterMatch"></character-infotags>
</div>
<div role="tabpanel" class="tab-pane" id="groups" :class="{active: tab === '2'}" v-if="!oldApi">
<character-groups :character="character" ref="tab2"></character-groups>
@ -83,6 +83,8 @@
import CharacterKinksView from './kinks.vue';
import Sidebar from './sidebar.vue';
import core from '../../chat/core';
import { Matcher, MatchReport } from './matcher';
interface ShowableVueTab extends Vue {
show?(): void
@ -119,6 +121,9 @@
selfCharacter: Character | undefined;
characterMatch: MatchReport | undefined;
@Hook('beforeMount')
beforeMount(): void {
this.shared.authenticated = this.authenticated;
@ -159,6 +164,8 @@
await Promise.all(due);
} catch(e) {
console.error(e);
this.error = Utils.isJSONError(e) ? <string>e.response.data.error : (<Error>e).message;
Utils.ajaxError(e, 'Failed to load character information.');
}
@ -177,13 +184,15 @@
protected async loadSelfCharacter(): Promise<Character> {
console.log('SELF');
// console.log('SELF');
const ownChar = core.characters.ownCharacter;
this.selfCharacter = await methods.characterData(ownChar.name, -1);
console.log('SELF LOADED');
// console.log('SELF LOADED');
this.updateMatches();
return this.selfCharacter;
}
@ -199,6 +208,18 @@
this.character = await methods.characterData(this.name, this.characterid);
standardParser.allowInlines = true;
standardParser.inlines = this.character.character.inlines;
this.updateMatches();
}
private updateMatches(): void {
if ((!this.selfCharacter) || (!this.character)) {
return;
}
const matcher = new Matcher(this.selfCharacter.character, this.character.character);
this.characterMatch = matcher.match();
}
}
</script>
@ -420,4 +441,27 @@
}
}
}
.infotag {
&.match {
background-color: green;
}
&.mismatch {
background-color: red;
}
&.weakMatch {
background-color: rgba(0, 162, 0, 0.6);
}
&.weakMismatch {
background-color: rgba(255, 96, 0, 0.6);
}
}
</style>

View File

@ -10,9 +10,10 @@
import {Component, Prop} from '@f-list/vue-ts';
import Vue from 'vue';
import {formatContactLink, formatContactValue} from './contact_utils';
import { Character, DisplayInfotag } from './interfaces';
import { DisplayInfotag } from './interfaces';
// import { Character as CharacterInfo } from '../../interfaces';
import {Store} from './data_store';
import { MatchReport, TagId } from './matcher';
@Component
@ -21,40 +22,34 @@
private readonly infotag!: DisplayInfotag;
@Prop({required: true})
private readonly selfCharacter!: Character;
private readonly characterMatch!: MatchReport;
get tagClasses() {
const styles = {
const styles: any = {
infotag: true,
};
//console.log('TAG', this.label, this.value, this.infotag);
console.log(`Infotag ${this.infotag.id}: ${this.label}`);
return this.getCharacterCompatibilityStyles(styles, this.selfCharacter);
}
if ((this.characterMatch) && (this.infotag.id in this.characterMatch)) {
const n = this.characterMatch[this.infotag.id];
console.log(`Found match [${this.infotag.id} === ${TagId[this.infotag.id]}]: ${n}`);
getCharacterCompatibilityStyles(styles: any, a: Character) {
if (a.character.name) {
styles.infotag = true;
if (n >= 1)
styles.match = true;
else if(n >= 0.5)
styles.weakMatch = true;
else if(n === 0)
styles.neutral = true;
else if(n <= -1)
styles.mismatch = true;
else if(n <= -0.5)
styles.weakMismatch = true;
}
// const c: CharacterInfo = this.selfCharacter.character;
// const t = this.infotag;
/* console.log(this.label, this.value, this.infotag.id, this.infotag, c.infotags);
switch (t.id) {
case InfotagView.TAGID_ORIENTATION:
break;
default:
// do nothing;
break;
}*/
return styles;
}

View File

@ -3,7 +3,7 @@
<div class="infotag-group col-sm-3" v-for="group in groupedInfotags" :key="group.id" style="margin-top:5px">
<div class="infotag-title">{{group.name}}</div>
<hr>
<infotag :infotag="infotag" v-for="infotag in group.infotags" :key="infotag.id" :selfCharacter="selfCharacter"></infotag>
<infotag :infotag="infotag" v-for="infotag in group.infotags" :key="infotag.id" :characterMatch="characterMatch"></infotag>
</div>
</div>
</template>
@ -16,6 +16,7 @@
import {Character, CONTACT_GROUP_ID, DisplayInfotag} from './interfaces';
import InfotagView from './infotag.vue';
import { MatchReport } from './matcher';
interface DisplayInfotagGroup {
id: number
@ -31,7 +32,7 @@
@Prop({required: true})
private readonly character!: Character;
@Prop({required: true})
readonly selfCharacter!: Character;
readonly characterMatch!: MatchReport;
get groupedInfotags(): DisplayInfotagGroup[] {
const groups = Store.kinks.infotag_groups;

View File

@ -0,0 +1,676 @@
import * as _ from 'lodash';
import { Character } from '../../interfaces';
export enum TagId {
Age = 1,
Orientation = 2,
Gender = 3,
Build = 13,
FurryPreference = 29,
BdsmRole = 15,
Position = 41,
BodyType = 51,
ApparentAge = 64,
RelationshipStatus = 42,
Species = 9,
LanguagePreference = 49
}
export enum Gender {
Male = 1,
Female = 2,
Transgender = 3,
Herm = 32,
MaleHerm = 51,
Cuntboy = 69,
None = 105,
Shemale = 141
}
export enum Orientation {
Straight = 4,
Gay = 5,
Bisexual = 6,
Asexual = 7,
Unsure = 8,
BiMalePreference = 89,
BiFemalePreference = 90,
Pansexual = 127,
BiCurious = 128
}
export enum BodyType {
Anthro = 122,
Feral = 121,
Morphable = 123,
Varies = 124,
Other = 125,
Androgynous = 126,
Human = 143,
Taur = 145
}
export enum KinkPreference {
Favorite = 1,
Yes = 0.5,
Maybe = 0,
No = -1
}
type ScoringCallback = (you: Character, them: Character) => number;
interface CompatibilityCollection {
[key: number]: ScoringCallback;
}
const orientationCompatibility: CompatibilityCollection = {
[Orientation.Straight]: (you: Character, them: Character) => Matcher.isCis(you, them) ? (Matcher.isSameSexCis(you, them) ? -1 : 1) : 0,
[Orientation.Gay]: (you: Character, them: Character) => Matcher.isCis(you, them) ? (Matcher.isSameSexCis(you, them) ? 1 : -1) : 0,
[Orientation.Bisexual]: (you: Character) => Matcher.isGenderedCis(you) ? 1 : 0,
[Orientation.Asexual]: () => 0,
[Orientation.Unsure]: () => 0,
[Orientation.BiMalePreference]: (you: Character, them: Character) => Matcher.isCis(you, them) ? (Matcher.isMaleCis(you) ? 1 : 0.5) : 0,
[Orientation.BiFemalePreference]: (you: Character, them: Character) => Matcher.isCis(you, them) ? (Matcher.isFemaleCis(you) ? 1 : 0.5) : 0,
[Orientation.Pansexual]: () => 1,
[Orientation.BiCurious]: (you: Character, them: Character) => Matcher.isCis(you, them) ? (Matcher.isSameSexCis(you, them) ? 0.5 : Matcher.isGenderedCis(you) ? 1 : 0) : 0
};
enum Kink {
Females = 554,
MaleHerms = 552,
Males = 553,
Transgenders = 551,
Herms = 132,
Shemales = 356,
Cuntboys = 231,
OlderCharacters = 109,
YoungerCharacters = 197,
Ageplay = 196,
UnderageCharacters = 207,
AnthroCharacters = 587,
Humans = 609
}
enum FurryPreference {
FurriesOnly = 39,
FursAndHumans = 40,
HumansOnly = 41,
HumansPreferredFurriesOk = 150,
FurriesPreferredHumansOk = 149
}
interface GenderKinkIdMap {
[key: number]: Kink
}
const genderKinkMapping: GenderKinkIdMap = {
[Gender.Female]: Kink.Females,
[Gender.Male]: Kink.Males,
[Gender.Cuntboy]: Kink.Cuntboys,
[Gender.Herm]: Kink.Herms,
[Gender.MaleHerm]: Kink.MaleHerms,
[Gender.Shemale]: Kink.Shemales,
[Gender.Transgender]: Kink.Transgenders
};
// if no species and 'no furry chareacters', === human
// if no species and dislike 'antho characters' === human
enum Species {
Human = 609,
Equine = 236,
Feline = 212,
Canine = 226,
Vulpine = 213,
Avian = 215,
Amphibian = 223,
Cervine = 227,
Insect = 237,
Lapine = 214,
Musteline = 328,
Dragon = 228,
Procyon = 325,
Rodent = 283,
Ursine = 326,
MarineMammal,
Primate = 613,
Elf = 611,
Orc = 615,
Fish = 608,
Reptile = 225,
Anthro = 587,
Minotaur = 121212
}
const nonAnthroSpecies = [Species.Human, Species.Elf, Species.Orc];
// const mammalSpecies = [Species.Human, Species.Equine, Species.Feline, Species.Canine, Species.Vulpine, Species.Cervine, Species.Lapine, Species.Musteline, Species.Rodent, Species.Ursine, Species.MarineMammal, Species.Primate, Species.Elf, Species.Orc, Species.Anthro, Species.Minotaur];
interface SpeciesMap {
[key: number]: string[]
}
const speciesMapping: SpeciesMap = {
[Species.Human]: ['human', 'humanoid', 'angel', 'android'],
[Species.Equine]: ['horse', 'stallion', 'mare', 'filly', 'equine', 'shire', 'donkey', 'mule', 'zebra', 'centaur', 'pony' ],
[Species.Feline]: ['cat', 'kitten', 'catgirl', 'neko', 'tiger', 'puma', 'lion', 'lioness', 'tigress', 'feline', 'jaguar', 'cheetah', 'lynx', 'leopard'],
[Species.Canine]: ['dog', 'wolf', 'dingo', 'coyote', 'jackal', 'canine', 'doberman', 'husky'],
[Species.Vulpine]: ['fox', 'fennec', 'kitsune', 'vulpine', 'vixen'],
[Species.Avian]: ['bird', 'gryphon', 'phoenix', 'roc', 'chimera', 'avian'],
[Species.Amphibian]: ['salamander', 'frog', 'toad', 'newt'],
[Species.Cervine]: ['deer', 'elk', 'moose'],
[Species.Insect]: ['bee', 'wasp', 'spider', 'scorpion', 'ant', 'insect'],
[Species.Lapine]: ['bunny', 'rabbit', 'hare', 'lapine'],
[Species.Dragon]: ['dragon', 'drake', 'wyvern'],
[Species.Musteline]: ['mink', 'ferret', 'weasel', 'stoat', 'otter', 'wolverine', 'marten'],
[Species.Procyon]: ['raccoon', 'coatimund', 'longtail'],
[Species.Rodent]: ['rat', 'mouse', 'chipmunk', 'squirrel', 'rodent'],
[Species.Ursine]: ['bear', 'panda', 'black bear', 'brown bear', 'polar bear'],
[Species.MarineMammal]: ['whale', 'killer whale', 'dolphin'],
[Species.Primate]: ['monkey', 'ape', 'chimp', 'chimpanzee', 'gorilla'],
[Species.Elf]: ['elf'],
[Species.Fish]: ['fish', 'shark', 'great white'],
[Species.Orc]: ['orc'],
[Species.Reptile]: ['chameleon', 'anole', 'alligator', 'snake', 'crocodile', 'lizard'],
[Species.Anthro]: ['anthro', 'anthropomorphic'],
[Species.Minotaur]: ['minotaur']
}
interface KinkPreferenceMap {
[key: string]: KinkPreference;
}
const kinkMapping: KinkPreferenceMap = {
favorite: KinkPreference.Favorite,
yes: KinkPreference.Yes,
maybe: KinkPreference.Maybe,
no: KinkPreference.No
};
export interface MatchReport {
[key: number]: number;
}
export class Matcher {
you: Character;
them: Character;
constructor(you: Character, them: Character) {
this.you = you;
this.them = them;
}
match(): MatchReport {
return {
[TagId.Orientation]: this.resolveScore(TagId.Orientation, orientationCompatibility),
[TagId.Gender]: this.resolveGenderScore(),
[TagId.Age]: this.resolveAgeScore(),
[TagId.FurryPreference]: this.resolveFurryScore(),
[TagId.Species]: this.resolveSpeciesScore()
};
}
private resolveScore(tagId: number, compatibilityMap: any, you: Character = this.you, them: Character = this.them): number {
const v = Matcher.getTagValueList(tagId, this.them);
if ((!v) || (!(v in compatibilityMap)))
return 0;
return compatibilityMap[v](you, them);
}
private resolveSpeciesScore() {
const you = this.you;
const them = this.them;
const yourSpecies = Matcher.species(you);
const theirSpecies = Matcher.species(them);
if (
((yourSpecies !== null) && (Matcher.hatesSpecies(them, yourSpecies))) ||
((theirSpecies !== null) && (Matcher.hatesSpecies(you, theirSpecies)))
) {
return -1;
}
if (
((yourSpecies !== null) && (Matcher.maybeSpecies(them, yourSpecies))) ||
((theirSpecies !== null) && (Matcher.maybeSpecies(you, theirSpecies)))
) {
return -0.5;
}
if (
((yourSpecies !== null) && (Matcher.likesSpecies(them, yourSpecies))) ||
((theirSpecies !== null) && (Matcher.likesSpecies(you, theirSpecies)))
) {
return 1;
}
return 0;
}
private resolveFurryScore() {
const you = this.you;
const them = this.them;
const youAreAnthro = Matcher.isAnthro(you);
const theyAreAnthro = Matcher.isAnthro(them);
const youAreHuman = Matcher.isHuman(you);
const theyAreHuman = Matcher.isHuman(them);
const yourScore = theyAreAnthro ? Matcher.furryLikeabilityScore(you) : theyAreHuman ? Matcher.humanLikeabilityScore(you) : 0;
const theirScore = youAreAnthro ? Matcher.furryLikeabilityScore(them) : youAreHuman ? Matcher.humanLikeabilityScore(them) : 0;
return Math.min(yourScore || 0, theirScore || 0);
}
static furryLikeabilityScore(c: Character): number | null {
const anthroKink = Matcher.getKinkPreference(c, Kink.AnthroCharacters);
if ((anthroKink === KinkPreference.Yes) || (anthroKink === KinkPreference.Favorite)) {
return 1;
}
if (anthroKink === KinkPreference.Maybe) {
return -0.5;
}
if (anthroKink === KinkPreference.No) {
return -1;
}
const furryPreference = Matcher.getTagValueList(TagId.FurryPreference, c);
if (
(furryPreference === FurryPreference.FursAndHumans) ||
(furryPreference === FurryPreference.FurriesPreferredHumansOk) ||
(furryPreference === FurryPreference.FurriesOnly)
) {
return 1;
}
if (furryPreference === FurryPreference.HumansPreferredFurriesOk) {
return 0.5;
}
if (furryPreference === FurryPreference.HumansOnly) {
return -1;
}
return 0;
}
static humanLikeabilityScore(c: Character): number | null {
const humanKink = Matcher.getKinkPreference(c, Kink.Humans);
if ((humanKink === KinkPreference.Yes) || (humanKink === KinkPreference.Favorite)) {
return 1;
}
if (humanKink === KinkPreference.Maybe) {
return -0.5;
}
if (humanKink === KinkPreference.No) {
return -1;
}
const humanPreference = Matcher.getTagValueList(TagId.FurryPreference, c);
if (
(humanPreference === FurryPreference.FursAndHumans) ||
(humanPreference === FurryPreference.HumansPreferredFurriesOk) ||
(humanPreference === FurryPreference.HumansOnly)
) {
return 1;
}
if (humanPreference === FurryPreference.FurriesPreferredHumansOk) {
return 0.5;
}
if (humanPreference === FurryPreference.FurriesOnly) {
return -1;
}
return 0;
}
static likesFurs(c: Character) {
const score = this.furryLikeabilityScore(c);
return (score !== null) ? (score > 0) : false;
}
static hatesFurs(c: Character) {
const score = this.furryLikeabilityScore(c);
return (score !== null) ? (score < 0) : false;
}
static likesHumans(c: Character) {
const score = this.humanLikeabilityScore(c);
return (score !== null) ? (score > 0) : false;
}
static hatesHumans(c: Character) {
const score = this.humanLikeabilityScore(c);
return (score !== null) ? (score < 0) : false;
}
private resolveAgeScore(): number {
const you = this.you;
const them = this.them;
const yourAgeTag = Matcher.getTagValue(TagId.Age, you);
const theirAgeTag = Matcher.getTagValue(TagId.Age, them);
if ((!yourAgeTag) || (!theirAgeTag)) {
return 0;
}
if ((!yourAgeTag.string) || (!theirAgeTag.string)) {
return 0;
}
const yourAge = parseInt(yourAgeTag.string, 10);
const theirAge = parseInt(theirAgeTag.string, 10);
if (
((theirAge < 16) && (Matcher.hates(you, Kink.Ageplay))) ||
((yourAge < 16) && (Matcher.hates(them, Kink.Ageplay))) ||
((theirAge < 16) && (Matcher.has(you, Kink.Ageplay) === false)) ||
((yourAge < 16) && (Matcher.has(them, Kink.Ageplay) === false)) ||
((yourAge < theirAge) && (Matcher.hates(you, Kink.OlderCharacters))) ||
((yourAge > theirAge) && (Matcher.hates(them, Kink.OlderCharacters))) ||
((yourAge > theirAge) && (Matcher.hates(you, Kink.YoungerCharacters))) ||
((yourAge < theirAge) && (Matcher.hates(them, Kink.YoungerCharacters))) ||
((theirAge < 18) && (Matcher.hates(you, Kink.UnderageCharacters))) ||
((yourAge < 18) && (Matcher.hates(them, Kink.UnderageCharacters)))
)
return -1;
if (
((theirAge < 18) && (Matcher.likes(you, Kink.UnderageCharacters))) ||
((yourAge < 18) && (Matcher.likes(them, Kink.UnderageCharacters))) ||
((yourAge > theirAge) && (Matcher.likes(you, Kink.YoungerCharacters))) ||
((yourAge < theirAge) && (Matcher.likes(them, Kink.YoungerCharacters))) ||
((yourAge < theirAge) && (Matcher.likes(you, Kink.OlderCharacters))) ||
((yourAge > theirAge) && (Matcher.likes(them, Kink.OlderCharacters))) ||
((theirAge < 16) && (Matcher.likes(you, Kink.Ageplay))) ||
((yourAge < 16) && (Matcher.likes(them, Kink.Ageplay)))
)
return 1;
return 0;
}
private resolveGenderScore() {
const you = this.you;
const them = this.them;
const yourGender = Matcher.getTagValueList(TagId.Gender, you);
const theirGender = Matcher.getTagValueList(TagId.Gender, them);
const yourGenderScore = Matcher.genderLikeabilityScore(them, yourGender);
const theirGenderScore = Matcher.genderLikeabilityScore(you, theirGender);
const yourFinalScore = (yourGenderScore !== null) ? yourGenderScore : this.resolveScore(TagId.Orientation, orientationCompatibility, you, them);
const theirFinalScore = (theirGenderScore !== null) ? theirGenderScore : this.resolveScore(TagId.Orientation, orientationCompatibility, them, you);
return Math.min(yourFinalScore, theirFinalScore);
}
static getTagValue(tagId: number, c: Character) {
return c.infotags[tagId];
}
static getTagValueList(tagId: number, c: Character): number | undefined {
const t = this.getTagValue(tagId, c);
if ((!t) || (!t.list)) {
return;
}
return t.list;
}
// Considers males and females only
static isSameSexCis(a: Character, b: Character): boolean {
const aGender = this.getTagValueList(TagId.Gender, a);
const bGender = this.getTagValueList(TagId.Gender, b);
if ((aGender !== Gender.Male) && (aGender !== Gender.Female)) {
return false;
}
return ((aGender !== undefined) && (aGender === bGender));
}
// Considers
static isGenderedCis(c: Character): boolean {
const gender = this.getTagValueList(TagId.Gender, c);
return ((!!gender) && (gender !== Gender.None));
}
static isMaleCis(c: Character): boolean {
const gender = this.getTagValueList(TagId.Gender, c);
return (gender === Gender.Male);
}
static isFemaleCis(c: Character): boolean {
const gender = this.getTagValueList(TagId.Gender, c);
return (gender === Gender.Female);
}
static isCis(...characters: Character[]): boolean {
return _.every(characters, (c: Character) => ((Matcher.isMaleCis(c)) || (Matcher.isFemaleCis(c))));
}
static genderLikeabilityScore(c: Character, gender?: Gender): number | null {
if (gender === undefined) {
return null;
}
const byKink = Matcher.getKinkGenderPreference(c, gender);
if (byKink !== null) {
if ((byKink === KinkPreference.Yes) || (byKink === KinkPreference.Favorite)) {
return 1;
}
if (byKink === KinkPreference.Maybe) {
return -0.5;
}
if (byKink === KinkPreference.No) {
return -1;
}
}
if (this.isCis(c)) {
if ((gender !== Gender.Female) && (gender !== Gender.Male)) {
return -1;
}
}
return null;
}
static likesGender(c: Character, gender: Gender): boolean | null {
const byKink = Matcher.getKinkGenderPreference(c, gender);
if (byKink !== null)
return ((byKink === KinkPreference.Yes) || (byKink === KinkPreference.Favorite));
if ((Matcher.isCis(c)) && ((gender === Gender.Male) || (gender === Gender.Female)))
return gender !== this.getTagValueList(TagId.Gender, c);
return null;
}
static dislikesGender(c: Character, gender: Gender): boolean | null {
const byKink = Matcher.getKinkGenderPreference(c, gender);
if (byKink !== null)
return (byKink === KinkPreference.No);
if ((Matcher.isCis(c)) && ((gender === Gender.Male) || (gender === Gender.Female)))
return gender === this.getTagValueList(TagId.Gender, c);
return null;
}
static maybeGender(c: Character, gender: Gender): boolean | null {
const byKink = Matcher.getKinkGenderPreference(c, gender);
if (byKink !== null)
return (byKink === KinkPreference.Maybe);
return null;
}
static getKinkPreference(c: Character, kinkId: number): KinkPreference | null {
if (!(kinkId in c.kinks))
return null;
return kinkMapping[c.kinks[kinkId] as string];
}
static getKinkGenderPreference(c: Character, gender: Gender): KinkPreference | null {
if (!(gender in genderKinkMapping))
return null;
return this.getKinkPreference(c, genderKinkMapping[gender]);
}
static getKinkSpeciesPreference(c: Character, species: Species): KinkPreference | null {
return this.getKinkPreference(c, species);
}
static likesSpecies(c: Character, species: Species): boolean | null {
const byKink = Matcher.getKinkSpeciesPreference(c, species);
if (byKink !== null)
return ((byKink === KinkPreference.Yes) || (byKink === KinkPreference.Favorite));
return null;
}
static maybeSpecies(c: Character, species: Species): boolean | null {
const byKink = Matcher.getKinkSpeciesPreference(c, species);
if (byKink !== null)
return (byKink === KinkPreference.Maybe);
return null;
}
static hatesSpecies(c: Character, species: Species): boolean | null {
const byKink = Matcher.getKinkSpeciesPreference(c, species);
if (byKink !== null)
return (byKink === KinkPreference.No);
return null;
}
static likes(c: Character, kinkId: Kink): boolean {
const r = Matcher.getKinkPreference(c, kinkId);
return ((r === KinkPreference.Favorite) || (r === KinkPreference.Yes));
}
static hates(c: Character, kinkId: Kink): boolean {
const r = Matcher.getKinkPreference(c, kinkId);
return (r === KinkPreference.No);
}
static has(c: Character, kinkId: Kink): boolean {
const r = Matcher.getKinkPreference(c, kinkId);
return (r !== null);
}
static isAnthro(c: Character): boolean | null {
const bodyTypeId = this.getTagValueList(TagId.BodyType, c);
if (bodyTypeId === BodyType.Anthro) {
return true;
}
const speciesId = this.species(c);
if (!speciesId)
return null;
return (nonAnthroSpecies.indexOf(parseInt(`${speciesId}`, 10)) < 0);
}
static isHuman(c: Character): boolean | null {
const bodyTypeId = this.getTagValueList(TagId.BodyType, c);
if (bodyTypeId === BodyType.Human) {
return true;
}
const speciesId = this.species(c);
return (speciesId === Species.Human);
}
static species(c: Character): Species | null {
let foundSpeciesId: Species | null = null;
let match = '';
const mySpecies = this.getTagValue(TagId.Species, c);
if ((!mySpecies) || (!mySpecies.string)) {
return Species.Human; // best guess
}
const finalSpecies = mySpecies.string.toLowerCase();
_.each(
speciesMapping as any,
(keywords: string[], speciesId: Species) => {
_.each(
keywords,
(k: string) => {
if ((k.length > match.length) && (finalSpecies.indexOf(k) >= 0)) {
match = k;
foundSpeciesId = speciesId;
}
}
);
}
);
return foundSpeciesId;
}
}

View File

@ -39,7 +39,7 @@
<div v-if="character.character.online_chat" @click="showInChat()" class="character-page-online-chat">Online In Chat</div>
<div class="quick-info-block">
<infotag-item v-for="infotag in quickInfoItems" :infotag="infotag" :key="infotag.id" :selfCharacter="selfCharacter"></infotag-item>
<infotag-item v-for="infotag in quickInfoItems" :infotag="infotag" :key="infotag.id" :characterMatch="characterMatch"></infotag-item>
<div class="contact-block">
<contact-method v-for="method in contactMethods" :method="method" :key="method.id"></contact-method>
@ -101,6 +101,7 @@
import FriendDialog from './friend_dialog.vue';
import InfotagView from './infotag.vue';
import {Character, CONTACT_GROUP_ID, SharedStore} from './interfaces';
import { MatchReport } from './matcher';
import MemoDialog from './memo_dialog.vue';
import ReportDialog from './report_dialog.vue';
@ -143,7 +144,7 @@
@Prop()
readonly oldApi?: true;
@Prop({required: true})
readonly selfCharacter!: Character;
readonly characterMatch!: MatchReport;
readonly shared: SharedStore = Store;
readonly quickInfoIds: ReadonlyArray<number> = [1, 3, 2, 49, 9, 29, 15, 41, 25]; // Do not sort these.