1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455 |
- declare let rand: Window["rand"];
- const bmi_attractive_range = [18.5,30];
- const timed_stat_changes ={ //Hourly
- energy: {
- sleep: 2,
- default: 6
- },
- hydra: {
- sleep: 4,
- default: 12
- },
- sleep:{
- sleep: -8,
- default: 4
- }
- }
- const dieRisks = {
- 'hunger':{
- variable: 'pcs_energy',
- durations: [720,720,14400]
- },
- 'thirst':{
- variable: 'pcs_hydra',
- durations: [720,720,1440]
- }
- }
- const fameAlwaysLocale = ['sex','prostitute'];
- class PlayerCharacter{
- gameover = 0;
- _death = {};
- name_first = 'Svetlana';
- name_last = 'Lebedev';
- name_nick = 'Sveta';
- get name_full(){return `${this.name_first} ${this.name_last}`;}
- avatar = '';
- dailyUpdate(){
- this.bodyDailyUpdate();
- }
- birthday = 1;
- birthmonth = 4;
- birthyear = 1999;
- get birthdayDate(){
- return new Date(Date.UTC(this.birthyear,this.birthmonth-1,this.birthday));
- }
- get age(){
- let age = State.variables.time.ageOfDate(this.birthday,this.birthmonth,this.birthyear);
- if(age < 16)
- {
- console.error("Critical Error: Playercharacter too young");
- Engine.restart();
- }
- return age;
- }
- set age(v){
- v = Math.max(v,16);
- let currentAge = this.age;
- let difference = v - currentAge;
- this.birthyear -= difference;
- console.log("AGE set to "+v);
- }
- /*get ageDays(){
- const oneDay = 86400000;
- let now = State.variables.time.now;
- let bday = this.birthdayDate;
- return Math.floor((now - bday)/oneDay);
- }*/
- _visualAgeDaysOffset = 0;
- get visualAgeDays(){return this._visualAgeDaysOffset}
- set visualAgeDays(v){this._visualAgeDaysOffset = v;}
- get visualAgeDaysInverse(){
- return this._visualAgeDaysOffset * -1;
- }
- set visualAgeDaysInverse(v){
- this._visualAgeDaysOffset = v * -1;
- }
- get visualAge(){
- return State.variables.time.ageOfDate(this.visualBirthday);
- }
- set visualAge(v){
- let currentVisualAge = this.visualAge;
- let offset = v - currentVisualAge;
- this._visualAgeDaysOffset += Math.floor(offset * 365.25);
- }
- get visualBirthday(){
- let visualBirthday = this.birthdayDate;
- visualBirthday.setUTCDate(visualBirthday.getUTCDate() - this._visualAgeDaysOffset);
- return visualBirthday;
- }
- get consciousness(){
- return this.#activeEffectValueByKey('consciousness','*');
- }
- /* Mood */
- _mood = new setup.Mood();
- get mood(){return this._mood.mood;}
- set mood(v){this._mood.mood = v}
- //get moodlets(){return this._mood.moodlets}
- get moodletsActive(){return this._mood.moodletsActive}
- get moodletsActiveByGroup(){return this._mood.moodletsActiveByGroup}
- get moodletsActiveEffect(){return this._mood.moodletsActiveEffect}
- get moodletsActiveByGroupAccumulationApplied(){return this._mood.moodletsActiveByGroupAccumulationApplied}
- moodletApplyById(moodletId,minutes=0){return this._mood.moodletApplyById(moodletId,minutes)}
- //moodletCombinedData(moodletId){return this._mood.moodletCombinedData(moodletId)}
- moodletDeactivateById(moodletId){return this._mood.moodletDeactivateById(moodletId)}
- moodletIncTime(moodletId,minutes,skipIncludedMoodlets=false){return this._mood.moodletIncTime(moodletId,minutes,skipIncludedMoodlets)}
- //moodletIsActive(moodletId){return this._mood.moodletIsActive(moodletId)}
- moodletUpdate(moodletId,updateObject){return this._mood.moodletUpdate(moodletId,updateObject)}
- #moodletsClean(){return this._mood._moodletsClean();}
-
- get moodletsSpecial():{[key: string]: ActiveMoodlet}{
- //return Object.assign({},this.moodletPain,this.activeEffectsMoodlets);
- return {pain:this.moodletPain};
- }
- get moodletPain():ActiveMoodlet{
- return PainMoodlet.createPainMoodlet(this.painTotal);
- }
-
-
- _pcs_willpwr = 70;
- get pcs_willpwr(){return this._pcs_willpwr;}
- set pcs_willpwr(v){
- this._pcs_willpwr = Math.clamp(v,0,this.willpowermax);
- console.log("Willpower set to "+this._pcs_willpwr);
- }
- will_counter = 0;
- _willpowermax = 70;
- get willpowermax(){return this._willpowermax}
- set willpowermax(v){this._willpowermax = Math.max(50,v);}
- pcs_willpower_feeder = 0;
- /* Hunger and eating */
- _energyBalance = 0; // A value between -6 and 6 that determines how fast you gain or lose bmi.
- _energy = 0; // The energy you consumed today.
- _energyDemand = 0; // Todays energy demand. Get updated every 15 minutes along witht the update of the hunger bar. Can also be increased by doing sports.
- _pcs_energy = 100;
- /**
- * This is effecitvely the hunger-bar.
- * @date 7/23/2023 - 5:56:29 PM
- *
- * @type {number}
- */
- get pcs_energy(){return this._pcs_energy;}
- set pcs_energy(v){
- this._pcs_energy = Math.clamp(v,0,100);
-
- if(v > 0 && this._death['hunger']?.stage){
- this._death['hunger'] = {stage:0};
- for(let i=1;i<=dieRisks.hunger.durations.length;i++)
- this.moodletDeactivateById('hunger_'+i);
- }
- }
- _dieHungerStage = 0;
- _dieHungerNextStageDate = undefined;
- get energy(){return this._energy;}
- set energy(v){this._energy = v;}
- get energyBalance(){return this._energyBalance;}
- set energyBalance(v){this._energyBalance = v;}
- get energyDemand(){return this._energyDemand;}
- set energyDemand(v){this._energyDemand = v;}
-
- /**
- * Energy Demand adjusted for the current BMI.
- * @date 7/27/2023 - 11:47:05 AM
- *
- * @readonly
- * @type {number}
- */
- get energyRequirement(){
- let baseRequirement = this.energyDemand; //timed_stat_changes.energy.sleep * 8 + timed_stat_changes.energy.default * 16;
- let requirementFactor = baseRequirement / 100;
- let bmi = this.bmi;
- // The following calculations assume that the base requirement is 100, therefore we need to multiplicate them with requirementFactor
- if(bmi < 19)
- return requirementFactor * (100 - Math.sqrt(19-bmi) * 10);
- if (bmi > 25)
- return requirementFactor * (100 + 5 * (bmi - 25));
- return baseRequirement;
- }
- _energyHistory = [];
- get energyHistory(){return this._energyHistory;}
- _energyHistoryLengthTarget = 7;
- get energyHistoryLengthTarget(){return this._energyHistoryLengthTarget;}
- set energyHistoryLengthTarget(v){
- this._energyHistoryLengthTarget = v;
- while(this._energyHistory.length > v)
- this._energyHistory.shift();
- }
- dailyEnergyUpdate(){
- let energyRequirement = this.energyRequirement;
- let energy = this.energy;
- let energyQuota = energy / energyRequirement;
- let balanceTarget = 0;
- if(energyQuota < 0.5)
- balanceTarget = -6;
- else if(energyQuota < 0.8)
- balanceTarget = -3;
- else if(energyQuota > 1.5)
- balanceTarget = 6;
- else if(energyQuota > 1.2)
- balanceTarget = 3;
-
- let balanceCurrent = this.energyBalance;
- let balanceDifference = balanceTarget - balanceCurrent;
- // Gaining weight is way easier than losing it.
- if(balanceDifference < 0)
- balanceDifference = balanceDifference / 3;
- else
- balanceDifference = balanceDifference * 2 / 3;
- this.energyBalance += balanceDifference;
- // Change the BMI
- let bmi_change = Math.pow(this.energyBalance,2) * 0.005 * Math.sign(this.energyBalance);
- this.bmi += bmi_change;
- console.log("$pc.dailyEnergyUpdate(): energyRequirement,energy,balanceOld, balanceNew, bmiChange, bmiNew",energyRequirement,energy,balanceCurrent,this.energyBalance,bmi_change,this.bmi);
- this._energyHistory.push({
- energyRequirement: energyRequirement,
- energy: energy,
- balanceTarget: balanceTarget,
- balanceChange: balanceDifference,
- balanceNew: this.energyBalance,
- bmiChange: bmi_change,
- bmiNew: this.bmi
- });
- this.energy = 0;
- this.energyDemand = 0;
- }
- _pcs_hydra = 100;
- _dieThirstStage = 0;
- _dieThirstNextStageDate = undefined;
- get pcs_hydra(){return this._pcs_hydra;}
- set pcs_hydra(v){
-
- /*if(v < 0){
- v = 0;
- this.pcs_health -= 5;
- setup.msgPredefined("warn_hydra_low");
- }
- */
- this._pcs_hydra = Math.clamp(v,0,100);
- if(v > 0 && this._death['thirst']?.stage){
- this._death['thirst'] = {stage:0};
- for(let i=1;i<=dieRisks.thirst.durations.length;i++)
- this.moodletDeactivateById('thirst_'+i);
- }
- }
- minutesTilStat(stat,target=0,isAsleep=undefined){
- const time = State.variables.time;
- isAsleep ??= (this.isSleeping == 1);
-
- let current = 0;
- let configIndex;
- switch(stat){
- case 'energy':
- case 'hunger':
- configIndex = 'energy';
- current = this.pcs_energy;
- break;
- case 'hydra':
- case 'thirst':
- configIndex = 'hydra';
- current = this.pcs_hydra;
- break;
- }
- const timeMod = 0.25 * this.timeFactor;
- let change = 0;
- if(isAsleep)
- change = timed_stat_changes[configIndex]['sleep'] * timeMod;
- else
- change = timed_stat_changes[configIndex]['default'] * timeMod;
- let requiredUpdates = Math.ceil((current - target) / (change || 1));
-
- const minutesToNext15MinutesInterval = 15 - time.now.getUTCMinutes() % 15;
- return Math.max(0,(requiredUpdates - 1)*15 + minutesToNext15MinutesInterval);
- }
- _pcs_sleep = 100;
- get pcs_sleep(){return this._pcs_sleep;}
- set pcs_sleep(v){
- /*if(v < 0){
- v = 0;
- this.mood -= 5;
- setup.msgPredefined("warn_sleep_low");
- }*/
- this._pcs_sleep = Math.clamp(v,0,100);
- console.log("Sleep set to "+this._pcs_sleep);
- }
- isSleeping = 0;
- _pcs_sweat = 0;
- get pcs_sweat(){return this._pcs_sweat;}
- set pcs_sweat(v){
- this._pcs_sweat = v;
- console.log("Sweat set to "+this._pcs_sweat);
- }
- sweatAdd(v){
- this.pcs_sweat += v;
- }
- get hornyMin(){
- const cycleArousalModificator = this.cycleArousalModificator;
- return cycleArousalModificator.target;
- }
- _pcs_horny = 0;
- get horny(){return Math.max(this._pcs_horny,this.hornyMin);}
- set horny(v){
- if(typeof v != "number" || isNaN(v)){
- console.error("Trying to set pcs_horny to non-number",v);
- return;
- }
- this._pcs_horny = Math.clamp(v,0,100);
- console.log("Horny set to "+this._pcs_horny);
- }
-
- /**
- * How much horny is supposed to decrease each minute.
- * @readonly
- * @type {number}
- */
- get hornyDeteriorationRate(){
- const cycleArousalModificator = this.cycleArousalModificator;
- return (1 / cycleArousalModificator.factor);
- }
- //_pcs_inhib = 0;
- get pcs_inhib(){
- //return this._pcs_inhib;
- return this.skillLevel('inhibition');
- }
- set pcs_inhib(v){
- //this._pcs_inhib = Math.clamp(v,0,100);
- //console.log("Inhibition set to "+this._pcs_inhib);
- this.skillSetLevel('inhibition',v);
- }
- _frost = 0;
- get frost(){
- if(this.alko > 0)
- return 0;
- return this._frost;
- }
- set frost(v){
- this._frost = v;
- }
-
- /**
- * @type {setup.Drugs}
- */
- _drugs = new setup.Drugs();
- get drugsActiveEffects(){return this._drugs.activeEffects}
- get drugsActiveEffectIds(){return this._drugs.activeEffectIds}
- drugsDeteriorate(minutes){return this._drugs.deteriorate(minutes)}
- drugVolInc(drugId, inc){return this._drugs.volInc(drugId, inc)}
- drugVolSet(drugId,v){return this._drugs.volSet(drugId, v)}
- drugVol(drugId){return this._drugs.vol(drugId)}
- get alko(){return this.drugVol('alcohol')}
- set alko(v){this.drugVolSet('alcohol',v)}
-
- // ----- Appearance History ------
- _appearanceHistory = [];
- appearanceHistoryPush(varname,val){
- this._appearanceHistory.push({
- time: State.variables.time.now,
- varname: varname,
- val: val
- });
- }
- // ----- Body ------
- bodyImage(part){
- }
- _pcs_vag = 0
- get pcs_vag(){return this._pcs_vag;}
- set pcs_vag(v){this._pcs_vag = Math.min(36,v);}
- _pcs_ass = 0
- get pcs_ass(){return this._pcs_ass;}
- set pcs_ass(v){this._pcs_ass = Math.min(36,v);}
- _pcs_throat = 0
- get pcs_throat(){return this._pcs_throat;}
- set pcs_throat(v){this._pcs_throat = Math.min(36,v);}
- _pcs_health = 0;
- get pcs_health(){return Math.min(this._pcs_health, this.healthmax)}
- set pcs_health(v){
- if(v < 0)
- this.gameover = 1;
- this._pcs_health = Math.min(v, this.healthmax);
- }
- get healthmax(){
- let healthmax_calc = Math.max(1,(this.vitality * 10 + this.strength * 5));
- let mult_by_pain = 1;
- let pain_total = this.painTotal;
- if(pain_total > 80)
- mult_by_pain = 0.20;
- else if(pain_total > 60)
- mult_by_pain = 0.40;
- else if(pain_total > 40)
- mult_by_pain = 0.60;
- else if(pain_total > 20)
- mult_by_pain = 0.80;
- else if(pain_total > 0)
- mult_by_pain = 0.90;
- healthmax_calc = Math.ceil(healthmax_calc * mult_by_pain);
- return healthmax_calc;
- }
- _pcs_stam = 0;
- get pcs_stam(){return this._pcs_stam}
- set pcs_stam(v){this._pcs_stam = Math.min(v, this.stammax)}
- get stammax(){return Math.max(1,5 * (2 * this.vitality + this.agility + this.strength) / 2)}
- get speed(){return (2 * (this.strength + this.agility) + this.vitality) / 5}
- genbsize = 12; // the set genetic bust size
- nbsize = 12; // starts at a set genetic bust size, but can be adjusted down if salo drops too low
- /*get wratio(){ // waist to hips ratio set in body_shape
- let wrtemp = ((2 * this.healthiness + this.muscularity + this.dexterity) / 4);
- let result = 85;
- if(wrtemp < 11)
- result = 85 + (11 - (this.healthiness + this.muscularity + this.dexterity) / 3);
- else if(wrtemp < 20)
- result = 85;
- else if(wrtemp < 35)
- result = 85 - (wrtemp - 20) / 3;
- else if(wrtemp < 55)
- result = 80 - (wrtemp - 35) / 4;
- else if(wrtemp < 80)
- result = 75 - (wrtemp - 55) / 5;
- else
- result = 70;
- if ((this.healthiness + this.muscularity + this.dexterity) / 3 > 100)
- result -= ((this.healthiness + this.muscularity) / 2 - this.dexterity) / 5;
- result = Math.max(result,65);
- return result;
- }
- get bratio(){ //band to waist ratio set in body_shape-->
- let brtemp = (2 * this.muscularity + this.healthiness + this.dexterity) / 4;
- if (brtemp < 10)
- return 105;
- else if (brtemp <= 23)
- return 106;
- else if (brtemp <= 80)
- return 106 + (brtemp - 23) / 3;
- else
- return 125;
- }
- get hratio(){ //hip to height ratio set in body_shape
- let hrtemp = (2 * this.dexterity + this.healthiness + this.muscularity) / 4;
- if (hrtemp < 35)
- return 60;
- else if (hrtemp < 45)
- return 59;
- else if (hrtemp < 60)
- return 58;
- else if (hrtemp < 80)
- return 57;
- else
- return 56;
- }*/
- _fat = 0; //the fat the character consumed recently
- get fat(){
- return this._fat;
- }
- set fat(v){
- console.log("FAT set to "+v);
- this._fat = v;
- }
- vofat = 0; //used as a place to put extra salo at extreme high values (i.e. really, really fat) set in body_shape
- _salo = 80; //the fat the character carries around with her
- get salo(){
- return this._salo;
- }
- set salo(v){
- this._salo = Math.min(v,250);
- console.log("SALO set to "+this._salo);
- }
- salocatnow = 0; //the current category of salo
- salolast = 0;
- salocatlast = 0; //the previous category of salo
- silicone = 0;
- silicone_butt = 0;
- butt_cheat = 0;
- magicf2b = 0; //magicf2b = set in body_shape for the fat moved to bust
- /*get pcs_hips(){return (this.pcs_hgt * this.hratio) / 100 + this.vhips;}
- get pcs_waist(){return (this.pcs_hips * this.wratio) / 100 + this.vofat;}
- get pcs_band(){return (this.pcs_waist * this.bratio) / 100 + this.vofat;}
- get pcs_bust(){return (this.pcs_waist * this.bratio) / 100 + this.nbsize + this.magicf2b + this.silicone;}
- get pcs_butt(){return (this.pcs_hips / 10) + this.silicone_butt + this.butt_cheat;}*/
- //get pcs_cupsize(){return (this.pcs_bust - this.pcs_band);}
- _cupsize = 15;
- get pcs_cupsize(){return this._cupsize;}
- set pcs_cupsize(v){this._cupsize = v;}
- get tits(){
- if(this.pcs_cupsize <= 5) return 0;
- if(this.pcs_cupsize <= 10) return 1;
- if(this.pcs_cupsize <= 15) return 2;
- if(this.pcs_cupsize <= 20) return 3;
- if(this.pcs_cupsize <= 25) return 4;
- if(this.pcs_cupsize <= 30) return 5;
- if(this.pcs_cupsize <= 35) return 6;
- if(this.pcs_cupsize <= 40) return 7;
- if(this.pcs_cupsize <= 45) return 8;
- if(this.pcs_cupsize <= 50) return 9;
- if(this.pcs_cupsize <= 55) return 10;
- return 11;
- }
- set tits(v){
- this.pcs_cupsize = Math.ceil((v+1)*5);
- }
- get titsize(){
- switch(this.tits){
- case 0: return 'AA cup';
- case 1: return 'A cup';
- case 2: return 'B cup';
- case 3: return 'C cup';
- case 4: return 'D cup';
- case 5: return 'E cup';
- case 6: return 'F cup';
- case 7: return 'G cup';
- case 8: return 'H cup';
- case 9: return 'I cup';
- case 10: return 'J cup';
- case 11: return 'K cup';
- default: return '??? cup';
- }
- }
- get pcs_weight(){
- /*//pcs_weight: Svetas weight in kg
- //Optimal Weight (170cm): 60kg, 60 salo
- //This is an approximation, so that a sveta with a different height has the same BMI for the same salo values.
- //The default height of 170 cm is used as a starting point. The bmi deviates by 0.06 for a deviation of 10cm and 0.4 for a deviation of 20cm.} -->
- return 30 + this.salo / 2 + (this.pcs_hgt - 170) * 7 / 10;
- //Do not remove (julzor)
- //This is the old more complex function, but it doesn''t work correctly.
- //This variable is actually only used ONCE (except for the BMI calculation), when stripping, to see if you fall off the pole from being too heavy. I am reluctant to touch it, since the math is not explained. Maybe later.
- //Weight = (height in cm x bottom of average weight in kilos for a 170cm female / 170cm + (hip modifier + 2 x overrun fat)/4 + cup size} -->
- //result = ($pcs_hgt * 62 / 170) + (vhips + (vofat * 2)) / 4 + tits-->*/
- return Math.pow(this.pcs_hgt / 100,2) * this.bmi;
- }
- _bmi = 20;
- get bmi(){
- /*let bmi_all_decimal_places = 10000 * (30 + this.salo / 2 + (this.pcs_hgt - 170) * 7 / 10) / (this.pcs_hgt * this.pcs_hgt);
- let bmi_rounded_to_one_decimal = Math.round(bmi_all_decimal_places * 10) / 10;
- return bmi_rounded_to_one_decimal;*/
- return this._bmi;
- }
- set bmi(v){
- this._bmi = v;
- }
- get bmi_is_attractive(){
- let bmi = this.bmi;
- return (bmi >= bmi_attractive_range[0] && bmi <= bmi_attractive_range[1]);
- }
- get bmi_desc(){
- let bmi = this.bmi;
- if(bmi < 16)
- return 'You are severely underweight.';
- else if(bmi < 19)
- return 'You are underweight';
- else if(bmi < 25)
- return 'You have a normal, healthy weight.';
- else if(bmi < 30)
- return 'You are overweight.';
- else if(bmi < 35)
- return 'You are moderately obese.';
- else if(bmi < 40)
- return 'You are severely obese.';
- else if(bmi < 45)
- return 'You are very severely obese.';
- else
- return 'You are morbidly obese.';
- }
- pcs_hgt = 170; // Height in cm
- pcs_hairlng = 300; // Hair length in mm
- get hairLength(){return this.pcs_hairlng;}
- set hairLength(v){this.pcs_hairlng = v;}
- hairColor = 0;
- hairColorNatural = 0;
- hairDyeFade = 0;
- hairDye(newColor,fadeDuration=undefined){
- this.appearanceHistoryPush('hairColor',newColor);
- this.hairColor = newColor;
- this.hairDyeFade = fadeDuration ?? 30;
- }
- pubesLength = 0; // Pubes hair length in mm
- pubestyle = 0; // The style the pubes get shaved into. 0 is not shaving.
- pubesState = 0; // 0: Default, 1: lasered
- get pubesGrowth(){
- switch(this.pubesState){
- case 0: return 0.5;
- case 1: return 0;
- }
- }
- get pubesAreLasered(){return (this.pubesState == 1);}
- set pubesAreLasered(v){
- if(v){
- this.pubesLength = 0;
- this.pubesState = 1;
- }else{
- this.pubesState = 0;
- }
- }
- preg = 0; // 1: is pregnant, 0: is not pregnant
- knowpreg = 0; // 1: Is pregnant and knows it, 0: doesn't know is pregnant but could be
- thinkpreg = 0; // 1: thinks she is pregnant (doesn't have to be true), 0: doesn't think she is pregnant (doesn't have to be true either)
- PregChem = 0; // Size of the pregnancy
- clit_size = 0;
- get isPregnancyAware(){ // Either knows she is pregnant, correctly assumes to be pregnant or is big enough to no longer be in denial.
- if (this.knowpreg == 1) return 1;
- if (this.preg == 1 && this.thinkpreg == 1) return 1;
- if (this.preg == 1 && this.PregChem > 3600) return 1;
- return 0;
- }
- get bodset(){ //body image and descriptor control variable, used to indicate which image and descriptor set is in use
- /*if(this.isPregnancyAware == 1) return 3;
- if (this.muscularity >= 70) return 2;
- if (this.muscularity <= 40) return 0;*/
- return 1;
- }
- get body(){
- /*let bodimgsets = State.variables.bodimgsets;
- if(this.isPregnancyAware){
- if(this.PregChem > 6216)
- return bodimgsets[(this.bodset * 10) + 8];
- if(this.PregChem < 2688)
- return bodimgsets[this.bodset * 10];
- return bodimgsets[Math.floor(this.bodset * 10 + ((this.PregChem - 2184) / 504))];
- }
- if(this.salocatnow <= 7)
- return bodimgsets[((this.bodset * 10) + this.salocatnow)];
- return bodimgsets[(this.bodset * 10) + 7];*/
- return null;
- }
- /*pcs_teeth = 0; //-1: perfectly white
- teeth = {
- brushed: 0,
- caffe_or_tea: 0,
- degradation:0 ,
- smoked: 0
- }
- get teethQuality(){return this.pcs_teeth}*/
- _teeth = 1;
- _teeethMissing = [];
- get teeth(){return this._teeth;}
- set teeth(v){this._teeth=Math.max(0,v);}
- get teethQuality(){
- let t = this.teeth;
- if(t < 100)
- return 0;
- else if(t < 1000)
- return 1;
- return 2;
- }
- set teethQuality(v){
- switch(v){
- case 0: this.teeth = 50; break;
- case 1: this.teeth = 500; break;
- case 2: this.teeth = 1500; break;
- }
- }
- get teethMissingCount(){
- return this._teeethMissing.length;
- }
- //pcs_breath = 0;
- _pcs_lipbalm = 0;
- get pcs_lipbalm(){return this._pcs_lipbalm;}
- set pcs_lipbalm(v){this._pcs_lipbalm = Math.max(0,v);}
- _leghair = 0;
- /**
- * The length of the hair on the legs in mm.
- * @date 7/23/2023 - 9:25:25 AM
- *
- * @type {number}
- */
- get legHair(){return this._leghair;}
- set legHair(v){this._leghair = v;}
- legHairState = 0; //0: default, 1: lasered
- get legHairGrowth(){
- switch(this.legHairState){
- case 0: return (this.age < 18 ? 0.14 : 0.21);
- case 1: return 0;
- }
- }
- get legHairIsLasered(){return (this.legHairState == 1);}
- set legHairIsLasered(v){
- if(v){
- this.legHair = 0;
- this.legHairState = 1;
- }else{
- this.legHairState = 0;
- }
- }
- get legHairVisibility(){
- if(this.legHair <= 0)
- return 0;
- else if(this.legHair <= 0.5) // ~3 Days
- return 1;
- else if(this.legHair <= 1.5) // ~1 Week
- return 2;
- else if(this.legHair <= 6) // ~1 Month
- return 3;
- return 4;
- }
- set legHairVisibility(v){
- switch (v) {
- case 0: this._leghair = 0; return;
- case 1: this._leghair = 0.2; return;
- case 2: this._leghair = 1; return;
- case 3: this._leghair = 4; return;
- default:this._leghair = 20; return;
- }
- }
- pcs_hairbsh = 0;
- deodorant_on = 0;
- deodorant_time = 0;
- moisturizerDailyCount = 0;
- skinDailyGain = 0;
- skinDailyPenalty = 0;
- _pcs_skin = 500;
- get pcs_skin(){
- return this._pcs_skin;
- }
- set pcs_skin(v){
- this._pcs_skin = Math.clamp(v,0,1000);
- }
- get skinAppearance(){
- return Math.round(this.pcs_skin / 200 - 2.5);
- }
- set skinAppearance(v){
- switch(v){
- case -3: this.pcs_skin = 0; return;
- case -2: this.pcs_skin = 100; return;
- case -1: this.pcs_skin = 300; return;
- case 0: this.pcs_skin = 500; return;
- case 1: this.pcs_skin = 700; return;
- case 2: this.pcs_skin = 900; return;
- case 3: this.pcs_skin = 1000; return;
- }
- }
- tan = 0;
- bodyDailyUpdate(){
- this.dailyEnergyUpdate();
- /*if(this.muscularity > this.strength)
- this.muscularity -= 1;
- else if(this.muscularity < this.strength)
- this.muscularity += 1;
- if(this.healthiness > this.vitality)
- this.healthiness -= 1;
- else if(this.healthiness < this.vitality)
- this.healthiness += 1;
- if(this.dexterity > this.agility)
- this.dexterity -= 1;
- else if(this.dexterity < this.agility)
- this.dexterity += 1;*/
- /*if(this.fat > (17 + this.healthiness / 25)){
- this.salo += 1;
- this.fat = 0;
- }
- else if(this.fat < (-2 - (this.healthiness / 10))){
- this.salo -= 1;
- this.fat = 0;
- }
- this.fat = this.fat / 4;
- this.bodySaloCalc();*/
- this.hairLength += 1;
- /*if(this.pcs_lashes > 2){
- if(this.lashextensionstyle >= 1){
- this.lashextensionduration -= 1;
- if(this.lashextensionduration >= 1 && this.lashextensionduration <= 4)
- message("warn","It's time for you to do your maintenance on your lash extensions; you should go to the salon or you risk growing them all out.");
- if(this.lashextensionduration <= 0){
- message("bad","You waited too long to do maintenance on your lash extensions; there's too little there to notice or work with at this point.");
- this.pcs_lashes = this.pcs_naturallashes;
- this.lashextensionstyle = 0;
- this.lashextensionduration = 0;
- this.lashextensionnew = 0;
- }
- }
- if(this.false_lashes > 0){
- this.false_lashes -= 1;
- if (this.false_lashes == 0){
- message("bad","Your false lashes came off in the night; there's no recovering them now.");
- this.pcs_lashes = this.pcs_naturallashes;
- }
- message("info","Somehow, your lashes managed to stay attached throughout the night. You might be able to get away with wearing them another day straight.");
- }
- }*/
- // Hair colour change
- //TODO: Dasdas
- /*if(this.pcs_haircol != this.nathcol){
- this.hairDyeFade = Math.max(this.hairDyeFade - 1 , 0);
- }*/
- // Leg and pubes hair growth
- this.legHair += this.legHairGrowth;
- //Pubic hair growth at 1/2 per night
- this.pubesLength += this.pubesGrowth;
- this.teeth += 1;
- console.info("Deactivated: degradation of pube hair coloring");
- /*<!-- !!pubic hair colouring-->
- <!-- !! pcs_pubecol = natural colour-->
- <!-- !! pcs_pubecol_num[1] = flag for saveupdate-->
- <!-- !! pcs_pubecol_num[2] = actual colour-->
- <!-- !! pcs_pubecol_num[3] = countdown timer for dye-->
- <<if $pcs_pubecol_num[2] != $pcs_pubecol>>
- <<setinit $pcs_pubecol_num[3] -= 1>>
- <<if $pcs_pubecol_num[3] < 0>>
- <<setinit $pcs_pubecol_num[3] = 0>>
- <</if>>
- <<if $pcs_pubecol_num[3] == 0>>
- <<setinit $pcs_pubecol_num[2] = $pcs_pubecol>>
- <</if>>
- <</if>>
- <<if getvar("$pubesLength") < 2>>
- <<setinit $pcs_pubecol_num[2] = $pcs_pubecol>>
- <</if>>*/
- console.info("Deactivated: degradation of hair scrunches");
- /*
- <<if getvar("$hscrunch") > 0>>
- <<set $hscrunchrand = rand(1, 100)>>
- <<if getvar("$hscrunchrand") <= 8>>
- <<setn $hscrunch -= 1>>
- <</if>>
- <</if>>*/
- /*if(this.pcs_skin <= 300)
- this.pcs_skin += Math.min(this.skinDailyGain * 2, 20) - this.skinDailyPenalty - 1;
- else if(this.$pcs_skin <= 600)
- this.pcs_skin += Math.min(this.skinDailyGain, 10) - this.skinDailyPenalty - 1;
- else if(this.pcs_skin <= 800)
- this.pcs_skin += Math.min(this.skinDailyGain / 2, 5) - this.skinDailyPenalty - 1;
- else if(this.pcs_skin <= 900)
- this.pcs_skin += Math.min(this.skinDailyGain / 3, 3) - this.skinDailyPenalty - 1;
- else if(this.pcs_skin <= 1000)
- this.pcs_skin += Math.min(this.skinDailyGain / 5, 2) - this.skinDailyPenalty - 1;
- if(this.pcs_teeth < 0){
- // Daly degradation of perfect white teeth
- let tempteeth = 1;
- if(this.teeth['caffe_or_tea'] > 8)
- tempteeth += 1;
- if(this.teeth['smoked'] > 1)
- tempteeth += 1;
- tempteeth -= Math.min(3, this.teeth['brushed']);
- this.teeth['degradation'] += Math.max(tempteeth, 0);
- this.teeth['brushed'] = 0;
- this.teeth['smoked'] = 0;
- this.teeth['caffe_or_tea'] = 0;
- if(this.teeth['degradation'] > 60)
- {
- // After a certain time of not taking care of your teeth you will loose you perfect whit smile.
- this.teeth['degradation'] = 0;
- this.pcs_teeth = 0;
- }
- this.teeth['degradation'] = Math.max(this.teeth['degradation'],0);
- }
- this.moisturizerDailyCount = 0;
- this.skinDailyGain = 0;
- this.skinDailyPenalty = 0;*/
- }
- get agility(){ return this.skillLevel('agility');} set agility(v){ this.skillSetLevel('agility',v);}
- get strength(){return this.skillLevel('strength');} set strength(v){this.skillSetLevel('strength',v);}
- get vitality(){return this.skillLevel('vitality');} set vitality(v){this.skillSetLevel('vitality',v);}
- // ---- Appearance ------
- get pcs_makeup(){
- /*if(this.cosmetic_tattoo > 0)
- return this.cosmetic_tattoo + 1;
- return this._pcs_makeup;*/
- console.warn('Usage of get $pc.pcs_makeup is deprecated');
- return Math.floor((this.makeupAmount + this.makeupQuality)/2);
- }
- set pcs_makeup(v){
- console.warn('Usage of set $pc.pcs_makeup is deprecated');
- //this._pcs_makeup = v;
- this.makeupAmount = v;
- this.makeupQuality= v;
- }
- _makeupAmount = 0;
- get makeupAmount(){
- return this._makeupAmount;
- }
- set makeupAmount(v){
- this._makeupAmount = v;
- }
- _makeupQuality = 0;
- get makeupQuality(){
- return this._makeupQuality;
- }
- set makeupQuality(v){
- this._makeupQuality = v;
- }
- cosmetic_tattoo = 0;
- get appearance(){
- return setup.func('Appearance','appearance');
- }
- get hotcat(){
- return this.appearance.hotcat.temp;
- }
- // ----- Toys -----
- analplugin = 0;
- vibratorin = 0;
- // ----- Skills -----
- get intelligence(){
- return this.skillLevel('intelligence');
- }
- _skills:{[key: string]:any} = {
- }
- get skillsAll(){
- let result = {};
- for(const [skillId, skillData] of Object.entries(this._skills)){
- result[skillId] = Object.assign({},skillData,{
- level: this.skillLevel(skillId)
- });
- }
- return result;
- }
- get skillExperienceGainMult(){
- let lackOfSleep = 0; //TODO: get the correct value
- if(lackOfSleep >= 20)
- return 0.25;
- else if(lackOfSleep >= 10)
- return 0.5;
- else if(lackOfSleep >= 5)
- return 0.75;
- else if(lackOfSleep >= 2)
- return 0.9;
- return 1;
- }
- #skillInit(skillId){
- if(!(skillId in this._skills))
- this._skills[skillId] = {
- lastUsed: 0,
- experience: 0,
- experienceHistory:[],
- ceil: 0,
- floor: 0
- }
- }
- #skill_dayly(){
- let today = State.variables.time.daystart;
- for (const [skillId, skillData] of Object.entries(this._skills)){
- this._skills[skillId].experienceHistory.push(skillData.experience);
- if(this._skills[skillId].experienceHistory.length > 30)
- this._skills[skillId].experienceHistory.shift();
- this._skills[skillId].ceil = Math.max(skillData.experience, skillData.floor);
- this._skills[skillId].floor = Math.max(Math.ceil(skillData.experience/2), skillData.floor);
- let daysNotUsed = today - this._skills[skillId].lastUsed;
- let newexperience = skillData.experience;
- if(daysNotUsed > 14){
- let currentLevel = this.#skill_exp2lvl(skillId,skillData.experience);
- let experienceForCurrentLevel = this.#skill_lvl2exp(skillId,currentLevel);
- let experienceForPreviousLevel= this.#skill_lvl2exp(skillId,currentLevel-1);
- let experienceDifferenceToPreviousLevel = experienceForCurrentLevel - experienceForPreviousLevel;
- let experienceLoss = Math.ceil(experienceDifferenceToPreviousLevel / 4);
- newexperience -= experienceLoss //* this.timeFactor;
- }
- this._skills[skillId].experience = Math.max(newexperience,this._skills[skillId].floor);
- }
- }
- #skill_exp2lvl(skillId,exp){
- return Math.floor(Math.pow(exp,1/2));
- }
- #skill_lvl2exp(skillId,lvl){
- return Math.pow(lvl,2);
- }
- skillExperienceGain(skillId,inc){
- if(inc == 0)
- return;
- this.#skillInit(skillId);
- this._skills[skillId].experience += inc;
- console.log("Skill Experience Gain:",skillId,inc,this._skills[skillId].experience );
- }
- skillsExperienceGain(skillObj:{[key: string]:number},factor=1){
- for(const [skillId,inc] of Object.entries(skillObj)){
- this.skillExperienceGain(skillId,inc*factor)
- }
- }
- skillExperienceHistory(skillId){
- this.#skillInit(skillId);
- let eh = clone(this._skills[skillId].experienceHistory);
- eh.push(this._skills[skillId].experience);
- return eh;
- }
- skillExperienceHistoryDayExpTuple(skillId){
- let today = State.variables.time.daystart;
- let eh = this.skillExperienceHistory(skillId);
- let result = [];
- let todayIndex = eh.length - 1;
- for(let i = 0; i < eh.length; i++){
- let day = -todayIndex + i + today;
- let tuple = [day,eh[i]];
- result.push(tuple);
- }
- return result;
- }
- skillFloor(skillId){
- this.#skillInit(skillId);
- return this._skills[skillId].floor;
- }
- skillLevel(skillId){
- this.#skillInit(skillId);
- return (
- this.#skill_exp2lvl(skillId,this._skills[skillId].experience) +
- this.#skillLevelBonusOfActiveEffect(skillId)
- );
- }
- #skillLevelBonusOfActiveEffect(skillId){
- let result = 0;
- for(const effect of Object.values(this.activeEffects))
- result += effect.skills?.[skillId] ?? 0;
- return result;
- }
- //Is usually used for initialization, therefore we'll add one history entry if it doesn't exist
- skillSetLevel(skillId,lvl){
- this.#skillInit(skillId);
- let exp = this.#skill_lvl2exp(skillId,lvl);
- this._skills[skillId].experience = exp;
- console.log("Setting skill level (Skill, Lvl, Exp)",skillId,lvl,exp);
- }
- // ----- Fetishes -----
- _fetishes = {};
- fetish(fetishId){
- return this._fetishes[fetishId] ?? 1;
- }
- fetishSet(fetishId,v){
- this._fetishes[fetishId] = v;
- }
- fetishes(fetishIds){
- if(typeof fetishIds == "string")
- return this.fetishes([fetishIds]);
- let result = 1;
- let indifferentCount = 0;
- let arouseCount = 0;
- for(const fetishId of fetishIds){
- const arousalOfFetish = this.fetish(fetishId);
- if(arousalOfFetish < 0)
- return arousalOfFetish;
- if(arousalOfFetish == 0)
- indifferentCount += 1;
- else if(arousalOfFetish > 1){
- result += arousalOfFetish - 1;
- arouseCount += 1;
- }
- }
- if(indifferentCount > 0 && arouseCount == 0)
- return 0;
- return result;
- }
- // ----- Sex-Stats -----
- /*
- Each Sex-Encounter has the following format:
- {
- npc: The ID of the npc Sveta had sex with. If she has sex with more than one character (e.g. threesome) an encounter has to be created for each of them.
- time:The time it happened (Date-Object (UTC-time))
- aware: Is Sveta aware of the sex-act? False in case of hypnosis, blackouts, etc.
- civ: Cum in vagina
- civa:Cum in vagina aware
- tags: Array of tags
- }
- */
- _sexEncounters = [];
- sexEncounterAdd(time,events){
- this._sexEncounters.push({
- time: time,
- events: events
- });
- }
- /*sexEncountersFiltered(maxage=Infinity,aware=undefined,civ=undefined,civa=undefined){
- let result = [];
- for (const sexEncounter of this._sexEncounters) {
- let ageOfEncounterInDays = Math.floor(State.variables.time.secondsSinceDate(sexEncounter.time) / 86400);
- if(ageOfEncounterInDays > maxage)
- continue;
- if(typeof aware === "boolean" && aware !== sexEncounter.aware) continue;
- if(typeof civ === "boolean" && civ !== sexEncounter.civ) continue;
- if(typeof civa === "boolean" && civa !== sexEncounter.civa) continue;
- result.push(sexEncounter);
- }
- return result;
- }*/
- sexEncountersFilteredByNpc(maxage=Infinity,aware=undefined){
- let result = {};
- for(const encounter of this._sexEncounters){
- for(const event of encounter.events){
- if(typeof aware === "boolean"){
- if((aware && event.unaware) || (!aware && !event.unaware))
- continue;
- }
- result[event.npc] ??= 0;
- result[event.npc] += 1;
- }
- }
- return result;
- }
- //Every Stat has two values: the number you are aware of and the number you are unaware of
- /** @access private */
- _sexStats = {
- vaginal: [0,0],
- vaginal_fist:[0,0],
- vaginal_dildo:[0,0],
- vaginal_strap:[0,0]
- }
- /**
- * Returns a sexstat or, it it isn't set, def. Returns undefined in case of an error.
- * @example
- * sexStat('vaginal_total','both',0);
- * @date 5/29/2023 - 9:32:32 AM
- *
- * @param {string} key
- * @param {string} [awareness='both'] - Either 'aware' or 'unaware' or 'both' (the sum of the former)
- * @param {number} [def=0]
- * @returns {number|undefined}
- */
- sexStat(key,awareness='both',def=0){
- switch(key){
- case 'vaginal_total':
- return 0;//return (this.stat('vaginal',awareness) + this.stat('vaginal_fist',awareness) + this.stat('vaginal_dildo',awareness) + this.stat('vaginal_strap',awareness));
- }
- if(!(key in this._sexStats))
- return def;
- switch(awareness){
- case 'both':
- return this._sexStats[key][0] + this._sexStats[key][1];
- case 'aware':
- return this._sexStats[key][0];
- case 'unaware':
- return this._sexStats[key][1];
- default:
- console.error('Invalid argument for PlayerCharacter.sexStat: awareness',awareness);
- }
- return undefined;
- }
- /**
- * Increases a sex-stat by inc.
- * @example
- * sexStatInc('vaginal','aware',1)
- * @date 5/29/2023 - 9:24:04 AM
- *
- * @param {string} key
- * @param {string} awareness - Either 'aware' or 'unaware'
- * @param {number} [inc=1]
- */
- sexStatInc(key,awareness,inc=1){
- let currentValue = this.sexStat(key,awareness);
- this.sexStatSet(key,awareness,currentValue+inc);
- }
- /**
- * Sets a sex-stat to v.
- * @example
- * sexStatSet('vaginal','aware',1)
- * @date 5/29/2023 - 9:30:58 AM
- *
- * @param {string} key
- * @param {string} awareness - Either 'aware' or 'unaware'
- * @param {number} v - The new value.
- * @returns {number|undefined} The new value or, if there was an error, undefined.
- */
- sexStatSet(key,awareness,v){
- this._sexStats[key] ??= [0,0];
- switch(awareness){
- case 'aware':
- this._sexStats[key][0] = v;
- return v;
- case 'unaware':
- this._sexStats[key][1] = v;
- return v;
- default:
- console.error('Invalid argument for PlayerCharacter.sexStatSet: awareness',awareness);
- return undefined;
- }
- }
- get isVirgin(){
- return (this.sexStat('vaginal','both') == 0) ? true : false;
- }
- get thinksIsVirgin(){
- return (this.sexStat('vaginal','aware') == 0) ? true : false;
- }
- _traits = {
- nerd_status: 0
- }
- trait(key,def=0){
- if(key in this._traits)
- return this._traits[key];
- return def;
- }
- traitDec(key,v){
- this._traits[key] -= v;
- }
- traitInc(key,v){
- this._traits[key] += v;
- }
- traitSet(key,v){
- this._traits[key] = v;
- }
- // ----- Arousal -----
- /*
- For checking arousal and when applicable triggering orgasms.
- action:
- All acts are from Sveta''s perspective and in cases of both giving and receiving, receiving should be used.
- It can be when receiving any of the following
- 'clit_finger' - Clit being stimulated directly by a finger
- 'clit_vibe' - Clit being stimulated directly by a vibrator (set low, use negative time and double/triple up for more power)
- 'porn' - viewing pornographic material
- 'voyeur_sex' - watching, usually as in spying on, other people have sex
- 'voyeur' - watching, usually as in spying on, erotic acts of others
- 'erotic' - being aroused by eroticism
- 'erotic_nudity' - being aroused by nudity of others
- 'trib' - rubbing pussy against another pussy
- 'massage' - rubbing your body, back, feet, etc. with their hands/arms
- 'cuni' - stimulation of your pussy by someones toungue
- 'rimming' - stimulation of your anus by someones toungue
- 'vaginal' 'vaginal_finger' 'vaginal_fist' 'vaginal_dildo' 'vaginal_strap' 'vaginal_vibe' - stimulation of your vagina with a penis and various others
- 'self_fisting' - fisting your own vagina
- 'anal' 'anal_finger' 'anal_fist' 'anal_dildo' 'anal_strap' 'anal_vibe' - stimulation of your anus with a penis and various others
- 'self_fisting_anal' - fisting your own anus
- 'kiss' - snogging, tonsil tennis, lip locking, etc.
- 'BDSM' - receiving candle wax, flogging, leash play, bondage etc
- 'pee' - being peed upon
- also when giving any of the following:
- 'flashlite' - flashing underwear
- 'flash' - flashing naked breasts/arse/vagina
- 'massage_give' - rubbing their body, back, feet, etc. with your hands/arms
- 'cuni_give' - stimulating someones pussy with your toungue
- 'rimming_give' - stimulating someones anus with your toungue
- 'vaginal_finger_give' 'vaginal_fist_give' 'vaginal_dildo_give' 'vaginal_strap_give' 'vaginal_vibe_give' - stimulating someones vagina in various ways
- 'clit_finger_give' - stimulating someones clit
- 'anal_finger_give' 'anal_fist_give' 'anal_dildo_give' 'anal_vibe_give' 'anal_strap_give' - stimulating someones anus in various ways
- 'hj' - jerking a guy off with your hand
- 'bj' - sucking a guy off
- 'dildo_suck' - simulating a bj on a dildo/strapon
- 'titjob' - using boobs to jerk off a guy
- 'footjob' - using feet to jerk off a guy
- 'BDSM_give' - giving candle wax, flogging, leash play, bondage etc
- 'pee_give' - peeing on somone
- finally
- 'foreplay' - receiving other stuff
- 'foreplay_give' - giving other stuff
- time
- for time taken in minutes - it is use partly for arousal calculation and partly for moving time ahead. If you want to calculate just the arousal and do not move time (simultaneous stimulation), use negative value.
- npcId
- specify the involved npc
- flags
- Are optional but can be themes involved in the act and can be any of the following:
- 'maso' 'bound' 'beast' 'exhibitionism' 'rough' 'prostitution' 'dom' 'sub' 'incest'
- 'feet' 'lesbian' 'group' 'gangbang' 'humiliation' 'deepthroat' 'unknown' 'gloryhole' 'rape' 'futa' 'masturbate'
- 'unaware'
- */
- _analCapacity = 0.5;
- get analCapacity(){
- return this._analCapacity;
- }
- _vaginalCapacity = 1;
- get vaginalCapacity(){
- return this._vaginalCapacity;
- }
- get vaginalLubrication(){
- return this.horny / 100;
- }
- analCapacityAdaptTo(v){
- if(v <= this._analCapacity)
- return;
- const sizeDifference = v - this._analCapacity;
- if(sizeDifference <= 1)
- this._analCapacity = Math.min(v, this._analCapacity + 0.1);
- else
- this._analCapacity = Math.min(v, this._analCapacity + sizeDifference / 10);
- }
- vaginalCapacityAdaptTo(v){
- if(v <= this._vaginalCapacity)
- return;
- const sizeDifference = v - this._vaginalCapacity;
- if(sizeDifference <= 1)
- this._vaginalCapacity = Math.min(v, this._vaginalCapacity + 0.1);
- else
- this._vaginalCapacity = Math.min(v, this._vaginalCapacity + sizeDifference / 10);
- }
-
- current_arousal_scene_actions = [];
- arouse(action,time,npcId=undefined,flags=undefined){
- console.warn("PlayerCharacter.arouse() is deprecated. Use <<arouse>> instead",action,time,npcId,flags);
- }
- //TODO
- arousalEnd(){
- console.warn("PlayerCharacter.arousalEnd() is deprecated");
- }
- // ----- Cum -----
- /*
- Cum-Locations
- 0 = 'In your Vagina'
- 1 = 'On your labia'
- 2 = 'On your panties over your vagina'
- 3 = 'In your anus'
- 4 = 'On your butt'
- 5 = 'On your panties over your butt'
- 6 = 'On your clothes in your groin area'
- 7 = 'On your clothes'
- 8 = 'On your back'
- 9 = 'On your legs'
- 10 = 'On your arms'
- 11 = 'On your face'
- 12 = 'Inside your mouth'
- 13 = 'On your hands'
- 14 = 'On your stomach'
- 15 = 'On your breasts'
- 16 = 'In your hair'
- 17 = 'In a condom in your vagina
- */
- _cum:{ [key: string]: Array<any>} = {};
- get cums(){
- this._cumPurgeExpired();
- return this._cum;
- }
-
- /**
- * Removes cum-data on the inside of the PCs body.
- * @date 10/8/2023 - 2:16:00 PM
- */
- _cumPurgeExpired(){
- const bodyparts = setup.getBodyparts();
- let updatedCumData = {};
- const maxAgeInMinutes = 360; // 6 Hours
- let oldestPossibleDateTime = State.variables.time.now;
- oldestPossibleDateTime.setUTCMinutes(oldestPossibleDateTime.getUTCMinutes() - maxAgeInMinutes);
- for (const [bodypart, cumData] of Object.entries(this._cum)) {
- if(bodyparts[bodypart].inside)
- updatedCumData[bodypart] = cumData.filter(cum => cum.time.getTime() >= oldestPossibleDateTime.getTime());
- else
- updatedCumData[bodypart] = cumData;
- }
- this._cum = updatedCumData;
- }
- cum(npcId,bodypartId){
- let bodypartData = setup.getBodypart(bodypartId);
- if(!bodypartData || bodypartData.cumDisabled)
- console.warn('The following bodypart is not enabled for $pc.cum():',bodypartId);
- bodypartId = bodypartData.id;
- const time = State.variables.time;
- const now = time.now;
- const cumData = {
- npc: npcId,
- time: now
- };
- this._cum[bodypartId] ??= [];
- this._cum[bodypartId].push(cumData);
- }
- cumCleanAll(){
- this._cum={};
- }
- cumCleanByActivity(activityId){
- const bodyparts = Object.entries(setup.getBodyparts());
- const bodypartIdsToClean = bodyparts.filter(([id,bodypart]) => bodypart['clean'].includes(activityId)).map(([id,bodypart]) => id);
- for(let bodypartIdToClean of bodypartIdsToClean)
- this._cum[bodypartIdToClean] = [];
- }
- get cumBodypartIds(){
- return Object.entries(this.cums).filter(([bodypartId,cumArray]) => cumArray.length > 0).map(([bodypartId,cumArray]) => bodypartId);
- }
- get cumVisibleBodypartIds(){
- return this.cumBodypartIds.filter(bodypartId=>!this.bodyPartCovered(bodypartId));
- }
- /*_cumCleanByCumLocation(bodyLocation){
- this._cum[bodyLocation] = [];
- }
- cumAtLocation(location){
- if(this._cum[location].length > 0)
- return 1;
- return 0;
- }*/
- /*cumClean(location){
- if(typeof location != "string"){
- switch(location){
- case 1: location = 'full external'; break;
- case 2: location = 'mouth'; break;
- case 3: location = 'interal'; break;
- case 4: location = 'external'; break;
- case 5: location = 'hair'; break;
- case 6: location = 'clothes'; break;
- case 7: location = 'enema'; break;
- case 8: location = 'blank'; break;
- case 9: location = 'hands'; break;
- case 10: location = 'full external'; break;
- case 11: location = 'face'; break;
- case 12: location = 'face and hair'; break;
- case 13: location = 'head'; break;
- case 14: location = 'belly'; break;
- case 15: location = 'wipe ass and pussy'; break;
- case 16: location = 'breasts'; break;
- case 17: location = 'arms and legs'; break;
- case 18: location = 'full except internal'; break;
- case 19: location = ''; break;
- case 20: location = 'everything'; break;
- default:
- console.error("Unsupported value in cumClean:",location);
- return;
- }
- }
- switch(location){
- case 'full external':
- this._cumCleanByCumLocation(1);
- this._cumCleanByCumLocation(2);
- this._cumCleanByCumLocation(4);
- this._cumCleanByCumLocation(5);
- this._cumCleanByCumLocation(6);
- this._cumCleanByCumLocation(7);
- this._cumCleanByCumLocation(8);
- this._cumCleanByCumLocation(9);
- this._cumCleanByCumLocation(10);
- this._cumCleanByCumLocation(11);
- this._cumCleanByCumLocation(12);
- this._cumCleanByCumLocation(13);
- this._cumCleanByCumLocation(14);
- this._cumCleanByCumLocation(15);
- this._cumCleanByCumLocation(16);
- break;
- case 'mouth':
- this._cumCleanByCumLocation(12);
- break;
- case 'interal':
- this._cumCleanByCumLocation(0);
- this._cumCleanByCumLocation(3);
- this._cumCleanByCumLocation(17);
- break;
- case 'external':
- this._cumCleanByCumLocation(1);
- this._cumCleanByCumLocation(4);
- this._cumCleanByCumLocation(8);
- this._cumCleanByCumLocation(9);
- this._cumCleanByCumLocation(10);
- this._cumCleanByCumLocation(11);
- this._cumCleanByCumLocation(13);
- this._cumCleanByCumLocation(14);
- this._cumCleanByCumLocation(15);
- this._cumCleanByCumLocation(16);
- break;
- case 'hair':
- this._cumCleanByCumLocation(16);
- break;
- case 'clothes':
- this._cumCleanByCumLocation(2);
- this._cumCleanByCumLocation(5);
- this._cumCleanByCumLocation(6);
- this._cumCleanByCumLocation(7);
- break;
- case 'enema':
- case 'ass':
- this._cumCleanByCumLocation(3);
- break;
- case 'bank':
- case '':
- break;
- case 'hand':
- case 'hands':
- this._cumCleanByCumLocation(13);
- break;
- case 'face':
- this._cumCleanByCumLocation(11);
- break;
- case 'face and hair':
- this._cumCleanByCumLocation(11);
- this._cumCleanByCumLocation(16);
- break;
- case 'head':
- this._cumCleanByCumLocation(11);
- this._cumCleanByCumLocation(12);
- this._cumCleanByCumLocation(16);
- break;
- case 'belly':
- this._cumCleanByCumLocation(14);
- break;
- case 'wipe ass and pussy':
- this._cumCleanByCumLocation(1);
- this._cumCleanByCumLocation(4);
- break;
- case 'breast':
- case 'breasts':
- this._cumCleanByCumLocation(15);
- break;
- case 'arms and legs':
- this._cumCleanByCumLocation(9);
- this._cumCleanByCumLocation(10);
- break;
- case 'full except internal':
- this._cumCleanByCumLocation(1);
- this._cumCleanByCumLocation(2);
- this._cumCleanByCumLocation(4);
- this._cumCleanByCumLocation(5);
- this._cumCleanByCumLocation(6);
- this._cumCleanByCumLocation(7);
- this._cumCleanByCumLocation(8);
- this._cumCleanByCumLocation(9);
- this._cumCleanByCumLocation(10);
- this._cumCleanByCumLocation(11);
- this._cumCleanByCumLocation(12);
- this._cumCleanByCumLocation(13);
- this._cumCleanByCumLocation(14);
- this._cumCleanByCumLocation(15);
- this._cumCleanByCumLocation(16);
- break;
- case 'everything':
- case 'reset':
- /*
- <<set $sparrage to null>>
- <<set $sparrloc to null>>
- <<set $sparrnam to null>>
- <<set $sparrppt to null>>
- <<set $sparridt to null>>
- <<set $sparrvol to null>>
- <<set $sparrslc to null>>
- <<set $sparrcnt to null>>
- *//*
- break;
- default:
- console.error("Unsupported location in cumClean:",location);
- return;
- }
- }*/
- // ----- Pain -----
- vgape = 0;
- agape = 0;
- spanked = 0;
- _pain = new setup.Pain();
- pain(bodypart){return this._pain.pain(bodypart)}
- get painByBodyparts(){return this._pain.painByBodyparts}
- painDeteriorate(timeToAdd){return this._pain.painDeteriorate(timeToAdd)}
- get painRaw(){return this._pain.painRaw}
- painInc(bodypart,v){return this._pain.painInc(bodypart,v)}
- painIncAll(v){return this._pain.painIncAll(v)}
- //get painRelief(){return this._pain.painRelief}
- painSet(bodypart,v){return this._pain.painSet(bodypart,v)}
- get painTotal(){return this._pain.painTotal}
- get painOfActiveEffects(){
- var painRaw = {};
- const activeEffects = this.activeEffects;
- for(const activeEffect of Object.values(activeEffects)){
- for(const [bodypartId,painValue] of Object.entries(activeEffect.pain ?? {})){
- painRaw[bodypartId] ??= [];
- painRaw[bodypartId].push(painValue);
- }
- }
- return painRaw;
- }
- // ----- Personality -----
- _pcs_dom = 0
- get pcs_dom(){return this._pcs_dom;}
- set pcs_dom(v){
- if(v < 0){
- this.pcs_sub -= v;
- this._pcs_dom = 0;
- }else{
- this._pcs_dom = Math.min(100,v);
- this._balanceDomSub();
- }
- }
- _pcs_sub = 0
- get pcs_sub(){return this._pcs_sub;}
- set pcs_sub(v){
- if(v < 0){
- this.pcs_dom -= v;
- this._pcs_sub = 0;
- }else{
- this._pcs_sub = Math.min(100,v);
- this._balanceDomSub();
- }
- }
- _balanceDomSub(){
- if(this._pcs_dom > 0 && this._pcs_sub > this._pcs_dom){
- this._pcs_sub -= this._pcs_dom;
- this._pcs_dom = 0;
- }
- if(this._pcs_sub > 0 && this._pcs_dom > this._pcs_sub){
- this._pcs_dom -= this._pcs_sub;
- this._pcs_sub = 0;
- }
- }
- timeFactor = 1; //Used for cheating
- // ----- Fame -----
- _fame = {};
- fame(location,type=null){
- if(!type){
- console.warn('Calling $pc.fame in deprecated format.',location);
- let idParts = location.split('_');
- location = idParts[0];
- let type = idParts[1];
- return this.fame(location,type);
- }
- if(!(location in this._fame))
- return 0;
- if(type == 'slut')
- return this.fame(location,'sex') + this.fame(location,'prostitute');
- return this._fame[location][type] || 0;
- }
- fameDec(location,type,amount,local=false){
- this.fameInc(location,type,amount * -1,local);
- }
- fameInc(location,type,amount,local=false){
- this._fame[location] ??= {};
- this._fame[location][type] ??= 0;
- let current = this._fame[location][type];
- if(typeof amount == "string"){
- if(current > 900){ //The original says 1000... how is this possible if 1000 is supposed to be the soft cap?
- switch(amount){
- case 'tiny':
- case 'small':
- return;
- case 'medium': amount = rand(0,1);break;
- case 'large': amount = rand(1,2);break;
- case 'huge': amount = rand(1,4);break;
- case 'BronzeMedal': amount = rand(15,25);break;
- case 'SilverMedal': amount = rand(25,35);break;
- case 'GoldMedal': amount = rand(35,45);break;
- default:
- console.warn('Argument for amount not reckognized in $pc.fameInc:',amount);
- amount = 0;
- }
- }else if(current > 700){
- switch(amount){
- case 'tiny': amount = rand(0,1);break;
- case 'small': amount = rand(1,2);break;
- case 'medium': amount = rand(1,4);break;
- case 'large': amount = rand(6,12);break;
- case 'huge': amount = rand(10,24);break;
- case 'BronzeMedal': amount = rand(25,60);break;
- case 'SilverMedal': amount = rand(60,100);break;
- case 'GoldMedal': amount = rand(100,150);break;
- default:
- console.warn('Argument for amount not reckognized in $pc.fameInc:',amount);
- amount = 0;
- }
- }else if(current > 400){
- switch(amount){
- case 'tiny': amount = rand(1,2);break;
- case 'small': amount = rand(1,4);break;
- case 'medium': amount = rand(6,12);break;
- case 'large': amount = rand(10,24);break;
- case 'huge': amount = rand(20,50);break;
- case 'BronzeMedal': amount = rand(50,100);break;
- case 'SilverMedal': amount = rand(100,150);break;
- case 'GoldMedal': amount = rand(150,200);break;
- default:
- console.warn('Argument for amount not reckognized in $pc.fameInc:',amount);
- amount = 0;
- }
- }else{
- switch(amount){
- case 'tiny': amount = rand(1,4);break;
- case 'small': amount = rand(6,12);break;
- case 'medium': amount = rand(10,24);break;
- case 'large': amount = rand(20,50);break;
- case 'huge': amount = rand(40,70);break;
- case 'BronzeMedal': amount = rand(150,250);break;
- case 'SilverMedal': amount = rand(250,350);break;
- case 'GoldMedal': amount = rand(350,450);break;
- default:
- console.warn('Argument for amount not reckognized in $pc.fameInc:',amount);
- amount = 0;
- }
- }
- }else{
- if(current > 900)
- amount /= 10;
- else if(current > 700)
- amount /= 6;
- else if(current > 400)
- amount /= 3;
- }
- if(amount == 0)
- return;
- let target = Math.clamp(current+amount,0,1000);
- this._fame[location][type] = target;
- if(type in fameAlwaysLocale)
- local = true;
- }
- // ----- Effects -----
- _effects:{ [key: string]: any } = {};
-
- get activeEffects():{[key: string]:any}{
- let result = {};
- const time = State.variables.time;
-
- for(const [effectid,effectData] of Object.entries(this._effects)){
- if(effectData.expiration === undefined)
- continue;
- if(this._effects[effectid] === null || time.isFuture(this._effects[effectid]))
- result[effectid] = effectData;
- }
- result = Object.assign({},this.drugsActiveEffects,result);
- return result;
- }
- get activeEffectsMoodlets():{ [key: string]: ActiveMoodlet; }{
- const time = State.variables.time;
- return Object.fromEntries(
- this.activeEffectsMoodletIDs.map(
- (moodletId) =>
- [
- moodletId,
- ActiveMoodlet.create(moodletId, {expiration: time.endTime})
- ]
- )
- );
- }
- get activeEffectsMoodletIDs():Array<string>{
- var result = [];
- const activeEffects = this.activeEffects;
- for(const activeEffect of Object.values(activeEffects)){
- if(activeEffect.moodlet)
- result.push(activeEffect.moodlet);
- }
- return result;
- }
- get activeEffectsSidebar(){
- return Object.values(this.activeEffects).filter(effect=>effect.sidebar);
- }
-
- /**
- * @param {string} key
- * @param {undefined|'+'|'*'} [reduceMode='+']
- * @returns {number}
- */
- #activeEffectValueByKey(key,reduceMode='+'){
- const valueArray = this.#activeEffectValuesByKey(key);
- switch (reduceMode) {
- case '*':
- return valueArray.reduce((accumulator, currentValue) => accumulator * currentValue,1);
- case '+':
- default:
- return valueArray.reduce((accumulator, currentValue) => accumulator + currentValue,0);
- }
- }
-
- /**
- * @param {string} key
- * @returns {{}}
- */
- #activeEffectValuesByKey(key){
- var result =[];
- const activeEffects = this.activeEffects;
- for(const effect of Object.values(activeEffects)){
- if(key in effect)
- result.push(effect[key]);
- }
- return result;
- }
- /**
- * Description placeholder
- * @date 10/3/2023 - 1:07:07 PM
- *
- * @param {string} effectId
- * @param {number|Date|null} expirationOrDuration - Either a date when the effect expires, a number that indicates the minutes til it expires, or null if it won't expire.
- */
- effectAdd(effectId, expirationOrDuration, metadata={}){
- const time = State.variables.time;
- let expiration = expirationOrDuration;
- if(typeof expirationOrDuration == 'number')
- expiration = time.nowWithMinutesOffset(expirationOrDuration);
- this._effects[effectId] = Object.assign({expiration:expiration},metadata) ;
- }
-
- /**
- * @param {string} effectId
- * @returns {boolean}
- */
- effectIsActive(effectId){
- return (effectId in this.activeEffects);
- }
- // ----- Timed Actions -----
- execute_every_15_minutes(){
- const time = State.variables.time;
- let timeMod = 0.25 * this.timeFactor; //We are doing some hourly calculations 4 times as often
- let change = {
- 'energy': 0,
- 'hydra': 0
- }
- if(this.isSleeping){
- change.energy = timed_stat_changes['energy']['sleep'] * timeMod;
- change.hydra = timed_stat_changes['hydra']['sleep'] * timeMod;
- }else{
- change.energy = timed_stat_changes['energy']['default'] * timeMod;
- change.hydra = timed_stat_changes['hydra']['default'] * timeMod;
- }
- this.pcs_energy -= change.energy;
- this.pcs_hydra -= change.hydra;
- this.energyDemand += change.energy;
- if(this.isSleeping){
- this.pcs_sleep -= timed_stat_changes['sleep']['sleep'] * timeMod ;
- this.pcs_stam += this.stammax / 10;
- this.pcs_willpwr += this.willpowermax / 100;
- this.painIncAll(-1);
- }else{
- this.pcs_sleep -= timed_stat_changes['sleep']['default'] * timeMod ;
- this.pcs_stam += this.stammax / 20;
- this.pcs_willpwr += this.willpowermax / 300;
- this.painIncAll(-0.5);
- }
- // ----- Dying -----
- //TODO
- /*
- for (const [riskId, riskData] of Object.entries(dieRisks)){
- this._death[riskId] ??= {stage:0};
- if(this[riskData['variable']] == 0){
- if(this._death[riskId].stage == 0 || !time.isFuture(this._death[riskId].nextStageDate)){
- if((this._death[riskId].stage ?? 0) >= riskData.durations.length){
- this.gameover = riskId;
- console.warn("GAMEOVER set",riskId);
- }else{
- this._death[riskId].nextStageDate = time.nowWithMinutesOffset(riskData.durations[this._death[riskId].stage]);
- this._death[riskId].stage += 1;
- this.moodletApplyById(riskId+'_'+this._death[riskId].stage);
- }
- }
- }
- }*/
- }
- execute_every_1_hour(){
- let timeFactor = this.timeFactor;
- if(this.mood <= 20)
- this.will_counter -= 2 * timeFactor;
- if(this.willpowermax > 100){
- if(this.pcs_willpwr < 25)
- this.will_counter -= 1 * timeFactor;
- }else{
- if(this.pcs_willpwr < this.willpowermax / 4)
- this.will_counter -= 1 * timeFactor;
- }
- if(this.will_counter <= -10){
- this.willpowermax -= 1;
- this.will_counter = 0;
- }
-
- this.pcs_lipbalm -= 1 * timeFactor;
- }
- execute_every_1_day(){
- this.bodyDailyUpdate();
- this.#skill_dayly();
- this.#moodletsClean();
- }
- execute_every_timeUpdate(timeToAdd){
- let timestamp = State.variables.time.minutesTimestamp;
- if(this.deodorant_on == 1 && timestamp > this.deodorant_time)
- this.deodorant_on = 0;
- this.performShoePainAndExperience(timeToAdd);
- this.performInhibitionExperience(timeToAdd);
-
- this.horny -= timeToAdd * this.hornyDeteriorationRate;
- this.drugsDeteriorate(timeToAdd);
- this.painDeteriorate(timeToAdd);
- }
- performInhibitionExperience(minutes){
- if(this.isSleeping)
- return;
- let balancingFactorExp = 0.5;
- let outsideFactor = 2;
- let location = State.variables.location;
- // You don't get any inhibition progress while being at home
- if(location.isHome)
- return;
- let totalExpFactor = balancingFactorExp * (location.isOutdoors ? outsideFactor : 1);
- let wardrobe = State.variables.wardrobe;
- let clothesInhib = wardrobe.PCloInhibit;
- let playerInhib = this.pcs_inhib;
- if(playerInhib > clothesInhib)
- return;
- let inhibDiff = clothesInhib - playerInhib;
- let expRaw = Math.ceil(inhibDiff / 10) * 10; // Round up to 10, 20, etc.
- let experienceGained = expRaw * minutes / 60 * totalExpFactor;
- this.skillExperienceGain("inhibition",experienceGained);
- }
- performShoePainAndExperience(minutes){
- if(this.isSleeping)
- return;
- let balancingFactorPain = 2;
- let balancingFactorExp = 0.5;
- let wardrobe = State.variables.wardrobe;
- if(!wardrobe.isWearingShoes)
- return;
- let shoeQuality = wardrobe.PShoQuaility;
- let shoeHeight = wardrobe.shoesHeight;
- let painOfShoe = shoeHeight / 10 - shoeQuality;
- let heelSkill = this.skillLevel("highHeels");
- let painReductionDueToSkill = heelSkill / 5;
- let painOfWalkingInShoes = Math.max(0,painOfShoe-painReductionDueToSkill) * balancingFactorPain;
- painOfWalkingInShoes = painOfWalkingInShoes / 60 * minutes;
- this.painInc('feet',painOfWalkingInShoes);
- // ----- Experience -----
- let experienceLevelRequiredForShoes = Math.clamp((shoeHeight / 10) * 3,0,100);
- let experienceLevelDifference = experienceLevelRequiredForShoes - heelSkill;
- let experienceGained = 0;
- if(experienceLevelDifference > 0)
- experienceGained = shoeHeight;
- else{
- //Results in gaining half as many experience for being 10 points over target, a quarter fo 20 over target, and so on.
- const basis = 2;
- const exponent = experienceLevelDifference / -10;
- experienceGained = shoeHeight / Math.pow(2,exponent);
- }
- experienceGained = Math.max(0,experienceGained) * balancingFactorExp;
- experienceGained = experienceGained / 60 * minutes;
- this.skillExperienceGain("highHeels",experienceGained);
- }
- // ----- Deco -----
- // Deco includes tattoos, piercings, glasses, etc.
- _deco = {}
- _decoOwned = {}
- decoAdd(type,position,index){
- if(!this._decoOwned[type])
- this._decoOwned[type] = {};
- if(!this._decoOwned[type][position])
- this._decoOwned[type][position] = [];
- if(!this._decoOwned[type][position].includes(index))
- this._decoOwned[type][position].push(index);
- }
- decoHas(type,position,index){
- if(!(this._decoOwned[type]?.[position]))
- return false;
- return this._decoOwned[type][position].includes(index);
- }
- decoGet(type,position='default'){
- return this._deco[type]?.[position] ?? 0;
- }
- get decoOwned(){return this._decoOwned;}
- decoRemove(type,position='default'){
- this.decoSet(type,position,null);
- }
- decoSet(type,position='default',v){
- if(!(type in this._deco))
- this._deco[type] = {};
- this._deco[type][position] = v;
- }
- decoWear(type,position='default',v){
- this.decoSet(type,position,v);
- if(v && v != -1)
- this.decoAdd(type,position,v);
- }
- // ----- Bodyparts -----
- bodyPartCovered(bodypartId){
- let bodypartData = setup.getBodypart(bodypartId);
- if(!bodypartData){
- console.error('Data not found by $pc.bodyPartCovered for bodypart',bodypartId);
- return false;
- }
- if(typeof bodypartData.covered === "undefined")
- return false;
- if(typeof bodypartData.covered === "boolean")
- return bodypartData.covered;
- if(typeof bodypartData.covered === "string")
- return Scripting.evalTwineScript(bodypartData.covered);
-
- console.error('Unsupported type of covered of bodypart in $pc.bodyPartCovered',bodypartId,bodypartData);
- return false;
- }
- // ----- Init -----
- init_final(){
- for (const [skillId, skillData] of Object.entries(this._skills)){
- this._skills[skillId].experienceHistory = [skillData.experience];
- }
- }
- // ----- Occupation -----
- get isSchoolStudent(){
- return State.variables.q.questIsActive("school");
- }
- // ----- Vehicles -----
- _vehicleInUse = null;
- get vehicleInUse(){return this._vehicleInUse;}
- set vehicleInUse(v){this._vehicleInUse = v;}
- get vehicleData(){
- let inventory = State.variables.inventory;
- let vehicleData = inventory.metadata(this.vehicleInUse,'vehicles');
- return vehicleData;
- }
- vehicleCanEnterPassage(passage){
- var vehicleType = this.vehicleType;
- if(vehicleType != 'car')
- return true;
- var passageTags = tags(passage);
- return passageTags.includes('car');
- }
- get vehicleSpeed(){
- return this.vehicleData.speed ?? 1;
- }
- get vehicleType(){
- if(!this.vehicleInUse)
- return 'walk';
- let inventory = State.variables.inventory;
- let vehicleData = inventory.metadata(this.vehicleInUse,'vehicles');
- return vehicleData.type;
- }
- // ----- Menstruation Cycle -----
- _cycleStart = -7;
- _cycleLength = 28;
- _cycleLengthLast = 28;
- get cycleDay(){
- const time = State.variables.time;
- const daystart = time.daystart;
- return daystart - this.cycleStart;
- }
- get cycleLength(){return this._cycleLength;} // the length of the current cycle
- set cycleLength(v){this._cycleLength = v;}
- get cycleLengthLast(){return this._cycleLengthLast;}
- set cycleLengthLast(v){this._cycleLengthLast = v;}
- get cycleStart(){return this._cycleStart;} // the daystart of the current cycle
- set cycleStart(v){this._cycleStart = v;}
- cycleStartMessageSent = true;
- cycleStartHour = 17;
- get menstruationLength(){return 3;}
- get ovulationDay(){return this.cycleStart + this.cycleLength - 14;}
- ovulationHour = 19;
- get cyclePhase(){
- if(this.effectIsActive('cycleBlocked'))
- return 'blocked';
- const today = State.variables.time.daystart;
- if(today - this.cycleStart - 1 > this.cycleLength || (today - this.cycleStart > this.cycleLength && State.variables.time.hour >= this.cycleStartHour))
- this.cycleStartNew();
- if(today == this.cycleStart){
- //if(State.variables.time.hour < this.cycleStartHour)
- // return 'end';
- return 'menses';
- }else if(this.cycleStart + this.menstruationLength >= today){
- return 'menses';
- }else if(today < this.ovulationDay){
- return 'start';
- }else if(today == this.ovulationDay && State.variables.time.hour < this.ovulationHour){
- return 'start';
- }else{
- return 'end';
- }
- }
- cycleStartNew(){
- const today = State.variables.time.daystart;
- this.cycleStart = today;
- this.cycleStartMessageSent = false;
- this.cycleLengthLast = this.cycleLength;
- if(this.cycleLength >= 35)
- this.cycleLength += rand(-3,0);
- else if(this.cycleLength >= 33)
- this.cycleLength += rand(-2,1);
- else if(this.cycleLength >= 26)
- this.cycleLength += rand(-1,1);
- else if(this.cycleLength >= 24)
- this.cycleLength += rand(-1,2);
- else
- this.cycleLength += rand(0,3);
- this.cycleStartHour = rand(0,23);
- this.ovulationHour = rand(0,23);
- }
- get cycleArousalModificator(){
- switch(this.cyclePhase){
- case 'end':
- return {factor: 0.5, target: 0};
- case 'menses':
- return {factor: 1, target: 0};
- case 'start':
- default:
- const ovulationDay = this.ovulationDay;
- const daystart = State.variables.time.daystart;
- const daysTilOvolationDay = ovulationDay - daystart;
- if(daysTilOvolationDay <= 1)
- return {factor: 2, target: 30};
- if(daysTilOvolationDay <= 2)
- return {factor: 1.5, target: 10};
- return {factor: 1, target: 0};
- }
- }
- get cycleProductsEnabled(){
- const today = State.variables.time.daystart;
- if(this.cyclePhase == 'menses')
- return true;
- if(today - this.cycleStart >= this.cycleLength)
- return true;
- return false;
- }
- _cycleProduct = null;
- get cycleProduct() {
- return this._cycleProduct;
- }
- set cycleProduct(value) {
- this._cycleProduct = value;
- }
- cycleProductSince = null;
-
- get cycleProductsUsed(){
- if(State.variables.time.secondsSinceDate(this.cycleProductSince) > 43200)
- return false;
- return !(!(this._cycleProduct));
- }
- get indecencies(){
- let result = [];
- if(this.cyclePhase == 'menses' && !this.cycleProductsUsed)
- result.push('menses_blood');
- if(State.variables.wardrobe.clothingIsNude){
- if(!State.variables.wardrobe.isWearingBra)
- result.push('naked_breast');
- if(!State.variables.wardrobe.isWearingPanties)
- result.push('naked_pussy');
-
- }
- if(this.cumVisibleBodypartIds.length > 0)
- result.push('cum');
- return result;
- }
- get isIndecent(){
- return (!(this.indecencies.length == 0));
- }
- constructor(){}
- _init(playerCharacter){
- Object.keys(playerCharacter).forEach(function (pn) {
- this[pn] = clone(playerCharacter[pn]);
- }, this);
- return this;
- }
- clone = function () {
- return (new PlayerCharacter())._init(this);
- };
- toJSON = function () {
- var ownData = {};
- Object.keys(this).forEach(function (pn) {
- if(typeof this[pn] !== "function")
- ownData[pn] = clone(this[pn]);
- }, this);
- return JSON.reviveWrapper('(new PlayerCharacter())._init($ReviveData$)', ownData);
- };
- }
- setup.PlayerCharacter = PlayerCharacter;
|